284 Commits

Author SHA1 Message Date
George Weale b1c984baa2 fix: honor ContextCacheConfig on the LiteLLM path
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 970091291
2026-08-24 14:44:11 -07:00
George Weale 811d379fdf fix: emit Anthropic prompt cache breakpoints for ContextCacheConfig
Fixes #5395

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 970042984
2026-08-24 13:29:39 -07:00
brucearctor a01d516a6b feat: support injecting custom LLM clients into Gemini and AnthropicLlm
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
2026-08-20 17:29:45 -07:00
Liang Wu 7616c78a1e fix: trigger Gemini 3.x Live response after sending conversation history
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
2026-08-19 14:28:55 -07:00
Anas Khan 1ed8d48620 fix: guard against Content with no parts in _content_to_message_param
Merge https://github.com/google/adk-python/pull/6312

Avoid raising TypeError when types.Content has parts=None or parts=[] in LiteLLM adapter.

PiperOrigin-RevId: 967357294
2026-08-19 12:48:40 -07:00
Google Team Member d0b33a0569 fix: cache read write token counts in LiteLLM and Anthropic models
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
2026-08-19 09:44:47 -07:00
prasanna8585 1cd6f464e5 fix: redact credentials from generate_content_config.http_options in debug logs
Merge https://github.com/google/adk-python/pull/6546

PiperOrigin-RevId: 967026896
2026-08-19 00:16:10 -07:00
Nikhil Chaudhary ff4567df38 fix: pass file metadata tuple to Azure for PDF uploads
Merge https://github.com/google/adk-python/pull/6548

Fixes #6539

PiperOrigin-RevId: 967023375
2026-08-19 00:07:02 -07:00
Jonathan Hill 2876987a78 refactor: introduce safe_json_loads helper and migrate selected callsites
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
2026-08-18 11:54:55 -07:00
Asjad Abbas 42a4a5f0e7 fix: resolve Claude 5 model names in the LLM registry
Merge https://github.com/google/adk-python/pull/6558

PiperOrigin-RevId: 966679423
2026-08-18 10:41:28 -07:00
Ahmed-Ali-313 7c2af5f9c1 fix: improve clarity and actionable context in error messages
Merge https://github.com/google/adk-python/pull/6487

PiperOrigin-RevId: 964922800
2026-08-14 15:33:07 -07:00
Harshitmishra001 2109ea96e1 fix: resolve systemic parts[0] indexing bugs
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
2026-08-13 17:46:04 -07:00
tranminhquang 602e58db71 fix: resolve nested provider behind litellm_proxy prefix
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
2026-08-13 17:44:08 -07:00
vaibhav-patel ede87c26ed feat: allow configuring Vertex AI API version
Merge https://github.com/google/adk-python/pull/6183

Fixes #3246

PiperOrigin-RevId: 964386853
2026-08-13 17:39:00 -07:00
Sureel Bhurat 17e38bf1f4 test: verify pre-tool-call text preservation in LiteLLM streaming
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
2026-08-13 17:19:51 -07:00
George Weale 703cf43f6b fix: send media attached to a tool response to non-Gemini models
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 964308319
2026-08-13 14:52:48 -07:00
George Weale f57a67d638 fix: bound Apigee completions HTTP client timeout and redirects
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 963758853
2026-08-12 17:50:06 -07:00
Google Team Member b66cba2809 feat: Allow clients using the load_artifacts_tool to customize how attachment data is fed to the LLM
PiperOrigin-RevId: 963516890
2026-08-12 10:12:39 -07:00
George Weale d63a255880 fix: omit HTTP options from Gemini debug logs
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 962265633
2026-08-10 11:24:16 -07:00
Liang Wu 0a6d05da3b feat(live): forward safety_settings from generate_content_config to the Live API
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
2026-08-07 14:37:40 -07:00
George Weale 03f44c8e10 fix(models): honor an LLM registered after that name was resolved
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 961124213
2026-08-07 14:32:42 -07:00
Google Team Member 745de0ac13 feat: Stop using the obsolete Gemini 1.x / Gemini 2+ model-id check in ADK
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
2026-08-06 20:15:47 -07:00
George Weale 456524d714 test: add unit tests for public symbols that had no coverage
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 960421043
2026-08-06 11:41:06 -07:00
George Weale 42f220a61b fix(models): report a missing Anthropic credential when the client is built
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 960406138
2026-08-06 11:15:31 -07:00
George Weale e300ae7aa6 fix: resolve Claude 5 model names to the Claude LLM class
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 960059516
2026-08-05 22:04:03 -07:00
George Weale 955325ddbb fix(models): use the reported total token count for interactions usage
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 959879570
2026-08-05 14:44:40 -07:00
George Weale a95b008f91 fix(models): report Anthropic thinking tokens without double counting output
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 959846025
2026-08-05 13:43:53 -07:00
George Weale b26d4f67cb fix: make advertised tool names match the names tools are registered under
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 959837402
2026-08-05 13:27:53 -07:00
George Weale e74917e719 fix(litellm): convert http_options.timeout from milliseconds to seconds
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
2026-08-03 17:39:10 -07:00
George Weale 7f82142adb refactor(types): make google.adk.models pass strict mypy
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
2026-08-03 15:57:49 -07:00
George Weale 36fd2c8e0c perf: avoid quadratic streaming accumulation in the LiteLLM adapter
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 957325226
2026-07-31 14:07:42 -07:00
Xuan Yang 2aff82c309 feat: Introduce a capability reporting system for LLM models
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
2026-07-30 15:23:25 -07:00
George Weale 83b71e68a9 chore: remove tracker references from comments and docstrings
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 956763571
2026-07-30 15:20:54 -07:00
mukunda katta cde301ba68 fix(models): wrap Anthropic rate limit errors
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
2026-07-30 13:06:52 -07:00
George Weale d58caa6c78 fix: route apigee, o-series and unlisted LiteLLM provider models
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 956689541
2026-07-30 13:01:00 -07:00
lottielin 0f738a5494 feat: support audio_stream_end for realtime input
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
2026-07-30 11:27:23 -07:00
George Weale 2547db61dd fix: honor Apigee request timeouts
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
2026-07-29 17:19:16 -07:00
George Weale 802a0793f0 fix: populate finish_reason on Anthropic LLM responses
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
2026-07-29 11:14:40 -07:00
Eva 550189ce4f fix: wrap input_schema payload in ReAct prompt and propagate tool_choice to LiteLLM
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
2026-07-28 13:23:15 -07:00
Anas Khan bb6d547738 test: exercise real connect path in test_connect
Merge https://github.com/google/adk-python/pull/6394

PiperOrigin-RevId: 954855829
2026-07-27 15:17:10 -07:00
Sebastien Coutu 716be893d2 fix(models): populate finish_reason on AnthropicLlm responses
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
2026-07-23 13:22:17 -07:00
Jinhyuk Kim 540cfdf737 fix: populate function name in FunctionResponse parts
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
2026-07-22 17:05:02 -07:00
George Weale c328cec946 fix: assemble only the current turn
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
2026-07-22 15:37:43 -07:00
Vishwa Murugan bb56aa56f7 fix(models): capture Anthropic thinking-block signatures during streaming
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
2026-07-22 12:08:02 -07:00
Google Team Member df0268921c fix(models): guard Gemini.client_kwargs against missing-field AttributeError
PiperOrigin-RevId: 951961168
2026-07-22 01:41:06 -07:00
Yong-Shin Jiang c429d7549d fix: collect all tools so native tools aren't dropped
Merge https://github.com/google/adk-python/pull/6416

Closes #6091

PiperOrigin-RevId: 949701684
2026-07-17 12:25:14 -07:00
George Weale 065234e279 fix: preserve non-image file MIME type in LiteLlm content conversion
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
2026-07-16 10:59:58 -07:00
George Weale ecbefd9547 fix: exclude LiteLlm llm_client from JSON serialization
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
2026-07-16 10:10:30 -07:00
Anas Khan 8eb8348287 test: re-enable finish_reason unknown-maps-to-other litellm test
Merge https://github.com/google/adk-python/pull/6303

PiperOrigin-RevId: 948646206
2026-07-15 17:54:21 -07:00
George Weale cba28271b4 perf: skip disabled request-log formatting
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
2026-07-15 12:10:08 -07:00