Merge https://github.com/google/adk-python/pull/5035
Enable injecting pre-configured LLM clients into Gemini and AnthropicLlm models to support multi-agent systems with distinct configurations.
Fixes#5027
PiperOrigin-RevId: 968151995
Gemini 3.x Live does not begin generating from `send_client_content` alone; it
starts only once it receives realtime input. As a result, seeding a new live
connection with prior conversation history left the model silent until the user
spoke again. This is most visible right after an agent transfer, where the
sub-agent is expected to reply immediately.
`GeminiLlmConnection.send_history` now follows the replayed history with a
minimal placeholder realtime input for Gemini 3.x Live models, gated on the
history ending with a user turn (the same condition that sets `turn_complete`).
Histories that end on a model turn still leave the model waiting for new user
input, preserving the documented `send_history` behavior. Other model families
are unaffected.
Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 967411170
Extract cache creation (write) tokens from LiteLLM and Anthropic model
usage metadata and map them to the GenerateContentResponseUsageMetadata
object. This ensures they are recorded in telemetry, allowing correct cost
calculations for prompt caching with providers like Bedrock.
Close#5835
PiperOrigin-RevId: 967259812
This CL introduces `_json_utils.safe_json_loads` to provide a uniform ValueError
when JSON parsing fails, wrapping the underlying json.JSONDecodeError.
Initial callsites in evaluations, sessions, and some models have been migrated.
Merge https://github.com/google/adk-python/pull/5858
PiperOrigin-RevId: 966722835
Merge https://github.com/google/adk-python/pull/6617
Resolves fragile parts[0] indexing assumptions in the core Gemini Live API connection and flow modules by scanning all parts.
Fixes#6616
PiperOrigin-RevId: 964390074
Merge https://github.com/google/adk-python/pull/6578
Resolve issue where nested providers behind a litellm_proxy prefix (like litellm_proxy/azure/...) were not correctly identified, causing provider-specific behavior (like Azure file uploads) to be skipped.
Fixes#6538
PiperOrigin-RevId: 964389247
Add tests to verify that pre-tool-call text is correctly preserved and not
duplicated in the final aggregated tool-call response in LiteLLM.
Closes#3697
PiperOrigin-RevId: 964379750
Safety settings configured via `LlmAgent.generate_content_config` were silently
dropped on the Live (bidiGenerateContent) path. `Gemini.connect()` copied only
`system_instruction`, `tools` and `thinking_config` from `LlmRequest.config`
into `LlmRequest.live_connect_config`, so `safetySettings` never reached
the server on either the Vertex AI or the Gemini API backend. The non-live path
was unaffected, because it forwards the whole `GenerateContentConfig` to
`generate_content`.
This has been the behavior since the first release; it is not a regression.
BEHAVIOR CHANGE: agents that set `safety_settings` in `generate_content_config`
and run under `run_live()` will now have those settings applied. Review the safety configuration of live agents before upgrading.
Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 961126550
Gemini 1.x is fully deprecated, so sorting Gemini model ids into "1.x"
and "or 2.0+" buckets no longer buys anything. Non-Gemini ids are unaffected: they still raise error.
PiperOrigin-RevId: 960655458
This is an observable behavior change: a configured timeout now takes
effect, so a request that used to hang far past it fails at the timeout
instead.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 958669285
Not annotations-only. This is one component's slice of a repo-wide typing
cleanup, and the wider change was found to contain behavior changes that have
not all been individually triaged, so please review it as a functional change.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 958623646
This change adds an LlmCapabilities class and a capabilities property to BaseLlm. This allows LLM instances to explicitly declare their supported features (starting with output_schema_with_tools) rather than having callers infer support from model names or types. Gemini and LiteLlm are updated to self-report their capabilities, while a deprecated name-based fallback with a warning is provided on BaseLlm for backwards compatibility.
This change is a no-op. The LlmCapabilities is not being used yet.
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 956764717
Merge https://github.com/google/adk-python/pull/5401
## Summary
- wrap Anthropic RateLimitError in a dedicated ADK exception with mitigation guidance
- apply the wrapper consistently for both streaming and non-streaming Claude requests
- add regression tests for both code paths
## Testing
- python3 -m py_compile src/google/adk/models/anthropic_llm.py tests/unittests/models/test_anthropic_llm.py
- python3 -m pytest tests/unittests/models/test_anthropic_llm.py -k "wraps_anthropic_rate_limit_error" *(fails in this environment because the pytest interpreter is missing the package during collection)*
Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/5401 from MukundaKatta:codex/adk-anthropic-rate-limit e08c51b37c3962968d959ae96181518f4de5a14a
PiperOrigin-RevId: 956692885
Merge https://github.com/google/adk-python/pull/4490
**Problem:**
`send_realtime` method only accepted `Blob` (audio/video bytes), `ActivityStart`, and `ActivityEnd`. There's no mechanism to send the `audioStreamEnd` boolean field, which is required to flush cached audio when Voice Activity Detection ([VAD](https://ai.google.dev/gemini-api/docs/live-guide#interruptions)) is enabled.
**Solution:**
This PR updates the `GeminiLlmConnection`, `BaseLlmFlow`, and `LiveRequestQueue` to support sending generic `LiveClientRealtimeInput` messages with `audio_stream_end` field configured to the [Gemini Live API](https://googleapis.github.io/python-genai/genai.html#genai.live.AsyncSession.send_realtime_input). This closes#2887.
### Testing Plan
**Unit Tests:**
- added unit test `test_send_realtime_audiostreamend`
- all unit tests passing locally
### Additional context
- processing priority: activity_start > activity_end > audio_stream_end > blob > content
- this is to follows the principal of control signals > data payloads, since `audio_stream_end` is a signal to flush the audio buffer
Co-authored-by: Liang Wu <wuliang@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/4490 from lottielin:support-audio-stream-end ea6d2dfae3b9f1c5456b8cd8c74d16163443c06c
PiperOrigin-RevId: 956638801
The Apigee model accepted a configured http_options.timeout but silently ignored it, so a stalled request could wait forever. This passes the timeout through to Apigee's OpenAI-compatible HTTP calls for both streaming and non-streaming requests, converting the documented Google GenAI millisecond value to HTTPX seconds and keeping the existing unlimited default when none is set.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 956171405
Wire the existing to_google_genai_finish_reason mapping into both the
non-streaming and streaming responses; previously finish_reason was
always unset on Claude responses.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 955985680
Merge https://github.com/google/adk-python/pull/5924
END_PUBLIC
## Summary
Fixes two issues that prevent Claude-family models from entering the ReAct tool-calling loop when used via LiteLLM inside a nested `AgentTool`:
1. **`AgentTool` with `input_schema`** — the serialized JSON payload sent as the first message causes Claude to interpret the request as already complete and respond directly without calling any tools.
2. **`tool_choice` not propagated** — `llm_request.config.tool_config.function_calling_config.mode` was not forwarded to LiteLLM's `completion_args`, so callers could not enforce tool use at the request level.
## Changes
### `src/google/adk/tools/agent_tool.py`
Wrap the serialized `input_schema` JSON in a natural-language instruction that explicitly asks the inner agent to use its available tools before producing a response. This keeps Claude in ReAct mode regardless of the message content format.
### `src/google/adk/models/lite_llm.py`
Read `llm_request.config.tool_config.function_calling_config.mode` and map it to LiteLLM's `tool_choice` parameter:
- `ANY` → `"required"`
- `NONE` → `"none"`
- `AUTO` → provider default (unchanged, key omitted from `completion_args`)
`_get_completion_inputs` now returns a 5-tuple `(messages, tools, response_format, generation_params, tool_choice)`.
## Unit Tests Added
### `tests/unittests/tools/test_agent_tool.py`
- `test_run_async_no_input_schema_passes_request_unchanged`: without `input_schema`, the content passed to the inner runner is `args['request']` verbatim.
- `test_run_async_with_input_schema_wraps_in_natural_language`: with `input_schema`, the text begins with `"Process the following structured request"`, contains `"Request:\n"` followed by the JSON payload, and is not a bare JSON blob.
- `test_run_async_with_input_schema_text_not_raw_json`: asserts the text does not start with `{`.
### `tests/unittests/models/test_litellm.py`
- `test_get_completion_inputs_tool_choice_none_without_tool_config`: `tool_choice` is `None` with no `tool_config`.
- `test_get_completion_inputs_tool_choice_required_for_any_mode`: returns `"required"` for `ANY` mode.
- `test_get_completion_inputs_tool_choice_none_for_none_mode`: returns `"none"` for `NONE` mode.
- `test_get_completion_inputs_tool_choice_none_for_auto_mode`: returns `None` for `AUTO` mode.
- `test_generate_content_async_propagates_tool_choice_required`: `acompletion` receives `tool_choice="required"` for `ANY`.
- `test_generate_content_async_propagates_tool_choice_none_mode`: `acompletion` receives `tool_choice="none"` for `NONE`.
- `test_generate_content_async_omits_tool_choice_for_auto_mode`: `tool_choice` key absent from `completion_args` for `AUTO`.
- `test_generate_content_async_omits_tool_choice_without_tool_config`: `tool_choice` key absent when no `tool_config`.
Also updated all existing `_get_completion_inputs` call sites (10 occurrences) to unpack the new 5-tuple.
## Pytest Results
```
tests/unittests/tools/test_agent_tool.py + tests/unittests/models/test_litellm.py
1 failed (pre-existing: test_custom_schema[GOOGLE_AI] — unrelated to this PR),
302 passed, 1 skipped in 2.98s
New tests: 11 passed (3 agent_tool + 8 litellm)
```
The 1 pre-existing failure (`test_custom_schema[GOOGLE_AI]`) is a `pydantic.ValidationError` that reproduces on the base branch before any of these changes and is unrelated to this fix.
## Related
- Fixes#5926
- Addresses #773 (expose tool_choice to callers via FunctionCallingConfig)
- Related #1063 (fixed FunctionDeclaration description in v1.20, different issue)
Co-authored-by: George Weale <gweale@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/5924 from ecanlar:fix/agent-tool-input-schema-tool-choice-litellm 9cd8f31167e0a2a55c76e6cc8ea4a7d0850fd243
PiperOrigin-RevId: 955433983
Merge https://github.com/google/adk-python/pull/5512
## Summary
Fixes#5394
`AnthropicLlm` was calling `to_google_genai_finish_reason()` but never wiring
its result into `LlmResponse`, so `finish_reason` was always `None`. This fix
connects it end-to-end and expands the mapping to cover all stop reasons the
current Anthropic API can return.
- **`to_google_genai_finish_reason`** — extended to cover `pause_turn` → `STOP`
(streaming interruption, e.g. server-side tool use) and `refusal` → `SAFETY`
(model refusal); all other unknown values continue to return
`FINISH_REASON_UNSPECIFIED`
- **`message_to_generate_content_response`** — passes `stop_reason` through
`to_google_genai_finish_reason` and sets it on the returned `LlmResponse`
- **`_generate_content_streaming`** — reads `stop_reason` from the
`message_delta` event and sets `finish_reason` on the final aggregated
`LlmResponse`
Co-authored-by: Jason Zhang <jasoncz@google.com>
PiperOrigin-RevId: 952921402
Ensures function name is included when converting interaction steps and deltas into FunctionResponse parts. Unit tests have been updated to verify that the name field is correctly populated.
PiperOrigin-RevId: 952413239
The Interactions adapter already sends only the current turn when a previous interaction id is present, but the standard flow still walked and copied the whole session history first, and could invoke the explicit cache manager even though Interactions only supports implicit caching. This finds the previous interaction id up front, assembles just the current turn for those requests, and skips explicit cache mutation on that path. Ordinary GenerateContent requests keep full history.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 952369834
When streaming a Claude response with extended thinking enabled,
`AnthropicLlm.generate_content_async` consumed `thinking_delta` and `text_delta`
content-block deltas but ignored `signature_delta`. The aggregated thinking
`Part` was therefore assembled without its `thought_signature`.
On a follow-up request within the same turn (for example, after a tool call),
that signature-less thinking block cannot be serialized back into a valid
Anthropic `thinking` block, breaking multi-step (tool-calling) turns when
streaming is enabled. The non-streaming path is unaffected because it already
captures the signature via `content_block_to_part`.
Handle `SignatureDelta` in the streaming content-block-delta loop so the
signature is accumulated and attached to the aggregated thinking `Part`,
matching the non-streaming path. Extended-thinking + tool-use turns now
round-trip correctly under streaming.
Adds a regression test asserting that a streamed `signature_delta` lands on the
final thinking part and round-trips to a valid Anthropic thinking block.
Co-authored-by: Vishwa Murugan <vishwamurugan@google.com>
PiperOrigin-RevId: 952256615
When converting an inlined artifact Part to a LiteLlm/OpenAI file block,
the MIME type of non-image files was dropped and sent as None, so OpenAI
rejected PDFs loaded via load_artifacts (while Gemini and Claude worked).
Propagate the part's mime_type into the file content block.
Close#4174
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 949061584
The llm_client field holds a LiteLLMClient with no JSON representation, so
serializing a LiteLlm agent (as adk web and api_server do when returning
the agent graph) raised PydanticSerializationError and 500'd every request
for LiteLlm-based agents. Exclude the client from serialization; it is
runtime state, not data.
Close#6164
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 949034831
The request-log string was built before logger.debug decided whether to emit it, so at INFO or WARNING the code still serialized the full request, tool declarations, and config on every model call. This guards the formatting with logger.isEnabledFor(DEBUG) and leaves logging unchanged when debug is on.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 948473770