KmemoAdvisor

class KmemoAdvisor @JvmOverloads constructor(cache: SemanticCache, order: Int = 0, scope: (ChatClientRequest) -> String = { SemanticCache.DEFAULT_SCOPE }) : CallAdvisor, StreamAdvisor

A caching org.springframework.ai.chat.client.advisor.api.Advisor for Spring AI's ChatClient, backed by a SemanticCache.

Spring AI has the advisor seam but ships no semantic cache, so this is the one-line win: register the advisor and repeated-in-meaning prompts are served from the cache — with kmemo's guards keeping the exchange rate for 250 USD from being served to someone who asked about 100.

val chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(KmemoAdvisor(cache))
.build()

The prompt's rendered text (org.springframework.ai.chat.prompt.Prompt.getContents) is the cache key. On a hit the advisor short-circuits the chain and returns the cached answer without calling the model; on a miss it calls the model through the chain, caches the assistant's reply text, and returns the model's real response untouched (metadata intact). Anything that changes what a correct answer looks like — the model, temperature, the system prompt — belongs in the scope, exactly as it does for SemanticCache.

Only the blocking call() path is cached. stream() calls pass straight through — a streamed response is not cached here — so the advisor is safe to register on a client used both ways.

The cache is a suspend API and the advisor SPI is blocking, so lookups run inside runBlocking on the calling request thread. That thread pays the embedding call on a miss; on a hit it pays one embedding and skips the model entirely, which is the whole point.

Parameters

cache

the semantic cache to serve from.

order

the advisor's order in the chain; keep it early so a hit avoids the work behind it.

scope

derives the cache scope from a request; constant SemanticCache.DEFAULT_SCOPE by default.

Constructors

Link copied to clipboard
constructor(cache: SemanticCache, order: Int = 0, scope: (ChatClientRequest) -> String = { SemanticCache.DEFAULT_SCOPE })

Properties

Link copied to clipboard
open override val name: String
Link copied to clipboard
open override val order: Int

Functions

Link copied to clipboard
open override fun adviseCall(request: ChatClientRequest, chain: CallAdvisorChain): ChatClientResponse
Link copied to clipboard
open override fun adviseStream(request: ChatClientRequest, chain: StreamAdvisorChain): Flux<ChatClientResponse>

Streaming is not cached; the request passes straight through the chain.

Link copied to clipboard
open override fun getName(): String
Link copied to clipboard
open override fun getOrder(): Int