Add Google ADK adapter plugin prototype#5108
Add Google ADK adapter plugin prototype#5108SebastianBoehler wants to merge 1 commit intolivekit:mainfrom
Conversation
| def _delta_text(text: str, emitted_text: str) -> tuple[str, str]: | ||
| if text.startswith(emitted_text): | ||
| delta = text[len(emitted_text) :] | ||
| return delta, text | ||
|
|
||
| return text, emitted_text + text |
There was a problem hiding this comment.
π΄ _delta_text fallback causes duplicate text emission when ADK produces multiple invocations with cumulative events
When an ADK run_async call produces events from multiple invocations (e.g., multi-agent or tool-calling scenarios where the model responds, invokes a tool, then responds again), _delta_text emits duplicate text. The function tracks all emitted text via concatenation in the fallback path (return text, emitted_text + text at line 323). When a second invocation starts with fresh cumulative text, its partial events don't start with the accumulated emitted_text, so they hit the fallback and are emitted in full. Then the next cumulative event from the same invocation also can't match emitted_text (which now includes text from both invocations), so it's also emitted in full β duplicating the partial text.
Concrete trace showing the duplication
ADK events: inv1 partial text="A", inv1 final text="AB", inv2 partial text="C", inv2 final text="CD"
- text="A", emitted="" β startswith("") β β delta="A", emitted="A"
- text="AB", emitted="A" β startswith("A") β β delta="B", emitted="AB"
- text="C", emitted="AB" β startswith("AB") β β fallback β delta="C", emitted="ABC"
- text="CD", emitted="ABC" β startswith("ABC") β β fallback β delta="CD", emitted="ABCCD"
Consumer receives: "A"+"B"+"C"+"CD" = "ABCCD" instead of expected "ABCD". The "C" prefix is duplicated.
Prompt for agents
In livekit-plugins/livekit-plugins-google-adk/livekit/plugins/google_adk/adk.py, the _delta_text function (lines 318-323) needs to be reworked to properly handle multi-invocation scenarios where cumulative text resets. One approach is to track the previous event's full text separately from total emitted text: when the new text doesn't start with the previous full text, treat it as a fresh invocation and reset the cumulative tracking. Alternatively, use the ADK event's invocation_id to detect invocation boundaries and reset emitted_text tracking on each new invocation. The _run method (line 182 onward) would need to pass the invocation_id context into the delta tracking logic.
Was this helpful? React with π or π to provide feedback.
Summary
livekit-plugins-google-adkplugin packagegoogle_adk.LLMAdapterthat wraps an ADK runner/agent behind LiveKit'sllm.LLMinterfaceMotivation
This is a prototype for first-class Google ADK support, similar in spirit to the existing LangChain plugin.
Issue: #5107
Notes
ChatContextfor eachchat()callgoogle-adkinstead of declaring it as a hard workspace dependency because ADK currently pinsopentelemetry-api/opentelemetry-sdkto<1.39, which conflicts with this repo's~=1.39.0pinsgoogle-adkseparately alongside this pluginValidation
uv run pytest tests/test_google_adk.pyuv run ruff check livekit-plugins/livekit-plugins-google-adk tests/test_google_adk.py