Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<module>tutorials/city-time-weather</module>
<module>tutorials/live-audio-single-agent</module>
<module>a2a</module>
<module>tokt</module>
</modules>

<properties>
Expand Down Expand Up @@ -67,6 +68,10 @@
<truth.version>1.4.5</truth.version>
<jspecify.version>1.0.0</jspecify.version>
<rxjava.version>3.1.12</rxjava.version>
<!-- ADK Kotlin engine interop (google-adk-tokt module). -->
<kotlin.version>2.1.20</kotlin.version>
<kotlinx-coroutines.version>1.10.2</kotlinx-coroutines.version>
<adk-kotlin.version>0.6.0</adk-kotlin.version>
<reactor-core.version>3.7.0</reactor-core.version>
<wiremock.version>2.35.1</wiremock.version>
<assertj.version>3.27.7</assertj.version>
Expand Down
142 changes: 142 additions & 0 deletions tokt/pom.xml
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.7.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.</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 (mirroring the Blaze layout), 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>
102 changes: 102 additions & 0 deletions tokt/src/main/java/com/google/adk/tokt/JavaAdkToKt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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.artifacts.BaseArtifactService as JavaArtifactService
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.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: adapt ADK Java tools, toolsets, plugins, services, and models so
* they can run on the ADK Kotlin engine. Whole-agent conversion is intentionally omitted (running a
* Java agent's own multi-step flow on the Kotlin runner is not yet supported); wrap the Java SPI
* pieces into a Kotlin `LlmAgent` instead.
*
* **Session is re-projected, not a live handle.** An adapted Java tool / toolset / plugin sees the
* session through `context.session()`, which is re-projected on each call, so a *fresh* read is
* always current. The returned `Session` is a snapshot though: unlike native ADK Java its
* `events()` list does not grow in place and its state is a copy. Re-read `context.session()` each
* time; do not cache a `Session` (or its `events()`) across tool calls or turns. State *writes* via
* `toolContext.state()` / `callbackContext.state()` still propagate to the Kotlin.
*/
object JavaAdkToKt {

/**
* Adapts an ADK Java tool. Its `ToolContext.session()` is a per-call snapshot; see [JavaAdkToKt].
*/
@JvmStatic fun asKtTool(javaTool: JavaBaseTool): KtBaseTool = JavaToolToKt(javaTool)

/** Adapts a whole collection of ADK Java tools (e.g. an `LlmAgent`'s `tools`). */
@JvmStatic
fun asKtTools(javaTools: List<JavaBaseTool>): List<KtBaseTool> = javaTools.map { asKtTool(it) }

/**
* Adapts an ADK Java toolset. Its `ReadonlyContext` session is a per-call snapshot; see
* [JavaAdkToKt].
*/
@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. Its callback contexts expose a per-call session snapshot; see
* [JavaAdkToKt].
*/
@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)
}

@JvmStatic fun asKtModel(javaLlm: JavaBaseLlm): KtModel = JavaModelToKt(javaLlm)

@JvmStatic
fun asKtSessionService(service: JavaSessionService): KtSessionService =
javaSessionServiceAsKt(service)

@JvmStatic
fun asKtArtifactService(service: JavaArtifactService): KtArtifactService =
javaArtifactServiceAsKt(service)

@JvmStatic
fun asKtMemoryService(service: JavaMemoryService): KtMemoryService =
javaMemoryServiceAsKt(service)
}
58 changes: 58 additions & 0 deletions tokt/src/main/java/com/google/adk/tokt/adapters/JavaModelToKt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.kt.models.LlmRequest
import com.google.adk.kt.models.LlmResponse
import com.google.adk.kt.models.Model
import com.google.adk.models.BaseLlm as JavaBaseLlm
import com.google.adk.tokt.codecs.LlmRequestCodec
import com.google.adk.tokt.codecs.LlmResponseCodec
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.reactive.asFlow

/**
* Java -> Kotlin adapter: presents a user's ADK Java [JavaBaseLlm] as the Kotlin's [Model] so the
* ADK Kotlin agent loop can call it. The model seam for routing ADK Java's `LlmAgent` onto the
* Kotlin.
*
* The Kotlin `LlmRequest` is converted to a Java `LlmRequest` ([LlmRequestCodec]), the Java model's
* RxJava `Flowable<LlmResponse>` is consumed as a coroutine [Flow] (via
* kotlinx-coroutines-reactive) and each response is converted back ([LlmResponseCodec]).
*/
internal class JavaModelToKt(private val javaLlm: JavaBaseLlm) : Model {

override val name: String = javaLlm.model()

// Deferred into `flow {}` and dispatched on IO: the Java model's request build + generation run
// off the engine dispatcher (RxJava is synchronous by default), and a synchronous throw is routed
// through the Flow's error channel rather than escaping at collection time.
override fun generateContent(request: LlmRequest, stream: Boolean): Flow<LlmResponse> =
flow {
emitAll(
javaLlm.generateContent(LlmRequestCodec.toJava(request), stream).asFlow().map {
LlmResponseCodec.fromJava(it)
}
)
}
.flowOn(Dispatchers.IO)
}
Loading
Loading