Skip to content

Add Google ADK for Java and Kotlin integrations - #199

Open
glaforge wants to merge 7 commits into
dokimos-dev:masterfrom
glaforge:feat/adk-integration
Open

glaforge wants to merge 7 commits into
dokimos-dev:masterfrom
glaforge:feat/adk-integration

Conversation

@glaforge

@glaforge glaforge commented Oct 21, 2026 •

Copy link
Copy Markdown

Relates to #198

Summary

Adds first-class Dokimos integration modules for Google Agent Development Kit (ADK) in both Java (com.google.adk:google-adk) and Kotlin (com.google.adk:google-adk-kotlin-core-jvm):

  • dokimos-adk-java (dev.dokimos.adk):
    • AdkTraceCollector — an ADK BasePlugin that passively captures Events, ToolCalls, ToolDefinitions (including full descriptions and JSON schemas from BaseTool.declaration()), multi-turn ConversationTrajectorys, retrieved RAG contexts (GroundingMetadata chunks + retrieval tool outputs), and CallMetrics (tokens with thoughtsTokenCount included in tokensOut, latency, and optional PriceTable cost).
    • AdkSupport — streamlined static factory methods for asJudge, simpleTask, agentTask, ragTask, asyncTask, asyncAgentTask, asyncRagTask, measuredTask, measuredAgentTask, measuredRagTask, toCallMetrics, toAgentTrace, toToolCalls, toToolCall, toToolDefinitions, toToolDefinition, toSchemaMap, attach, extractTexts, toConversationTrajectory, and asConversationalApp.
  • dokimos-adk-kotlin (dev.dokimos.adk.kt):
    • AdkTraceCollector — an ADK Kotlin Plugin with coroutine-native callbacks (onUserMessage, beforeRun, onEvent, afterModel, beforeTool, afterTool, onToolError, afterRun).
    • AdkSupport + idiomatic Kotlin extension functions (Model.asJudge, Runner.asJudge, BaseAgent.asJudge, BaseAgent.toAgentTrace, BaseAgent.runTrace, Runner.toAgentTrace, Runner.runTrace, Session.toAgentTrace, List<Event>.toAgentTrace, BaseAgent.toToolDefinitions, BaseTool.toToolDefinition, FunctionDeclaration.toToolDefinition, Schema.toSchemaMap, Session.toConversationTrajectory, List<Event>.toConversationTrajectory, BaseAgent.asConversationalApp, Runner.asConversationalApp).
  • Examples & Docs:
    • Runnable examples in dokimos-examples (AdkAgentEvaluationExample.java and AdkKotlinAgentEvaluationExample.kt).
    • Docusaurus integration guide (docs/docs/integrations/adk.md) and installation instructions (docs/docs/getting-started/installation.md).
    • Claude Code plugin (plugins/evaluate-adk).

Review Updates

  1. toSchemaMap insertion order: Preserved LinkedHashMap insertion order by returning Collections.unmodifiableMap(map) instead of Map.copyOf(map).
  2. thoughtsTokenCount in tokensOut: Included thoughtsTokenCount in tokensOut (candidatesTokenCount + thoughtsTokenCount) across AdkSupport and AdkTraceCollector in both Java and Kotlin since thinking tokens are billed at the output token rate, and documented this in Javadoc/KDoc.
  3. Trimmed AdkSupport public API surface (>1,050 net lines removed):
    • Reduced AdkSupport.java from 88 public static methods (2,522 lines) to 39 public static methods (1,775 lines) by removing redundant inputKey/outputKey/toolCallsKey permutations, custom Executor overloads, duplicate List<String> vs varargs retrievalToolNames overloads, 2-arg PriceTable overloads, measuredAsync* methods, toToolCall(FunctionCall, String), and toToolDefinitions(LlmAgent).
    • Reduced AdkSupport.kt from 1,719 lines to 1,470 lines by removing custom Executor overloads, removing measuredAsync* methods (folding CallMetrics + optional modelId/priceTable directly into asyncTask/asyncAgentTask/asyncRagTask), dropping @JvmOverloads from multi-parameter functions, and removing toToolCall(FunctionCall, String?) and toToolDefinitions(LlmAgent).
  4. Live integration tests with both GEMINI_API_KEY and OPENAI_API_KEY:
    • AdkAgentIT (dokimos-adk-java): Runs against Gemini (gemini-3.8-flash) when GEMINI_API_KEY is set, and against OpenAI (OpenAiChatModelName.GPT_4_O_MINI) when OPENAI_API_KEY is set via ADK Java's com.google.adk:google-adk-langchain4j (LangChain4j) integration.
    • AdkKotlinAgentIT (dokimos-adk-kotlin): Runs against Gemini (gemini-3.8-flash) when GEMINI_API_KEY is set, and against OpenAI (gpt-4o-mini) when OPENAI_API_KEY is set via ADK Kotlin's first-party com.google.adk:google-adk-kotlin-integrations-openai-jvm (ChatCompletions) integration.

Testing & Verification

  • Unit tests: 24 unit tests in dokimos-adk-java and 24 unit tests in dokimos-adk-kotlin covering trace extraction, parallel tool call correlation, OpenAPI/GenAI schema conversion to JSON Schema (including insertion-order preservation), RAG context extraction, multi-turn conversation simulation, CallMetrics (thoughtsTokenCount + PriceTable) computation, and JudgeLM adapters.
  • Live integration tests (@Tag("integration")): AdkAgentIT and AdkKotlinAgentIT support both OPENAI_API_KEY (for CI) and GEMINI_API_KEY (for local / Gemini runs).
  • Formatting & Docs: mvn spotless:check and mvn javadoc:javadoc pass cleanly on Java 17+.

@fkapsahili fkapsahili self-assigned this Oct 29, 2026

@fkapsahili fkapsahili left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glaforge Thanks a lot again for the contribution, and sorry for the long delay! I was quite busy the last few weeks. I love this PR! :)

I ran the build and tests for both ADK modules locally and everything passed. I think this looks really good overall.

A few small suggestions here:

Can you maybe return Collections.unmodifiableMap(map) instead of Map.copyOf(map) at the end of toSchemaMap? Map.copyOf doesn't keep the insertion order, so the schema keys can come out in a different order between runs.

Could you also check if thoughtsTokenCount should be part of tokensOut? As far as I know, thinking tokens are billed as output, so the cost would be a bit too low for thinking models. If you want to keep it as is, no problem, a short Javadoc note would be enough for me.

One bigger thing, but not blocking from my side: AdkSupport is quite large (about 2.5k lines and ~100 public static methods), with many sync, async and measured variants of the same things. Since all of it becomes public API, maybe we could start with a smaller set and add the other overloads when someone needs them? Happy to merge as is and do this in a follow-up, what do you think?

One more question: do we really need GEMINI_API_KEY for the integration tests? We use OPENAI_API_KEY for the others. For Kotlin I think Gemini is the only option, so this is fine by me. For Java maybe google-adk-langchain4j with OpenAI could work, but I haven't tried it. I can also just add the secret on our side.

None of these are blocking from my side. Thanks again!

…egration tests

- Add OpenAI integration test in AdkAgentIT (dokimos-adk-java) gated on
  OPENAI_API_KEY using com.google.adk:google-adk-langchain4j (LangChain4j
  wrapping OpenAiChatModel with OpenAiChatModelName.GPT_4_O_MINI), alongside
  the Gemini test gated on GEMINI_API_KEY.
- Add OpenAI integration test in AdkKotlinAgentIT (dokimos-adk-kotlin) gated
  on OPENAI_API_KEY using com.google.adk:google-adk-kotlin-integrations-openai-jvm
  (ChatCompletions with gpt-4o-mini), alongside the Gemini test gated on
  GEMINI_API_KEY.
- Document third-party model integrations in docs/docs/integrations/adk.md.
@glaforge

glaforge commented Nov 10, 2026 •

Copy link
Copy Markdown
Author

Thanks a lot for the thorough review @fkapsahili! I've addressed all four points across recent commits:

  1. Preserve LinkedHashMap insertion order in toSchemaMap (1e08d3b):

    • Replaced Map.copyOf(map) with Collections.unmodifiableMap(map) at the end of AdkSupport.toSchemaMap (and extractSchemaFromDeclaration) in dokimos-adk-java so the LinkedHashMap key order is preserved, and added a unit test verifying insertion order.
  2. Include thoughtsTokenCount in tokensOut (f3d7cfc):

    • Updated both AdkSupport and AdkTraceCollector in dokimos-adk-java and dokimos-adk-kotlin so tokensOut = candidatesTokenCount + thoughtsTokenCount (since Gemini thinking tokens are billed at the output token rate), documented this in Javadoc/KDoc, and added unit tests covering thinking token accumulation and PriceTable cost computation.
  3. Trim AdkSupport public API surface (264a628):

    • Removed >1,050 net lines across AdkSupport.java (88 public static methods -> 39; 2,522 lines -> 1,775 lines) and AdkSupport.kt (1,719 lines -> 1,470 lines):
      • Dropped redundant inputKey/outputKey/toolCallsKey permutations in Java (keeping the default "input" / "output" / "toolCalls" overloads and the full-custom overloads).
      • Dropped custom Executor overloads on asyncTask / asyncAgentTask / asyncRagTask (defaulting to ForkJoinPool.commonPool() in Java and Dispatchers.IO in Kotlin).
      • Dropped duplicate List<String> vs String... retrievalToolNames overloads in ragTask / asyncRagTask / measuredRagTask / extractTexts (keeping varargs).
      • Dropped 2-arg measuredTask(target, priceTable) overloads where modelId can be passed explicitly in the 3-arg overload.
      • Dropped measuredAsync* methods (asyncTask / asyncAgentTask / asyncRagTask in Kotlin now always populate CallMetrics, with optional modelId and priceTable parameters).
      • Dropped @JvmOverloads from multi-parameter functions in AdkSupport.kt.
      • Dropped toToolCall(FunctionCall, String) and toToolDefinitions(LlmAgent) in favor of toToolCall(FunctionCall) / toToolCall(FunctionCall, FunctionResponse) and toToolDefinitions(BaseAgent).
  4. Support both OPENAI_API_KEY and GEMINI_API_KEY in live integration tests (67ff768):

    • AdkAgentIT (dokimos-adk-java): Added a live OpenAI test gated on OPENAI_API_KEY using ADK Java's com.google.adk:google-adk-langchain4j integration (LangChain4j wrapping LangChain4j's OpenAiChatModel with OpenAiChatModelName.GPT_4_O_MINI), alongside the GEMINI_API_KEY test.
    • AdkKotlinAgentIT (dokimos-adk-kotlin): Added a live OpenAI test gated on OPENAI_API_KEY using ADK Kotlin's first-party com.google.adk:google-adk-kotlin-integrations-openai-jvm integration (ChatCompletions with gpt-4o-mini), alongside the GEMINI_API_KEY test.
    • This ensures the live integration tests execute in CI with OPENAI_API_KEY while still allowing local/CI runs against Gemini when GEMINI_API_KEY is provided.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants