@@ -40,6 +40,7 @@ import com.google.adk.kt.apps.App as KtApp
4040import com.google.adk.kt.callbacks.AfterModelCallback as KtAfterModelCallback
4141import com.google.adk.kt.events.Event as KtEvent
4242import com.google.adk.kt.events.EventActions as KtEventActions
43+ import com.google.adk.kt.models.CacheMetadata as KtCacheMetadata
4344import com.google.adk.kt.models.LlmResponse as KtLlmResponse
4445import com.google.adk.kt.runners.InMemoryRunner as KtInMemoryRunner
4546import com.google.adk.kt.runners.Runner as KtRunner
@@ -94,6 +95,7 @@ import com.google.adk.kt.workflow.Workflow as KtWorkflow
9495import com.google.adk.memory.InMemoryMemoryService as JavaInMemoryMemoryService
9596import com.google.adk.models.BaseLlm as JavaBaseLlm
9697import com.google.adk.models.BaseLlmConnection as JavaBaseLlmConnection
98+ import com.google.adk.models.CacheMetadata as JavaCacheMetadata
9799import com.google.adk.models.LlmRequest as JavaLlmRequest
98100import com.google.adk.models.LlmResponse as JavaLlmResponse
99101import com.google.adk.plugins.BasePlugin as JavaBasePlugin
@@ -3292,6 +3294,52 @@ class KtRunnerInteropTest {
32923294 )
32933295 }
32943296
3297+ @Test
3298+ fun ktRunner_bridgedPluginReturningTheResponse_keepsItsCacheMetadata () = runBlocking {
3299+ // The event keeps the response's cache metadata; the next turn finds its cache there.
3300+ val model =
3301+ object : JavaBaseLlm (" java-model" ) {
3302+ override fun generateContent (
3303+ llmRequest : JavaLlmRequest ,
3304+ stream : Boolean ,
3305+ ): Flowable <JavaLlmResponse > =
3306+ Flowable .just(
3307+ JavaLlmResponse .builder()
3308+ .content(modelText(" cached" ))
3309+ .cacheMetadata(
3310+ JavaCacheMetadata .builder().fingerprint(" abc" ).contentsCount(2 ).build()
3311+ )
3312+ .build()
3313+ )
3314+
3315+ override fun connect (llmRequest : JavaLlmRequest ): JavaBaseLlmConnection =
3316+ throw UnsupportedOperationException ()
3317+ }
3318+ // Returning the response sends it through LlmResponseCodec to Java and back.
3319+ val passthrough =
3320+ object : JavaBasePlugin (" passthrough" ) {
3321+ override fun afterModelCallback (
3322+ callbackContext : JavaCallbackContext ,
3323+ llmResponse : JavaLlmResponse ,
3324+ ): Maybe <JavaLlmResponse > = Maybe .just(llmResponse)
3325+ }
3326+ val runner =
3327+ KtInMemoryRunner (
3328+ KtApp (
3329+ appName = " app" ,
3330+ rootAgent = KtLlmAgent (name = " a" , model = JavaAdkToKt .asKtModel(model)),
3331+ plugins = listOf (JavaAdkToKt .asKtPlugin(passthrough)),
3332+ )
3333+ )
3334+
3335+ val events = runner.turn()
3336+
3337+ assertEquals(
3338+ KtCacheMetadata (fingerprint = " abc" , contentsCount = 2 ),
3339+ events.single { it.author == " a" }.cacheMetadata,
3340+ )
3341+ }
3342+
32953343 @Test
32963344 fun ktRunner_bridgedNoOpPlugin_preservesAgentTransfer () = runBlocking {
32973345 // Regression: bridging any Java plugin must not drop the request's Kotlin-only toolsDict in
@@ -4309,6 +4357,31 @@ class KtRunnerInteropTest {
43094357 )
43104358 }
43114359
4360+ @Test
4361+ fun asKtSessionService_cacheMetadata_survivesTheJavaStore () = runBlocking {
4362+ // The next turn finds the context cache to reuse in the stored events.
4363+ val sessions = JavaAdkToKt .asKtSessionService(JavaInMemorySessionService ())
4364+ val key = KtSessionKey (" app" , " u" , " s" )
4365+ val session = sessions.createSession(key)
4366+ val fingerprintOnly = KtCacheMetadata (fingerprint = " abc" , contentsCount = 2 )
4367+ val active =
4368+ KtCacheMetadata (
4369+ fingerprint = " abc" ,
4370+ contentsCount = 2 ,
4371+ cacheName = " cachedContents/1" ,
4372+ expireTime = 2_000_000_000_000L ,
4373+ invocationsUsed = 3 ,
4374+ createdAt = 1_999_999_000_000L ,
4375+ )
4376+
4377+ for (cache in listOf (fingerprintOnly, active)) {
4378+ val unused = sessions.appendEvent(session, KtEvent (author = " model" , cacheMetadata = cache))
4379+ }
4380+
4381+ val stored = assertNotNull(sessions.getSession(key)).events.map { it.cacheMetadata }
4382+ assertEquals(listOf (fingerprintOnly, active), stored)
4383+ }
4384+
43124385 @Test
43134386 fun eventCodec_workflowFields_roundTripThroughTheJavaEvent () {
43144387 // Java code reads these fields too, so a Kotlin Content in the output crosses as a genai one.
0 commit comments