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
17 changes: 17 additions & 0 deletions core/src/main/java/com/google/adk/sessions/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,23 @@ public Builder events(List<Event> events) {
return this;
}

/**
* Backs {@link Session#events()} with the given list directly, <b>without copying</b>, so the
* session reflects a caller-owned live or converting view whose events grow as the owner
* appends. The caller keeps ownership and the list's own semantics - thread-safety, and a
* read-only view throwing on {@code add} - so ordinary code should use {@link #events(List)},
* which defensively copies.
*
* @deprecated Not a real deprecation - a warn-off for application code from a seam meant for
* ADK's own framework and interop adapters; use {@link #events(List)} instead.
*/
@Deprecated
@CanIgnoreReturnValue
public Builder eventsView(List<Event> events) {
this.events = events;
return this;
}

@CanIgnoreReturnValue
public Builder lastUpdateTime(Instant lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
Expand Down
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 @@ -70,6 +71,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.11.0</kotlinx-coroutines.version>
<adk-kotlin.version>0.9.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
147 changes: 147 additions & 0 deletions tokt/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?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.9.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 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>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<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 tokt/src/main/java/com/google/adk/tokt/InteropDispatcher.kt
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
120 changes: 120 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,120 @@
/*
* 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: adapts ADK Java tools, toolsets, plugins, services, and models so
* they can run on the ADK Kotlin engine. Wrap the adapted pieces in a Kotlin `LlmAgent`; this does
* not convert a whole Java agent.
*
* 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.
*
* A few 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.
* - A bridged plugin's `onRunErrorCallback` does not fire: the engine surfaces run-level errors
* through the returned event stream to the caller, not to plugins. The `onModelErrorCallback` and
* `onToolErrorCallback` recovery hooks do fire.
* - A bridged tool's or plugin's `requestedAuthConfigs` and `deletedArtifactIds` writes do not
* reach the engine, whose event actions have no equivalent; a tool's `requestedToolConfirmations`
* does cross.
*/
object JavaAdkToKt {

/** 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)
}
34 changes: 34 additions & 0 deletions tokt/src/main/java/com/google/adk/tokt/KotlinAdkToJava.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.kt.runners.Runner as KtRunner
import com.google.adk.runner.Runner as JavaRunner

/**
* Reverse interop entry point: exposes an ADK Kotlin-engine [KtRunner] through the ADK Java
* [JavaRunner] surface, so it can be injected into code written against the Java runner. The
* returned runner is a real [JavaRunner] whose `runAsync` streams `Event`s backed by the Kotlin
* engine; live mode is not bridged. The forward direction (Java components onto the Kotlin engine)
* lives in [com.google.adk.tokt.JavaAdkToKt].
*/
object KotlinAdkToJava {

/** Exposes a Kotlin-engine [runner] as an ADK Java [JavaRunner]. */
@JvmStatic
fun asJavaRunner(runner: KtRunner): JavaRunner = KtRunnerToJava(runner)
}
Loading
Loading