Chat Routing and Google ADK¶
A chat turn starts with routing, not with a model draft. ADKRunner checks the
response cache, asks the Flash-Lite classifier for one intent label, and handles
the grounded routes directly from request-scoped services. Only
GENERAL_CONVERSATION falls through to the Google ADK 2.0 workflow.
flowchart TD
Q[user message] --> C{response cache}
C -->|hit| H[final event]
C -->|miss| I{Flash-Lite intent}
I -->|PRODUCT_RAG| P[product vector search]
P --> PV[structured selection<br/>and validation]
I -->|STORE_LOCATION| S[store lookup]
I -->|PRODUCT_AVAILABILITY| A[inventory lookup]
I -->|ORDER_STATUS| U[unsupported order response]
I -->|GENERAL_CONVERSATION| W[ADK workflow]
PV --> F[grounded final event]
S --> F
A --> F
U --> F
W --> F
Grounded Routes¶
The classifier returns one enum value:
PRODUCT_RAGfor menu, recommendation, roast, price, caffeine, and product questions.STORE_LOCATIONfor store, address, hours, nearest-cafe, and directions questions.PRODUCT_AVAILABILITYfor store-level pickup or stock questions.ORDER_STATUSfor order tracking, which this demo explicitly does not implement.GENERAL_CONVERSATIONfor greetings and small talk.
The first three labels are grounded routes. They query Oracle through
AgentToolsService, shape rows into store_results, inventory_results,
map_actions, metrics, and SQL phases, then emit one final event. Product
RAG may use Gemini structured output to select among returned product ids, but
the final product answer is rendered from Oracle rows. ORDER_STATUS returns a
clear unsupported message instead of asking the model to invent order data.
ADK Fallback¶
General conversation uses a Google ADK Workflow. The workflow still fans out
from START: one branch runs an LlmAgent, and the other runs the same
Flash-Lite classifier as a FunctionNode for workflow output labelling.
return Workflow(
name="coffee_workflow",
edges=[("START", intent, join), ("START", coffee, join), (join, merge)],
max_concurrency=2,
)
The ADK path has the same closure-bound tools as the deterministic path. If the model calls the product vector-search tool during a general turn, the runner treats the response as product-grounded and sends it through the same selector, validator, and row-renderer used by directly-routed Product RAG.
Closure-Bound Tools¶
ADK tools are plain async Python callables. Each request builds new closures
that capture the request-scoped AgentToolsService, which in turn holds the
SQLSpec driver and the product, store, cache, metrics, and Vertex AI services.
That is how tool calls use the same Oracle session as the rest of the request.
A representative tool:
async def search_products_by_vector(query: str, limit: int = 5, similarity_threshold: float = 0.7) -> dict[str, Any]:
"""Search the Cymbal Coffee menu with vector RAG.
Use for menu, catalog, recommendation, flavor, roast, price, caffeine,
availability, dietary substitution, and idiomatic preference requests.
"""
result = await tools_service.search_products_by_vector(query, limit, similarity_threshold)
_record_product_search_result(metric_state, result, query)
return result
The docstring is part of the model contract in the ADK fallback path. The deterministic route calls the same service method directly.
Streaming Contract¶
Grounded routes emit one final event. General conversation can emit delta
events from ADK before its final event. The final payload uses the same fields
either way:
Field |
Meaning |
|---|---|
|
The text the user sees |
|
|
|
Response cache hit |
|
Embedding cache hit during retrieval |
|
|
|
Per-phase timings the chat UI shows as badges |
|
Store rows for location turns |
|
Store-product rows for availability turns |
|
No-key Google Maps actions derived from store rows |
|
Request-safe location facts, never raw browser coordinates |
|
The ADK session, separate from the Litestar browser session |
Sessions¶
The Litestar browser session and the ADK conversation session are separate
stores. The chat controller bridges them by storing adk_session_id and
adk_user_id on the Litestar session at request time, then passing those
identifiers to the SQLSpec-backed OracleAsyncADKStore. ADK owns the
session-backed display history and fallback workflow state; Litestar owns the
browser cookie.
Where This Is Used¶
The walkthrough follows one product-RAG turn through this router: see the walkthrough.
RAG explains what happens inside the product vector-search route: see RAG.
Maps covers store lookup, availability rows, location opt-in, and Google Maps links: see Cymbal Coffee Maps.