-
Notifications
You must be signed in to change notification settings - Fork 412
feat: translate ADK Java LlmAgent and workflow agents to native engine agents #1454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!-- | ||
| Copyright 2026 Google LLC | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| --> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>com.google.adk</groupId> | ||
| <artifactId>google-adk-parent</artifactId> | ||
| <version>1.8.1-SNAPSHOT</version><!-- {x-version-update:google-adk:current} --> | ||
| </parent> | ||
|
|
||
| <artifactId>google-adk-tokt</artifactId> | ||
| <name>Agent Development Kit - Kotlin engine interop</name> | ||
| <description>One-way interop that adapts ADK Java agents, tools, toolsets, plugins, services, and models so they can run on the ADK Kotlin engine. The name is short for "to Kotlin", matching the JavaAdkToKt entry point.</description> | ||
|
|
||
| <dependencies> | ||
| <!-- ADK Java facade types being adapted. --> | ||
| <dependency> | ||
| <groupId>com.google.adk</groupId> | ||
| <artifactId>google-adk</artifactId> | ||
| <version>${project.version}</version> | ||
| </dependency> | ||
| <!-- ADK Kotlin engine the Java types are adapted to. --> | ||
| <dependency> | ||
| <groupId>com.google.adk</groupId> | ||
| <artifactId>google-adk-kotlin-core-jvm</artifactId> | ||
| <version>${adk-kotlin.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.google.genai</groupId> | ||
| <artifactId>google-genai</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.reactivex.rxjava3</groupId> | ||
| <artifactId>rxjava</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.google.guava</groupId> | ||
| <artifactId>guava</artifactId> | ||
| <version>33.0.0-jre</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jspecify</groupId> | ||
| <artifactId>jspecify</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlin</groupId> | ||
| <artifactId>kotlin-stdlib</artifactId> | ||
| <version>${kotlin.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlinx</groupId> | ||
| <artifactId>kotlinx-coroutines-core-jvm</artifactId> | ||
| <version>${kotlinx-coroutines.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlinx</groupId> | ||
| <artifactId>kotlinx-coroutines-rx3</artifactId> | ||
| <version>${kotlinx-coroutines.version}</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlinx</groupId> | ||
| <artifactId>kotlinx-coroutines-reactive</artifactId> | ||
| <version>${kotlinx-coroutines.version}</version> | ||
| </dependency> | ||
|
|
||
| <!-- Test. --> | ||
| <dependency> | ||
| <groupId>org.jetbrains.kotlin</groupId> | ||
| <artifactId>kotlin-test-junit</artifactId> | ||
| <version>${kotlin.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>junit</groupId> | ||
| <artifactId>junit</artifactId> | ||
| <version>4.13.2</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <!-- Kotlin sources live under src/main/java, so point the Kotlin compiler at those | ||
| directories explicitly. --> | ||
| <plugin> | ||
| <groupId>org.jetbrains.kotlin</groupId> | ||
| <artifactId>kotlin-maven-plugin</artifactId> | ||
| <version>${kotlin.version}</version> | ||
| <configuration> | ||
| <jvmTarget>${java.version}</jvmTarget> | ||
| <args> | ||
| <arg>-opt-in=kotlin.time.ExperimentalTime</arg> | ||
| </args> | ||
| </configuration> | ||
| <executions> | ||
| <execution> | ||
| <id>compile</id> | ||
| <phase>compile</phase> | ||
| <goals> | ||
| <goal>compile</goal> | ||
| </goals> | ||
| <configuration> | ||
| <sourceDirs> | ||
| <sourceDir>${project.basedir}/src/main/java</sourceDir> | ||
| </sourceDirs> | ||
| </configuration> | ||
| </execution> | ||
| <execution> | ||
| <id>test-compile</id> | ||
| <phase>test-compile</phase> | ||
| <goals> | ||
| <goal>test-compile</goal> | ||
| </goals> | ||
| <configuration> | ||
| <sourceDirs> | ||
| <sourceDir>${project.basedir}/src/test/java</sourceDir> | ||
| </sourceDirs> | ||
| </configuration> | ||
| </execution> | ||
| </executions> | ||
| </plugin> | ||
| <plugin> | ||
| <artifactId>maven-surefire-plugin</artifactId> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> | ||
29 changes: 29 additions & 0 deletions
29
tokt/src/main/java/com/google/adk/tokt/InteropDispatcher.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.adk.tokt | ||
|
|
||
| import kotlinx.coroutines.CoroutineDispatcher | ||
| import kotlinx.coroutines.Dispatchers | ||
|
|
||
| /** | ||
| * The dispatcher every crossing in this module hops to. | ||
| * | ||
| * ADK Java's SPI is RxJava, which is synchronous unless the implementation says otherwise, so a | ||
| * user-authored Java tool, plugin or service may block. Running it on the engine's dispatcher would | ||
| * stall the coroutine driving the agent loop, so each adapter moves the call here. | ||
| */ | ||
| internal val InteropDispatcher: CoroutineDispatcher = Dispatchers.IO |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.adk.tokt | ||
|
|
||
| import com.google.adk.agents.BaseAgent as JavaBaseAgent | ||
| import com.google.adk.artifacts.BaseArtifactService as JavaArtifactService | ||
| import com.google.adk.kt.agents.BaseAgent as KtBaseAgent | ||
| import com.google.adk.kt.artifacts.ArtifactService as KtArtifactService | ||
| import com.google.adk.kt.memory.MemoryService as KtMemoryService | ||
| import com.google.adk.kt.models.Model as KtModel | ||
| import com.google.adk.kt.plugins.Plugin as KtPlugin | ||
| import com.google.adk.kt.sessions.SessionService as KtSessionService | ||
| import com.google.adk.kt.tools.BaseTool as KtBaseTool | ||
| import com.google.adk.kt.tools.Toolset as KtToolset | ||
| import com.google.adk.memory.BaseMemoryService as JavaMemoryService | ||
| import com.google.adk.models.BaseLlm as JavaBaseLlm | ||
| import com.google.adk.plugins.Plugin as JavaPlugin | ||
| import com.google.adk.sessions.BaseSessionService as JavaSessionService | ||
| import com.google.adk.tokt.adapters.JavaModelToKt | ||
| import com.google.adk.tokt.adapters.JavaPluginToKt | ||
| import com.google.adk.tokt.adapters.JavaToolToKt | ||
| import com.google.adk.tokt.adapters.JavaToolsetToKt | ||
| import com.google.adk.tokt.adapters.javaAgentAsKt | ||
| import com.google.adk.tokt.services.javaArtifactServiceAsKt | ||
| import com.google.adk.tokt.services.javaMemoryServiceAsKt | ||
| import com.google.adk.tokt.services.javaSessionServiceAsKt | ||
| import com.google.adk.tools.BaseTool as JavaBaseTool | ||
| import com.google.adk.tools.BaseToolset as JavaBaseToolset | ||
|
|
||
| /** | ||
| * Forward interop entry point: adapts ADK Java agents, tools, toolsets, plugins, services, and | ||
| * models so they can run on the ADK Kotlin engine. [asKtAgent] adapts a whole Java agent, | ||
| * translating ADK's own agent types to native engine agents and running any other agent opaquely; | ||
| * alternatively wrap the individual Java pieces in a Kotlin `LlmAgent`. | ||
| * | ||
| * An adapted component behaves as it does on ADK Java. It sees the session as it currently stands, | ||
| * including events and state written earlier in the same turn, and its state, artifact and | ||
| * control-flow writes reach the engine. Blocking work is fine: calls are dispatched off the thread | ||
| * driving the agent. | ||
| * | ||
| * Two things to know before relying on them: | ||
| * - Setting `branch` on a bridged context throws. The branch is the engine's to set. | ||
| * - Behind an adapted Java session service ([asKtSessionService]), a resumable workflow restarts | ||
| * instead of resuming, because ADK Java has nowhere to store the engine's resumption state. Keep | ||
| * the Kotlin session service if you rely on resumability. | ||
| */ | ||
| object JavaAdkToKt { | ||
|
|
||
| /** | ||
| * Adapts a whole ADK Java agent so it runs on the Kotlin engine. An exact `LlmAgent` and the | ||
| * exact `Sequential` / `Parallel` / `Loop` workflow agents are translated to native engine | ||
| * agents, so the engine drives their model, tool, and agent callbacks directly. Any subclass of | ||
| * those, any other agent, or an `LlmAgent` using a feature the engine cannot reproduce runs | ||
| * opaquely as a leaf via `JavaAgentToKt`, preserving its own behavior. | ||
| */ | ||
| @JvmStatic fun asKtAgent(javaAgent: JavaBaseAgent): KtBaseAgent = javaAgentAsKt(javaAgent) | ||
|
|
||
| /** | ||
| * Adapts a whole collection of ADK Java agents (e.g. to hand a Kotlin agent its `subAgents`). | ||
| * Kept alongside [asKtAgent] for Java callers, as with [asKtTools]. | ||
| */ | ||
| @JvmStatic | ||
| fun asKtAgents(javaAgents: List<JavaBaseAgent>): List<KtBaseAgent> = javaAgents.map { | ||
| asKtAgent(it) | ||
| } | ||
|
|
||
| /** Adapts an ADK Java tool. */ | ||
| @JvmStatic fun asKtTool(javaTool: JavaBaseTool): KtBaseTool = JavaToolToKt(javaTool) | ||
|
|
||
| /** | ||
| * Adapts a whole collection of ADK Java tools (e.g. an `LlmAgent`'s `tools`). Kept alongside | ||
| * [asKtTool] for Java callers, who would otherwise write `stream().map(...).toList()`. | ||
| */ | ||
| @JvmStatic | ||
| fun asKtTools(javaTools: List<JavaBaseTool>): List<KtBaseTool> = javaTools.map { asKtTool(it) } | ||
|
|
||
| /** Adapts an ADK Java toolset. */ | ||
| @JvmStatic fun asKtToolset(javaToolset: JavaBaseToolset): KtToolset = JavaToolsetToKt(javaToolset) | ||
|
|
||
| /** Adapts a whole collection of ADK Java toolsets. */ | ||
| @JvmStatic | ||
| fun asKtToolsets(javaToolsets: List<JavaBaseToolset>): List<KtToolset> = javaToolsets.map { | ||
| asKtToolset(it) | ||
| } | ||
|
|
||
| /** Adapts an ADK Java plugin. */ | ||
| @JvmStatic fun asKtPlugin(javaPlugin: JavaPlugin): KtPlugin = JavaPluginToKt(javaPlugin) | ||
|
|
||
| /** Adapts a whole collection of ADK Java plugins (e.g. a `Runner`'s `plugins`). */ | ||
| @JvmStatic | ||
| fun asKtPlugins(javaPlugins: List<JavaPlugin>): List<KtPlugin> = javaPlugins.map { | ||
| asKtPlugin(it) | ||
| } | ||
|
|
||
| /** Adapts an ADK Java model so the Kotlin engine can call it. */ | ||
| @JvmStatic fun asKtModel(javaLlm: JavaBaseLlm): KtModel = JavaModelToKt(javaLlm) | ||
|
|
||
| /** | ||
| * Adapts an ADK Java session service for the Kotlin engine. Passing a service that is itself an | ||
| * adapted Kotlin one returns the original rather than stacking a second adapter. Note the | ||
| * `agentState` / `rewindBeforeInvocationId` loss described in [JavaAdkToKt]. | ||
| */ | ||
| @JvmStatic | ||
| fun asKtSessionService(service: JavaSessionService): KtSessionService = | ||
| javaSessionServiceAsKt(service) | ||
|
|
||
| /** | ||
| * Adapts an ADK Java artifact service for the Kotlin engine, unwrapping a round-tripped Kotlin | ||
| * one rather than stacking adapters. An empty or unmapped artifact part is rejected outright. | ||
| */ | ||
| @JvmStatic | ||
| fun asKtArtifactService(service: JavaArtifactService): KtArtifactService = | ||
| javaArtifactServiceAsKt(service) | ||
|
|
||
| /** | ||
| * Adapts an ADK Java memory service for the Kotlin engine, unwrapping a round-tripped Kotlin one | ||
| * rather than stacking adapters. | ||
| */ | ||
| @JvmStatic | ||
| fun asKtMemoryService(service: JavaMemoryService): KtMemoryService = | ||
| javaMemoryServiceAsKt(service) | ||
| } |
41 changes: 41 additions & 0 deletions
41
tokt/src/main/java/com/google/adk/tokt/adapters/AgentAdapters.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.adk.tokt.adapters | ||
|
|
||
| import com.google.adk.agents.BaseAgent as JavaBaseAgent | ||
| import com.google.adk.kt.agents.BaseAgent as KtBaseAgent | ||
|
|
||
| /** | ||
| * The Java view of a Kotlin agent, for handing to a Java plugin callback. An agent that is itself | ||
| * an adapted Java one is unwrapped to the original rather than wrapped again, so a Java plugin | ||
| * pairing callbacks by agent identity sees the instance it knows. | ||
| * | ||
| * Note the unwrapped case returns a *runnable* Java agent, so "a plugin must not run the agent it | ||
| * is given" ([KtAgentToJava]) is convention there rather than something the type enforces. | ||
| */ | ||
| internal fun ktAgentAsJava(ktAgent: KtBaseAgent): JavaBaseAgent = | ||
| (ktAgent as? JavaAgentToKt)?.javaAgent ?: KtAgentToJava(ktAgent) | ||
|
|
||
| /** | ||
| * The Kotlin agent for a Java one, so it can run on the engine. A Java agent that is itself a view | ||
| * of a Kotlin agent is unwrapped, so a Kt -> Java -> Kt round trip collapses to the original | ||
| * instead of running the engine through two adapters. Everything else is routed through | ||
| * [AgentCodec], which translates ADK's own agent types to native engine agents and falls back to | ||
| * the opaque [JavaAgentToKt] for the rest. | ||
| */ | ||
| internal fun javaAgentAsKt(javaAgent: JavaBaseAgent): KtBaseAgent = | ||
| (javaAgent as? KtAgentToJava)?.ktAgent ?: AgentCodec.toKotlin(javaAgent) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-groupId