diff --git a/.github/.release-please-manifest.json b/.github/.release-please-manifest.json index 78baf5bf9bf..8ff2f5ec44f 100644 --- a/.github/.release-please-manifest.json +++ b/.github/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.5.0" + ".": "2.6.1" } diff --git a/.github/release-please-config.json b/.github/release-please-config.json index 373823b6bc1..851ccd24dd2 100644 --- a/.github/release-please-config.json +++ b/.github/release-please-config.json @@ -57,5 +57,5 @@ ] } }, - "last-release-sha": "c9bacd40ee4f8ad9951d543b240ce3f2f59ebb42" + "last-release-sha": "7fd876027049f86a4a580a56c45373c8e2908e24" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 577a7500156..c1013e4409b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,166 @@ # Changelog +## [2.6.1](https://github.com/google/adk-python/compare/v2.6.0...v2.6.1) (2026-07-30) + + +### Features + +* add parent terminal grouping and TTL pruning to ADK CLI telemetry ([99bbd83](https://github.com/google/adk-python/commit/99bbd83ddac5f1ade93cdde1be98a1e3164e1da7)) +* capture TTY connectivity in CLI environment telemetry ([c12a025](https://github.com/google/adk-python/commit/c12a025184cec7859d829d8ce7178ddbe3e2e302)) + + +### Bug Fixes + +* **cli:** implement early telemetry recording for long-running web servers and log successful exit code upon routine teardown ([77726c5](https://github.com/google/adk-python/commit/77726c55b33d6da1688a3d6893fe5e474d79116b)) +* Stop re-validating already-consumed tool confirmations ([2b1204d](https://github.com/google/adk-python/commit/2b1204d0a0dd0d97f1e23f1188ea7366bfcb61e1)) + +## [2.6.0](https://github.com/google/adk-python/compare/v2.5.0...v2.6.0) (2026-07-29) + + +### Features + +* **a2a:** support per-invocation auth headers when fetching agent cards ([3dd1156](https://github.com/google/adk-python/commit/3dd1156c33fe9e4857c467fbd16f209fb0ac5b4b)) +* add --extra_packages option to `adk deploy agent_engine` ([93db97d](https://github.com/google/adk-python/commit/93db97db338fa72cf5b4fef8125552f30e614b57)), closes [#3936](https://github.com/google/adk-python/issues/3936) +* Add ADK evaluation samples: shared home-automation agent ([6ba41ef](https://github.com/google/adk-python/commit/6ba41ef35ea5fb83653d38b10ab67d2042805a61)) +* add agent identity auth manager finalize endpoint for 3 legged OAuth flow with auth manager ([8d2ded3](https://github.com/google/adk-python/commit/8d2ded3bec122845d06eb7b6ad6ab99723ab8160)) +* Add basic_criteria ADK eval sample ([fdebd9d](https://github.com/google/adk-python/commit/fdebd9d5635abd4e7aa607ac42292252128f0db3)) +* Add bigquery-graph skill to ADK BigQuery tools ([f1a0a14](https://github.com/google/adk-python/commit/f1a0a1480bbcd3d6a23fbe75630303c9f8a9720d)) +* add capability to log commands run in CLI ([a58220c](https://github.com/google/adk-python/commit/a58220cd05a671082b913fd4955613f806b2bd49)) +* Add custom_metric ADK eval sample ([f71d9df](https://github.com/google/adk-python/commit/f71d9df9179a4d37a54051ffceb6dda5c821e4c4)) +* Add llm_judge_match ADK eval sample ([0f0fa6b](https://github.com/google/adk-python/commit/0f0fa6bb4a6af4e16f88cd7283daab4451c03714)) +* add metadata extraction symmetry to A2A converters ([3127f36](https://github.com/google/adk-python/commit/3127f368915eb6450dfd551914628ded0e58c610)) +* add opt-in final_response_tool_names to BigQueryAgentAnalyticsPlugin ([07455ee](https://github.com/google/adk-python/commit/07455ee62cbd5c11625aed8c75380b88eba55e6d)) +* Add ReflectAndRetryModelPlugin for self-healing model errors ([322f455](https://github.com/google/adk-python/commit/322f45591c61da6a15f404ba2e7d0fb520f16356)) +* Add rubric_criteria ADK eval sample ([852a66a](https://github.com/google/adk-python/commit/852a66ad62a051beb2284517369fdd1d9e8dd4f0)) +* add state_delta support to LiveRequest for live mode ([8219774](https://github.com/google/adk-python/commit/82197740a603e146ee35e0f18d2761a5c8f155d6)), closes [#4220](https://github.com/google/adk-python/issues/4220) +* add telemetry consent check, status commands, and interrupt safety to CLI ([6bab08f](https://github.com/google/adk-python/commit/6bab08fc803d26853417c4d6e71704b1a72e035e)) +* Add telemetry consent configuration endpoints and local writing utility ([26f3d45](https://github.com/google/adk-python/commit/26f3d454c7c3346d4b0be406b24561940436b4a5)) +* add telemetry metrics collection for ADK CLI execution ([2280f1c](https://github.com/google/adk-python/commit/2280f1cc5b7cc3c6ecf657273d92ea5fb4f5b439)) +* Add test_file_vs_evalset ADK eval sample ([57a34ba](https://github.com/google/adk-python/commit/57a34ba77a1e98587d477fae5828bfcf7c0f7bf9)) +* Add user_simulation ADK eval sample ([481fe21](https://github.com/google/adk-python/commit/481fe21cff0502f3e9c14861bd5f885b7f5969f9)) +* **agents:** add instruction field to ManagedAgent ([720af3d](https://github.com/google/adk-python/commit/720af3d5c94708cb80294f2eafe9734cf92582ab)) +* **agents:** forward ManagedAgent instruction as system_instruction ([49fdc26](https://github.com/google/adk-python/commit/49fdc26e40018a268da43803d555151dafe988b8)) +* **eval:** Make live and audio evals reachable via public entrypoints ([5091f0a](https://github.com/google/adk-python/commit/5091f0a65acb963e0dd3f1db152c2a8d413dc9e4)) +* **eventarc:** add Eventarc Advanced toolset for ADK ([217a90a](https://github.com/google/adk-python/commit/217a90a2e6c9725aeaac3dffedca8d63c25037fd)) +* **eventarc:** add Eventarc Advanced toolset for ADK ([d4f157d](https://github.com/google/adk-python/commit/d4f157d2ed6fad21a6aa4c6e29e6133e3fe5db76)) +* forward the OAuth2 nonce to the authorization request ([79cc1f2](https://github.com/google/adk-python/commit/79cc1f2970faeae7889a0f8b75bb2f5415cbe4eb)), closes [#2067](https://github.com/google/adk-python/issues/2067) +* **integrations:** add OCI Generative AI provider ([625ef1a](https://github.com/google/adk-python/commit/625ef1aa693ebb1980620be39c02d3ddd5154672)), closes [#5069](https://github.com/google/adk-python/issues/5069) +* make agent evaluation compatible with pre-loaded artifacts ([02e32a4](https://github.com/google/adk-python/commit/02e32a4d53e7e477453785818843ba8f085a318b)), closes [#2075](https://github.com/google/adk-python/issues/2075) +* make EventsCompactionConfig sliding-window fields optional ([ec42cfa](https://github.com/google/adk-python/commit/ec42cfa237bc2613f62759962454aed6dea43130)), closes [#6398](https://github.com/google/adk-python/issues/6398) +* Publish companion constraints-3.11.txt and constraints-3.12.txt file for transitive dependency protection (4 day buffer to protect from supply chain attack) ([75c773e](https://github.com/google/adk-python/commit/75c773ed9d2a369e69fb1ce387cf31983bf9450b)) +* refresh expired OAuth2 tokens in OpenAPI credential exchanger ([b3c9783](https://github.com/google/adk-python/commit/b3c9783427b2d6cfad945bdb841396fb0a63c82f)) +* **samples:** add ManagedAgent create-and-use custom-agent sample ([d86ae20](https://github.com/google/adk-python/commit/d86ae20c6a1d6a0ebddcf35f3ebd64eabbb4a56b)) +* support audio generation user simulator ([327afeb](https://github.com/google/adk-python/commit/327afeb427fba2152814744a639d86e6561b2199)) +* support serving a to_a2a agent under a path prefix ([1ae292f](https://github.com/google/adk-python/commit/1ae292fc9627f5991795c2a792f29b59c5559f57)), closes [#4448](https://github.com/google/adk-python/issues/4448) +* support user labels in RunConfig ([13bef9c](https://github.com/google/adk-python/commit/13bef9cf93e4b5a7e97362bc8514c9684cd2eb03)) +* **telemetry:** add request-driven metric export for Agent Engine ([8930d9b](https://github.com/google/adk-python/commit/8930d9b19338873d091215a1e56f99ce6fd2060b)) +* **telemetry:** derive gen_ai error.type from provider status code ([b956d15](https://github.com/google/adk-python/commit/b956d15ce5c58f1237154002f1755c33b4ed31ce)) +* Update agent_registry to handle mTLS endpoints internally ([66cf08d](https://github.com/google/adk-python/commit/66cf08d1876b49168cf5ea67853a319e3d9eee56)) + + +### Bug Fixes + +* **a2a:** honor task cancellation instead of raising NotImplementedError ([fb55d4a](https://github.com/google/adk-python/commit/fb55d4a669e35fd9da69bbb951945d1a19792f9f)) +* **agents:** await cancelled tasks in pre-3.11 ParallelAgent merge ([bc55099](https://github.com/google/adk-python/commit/bc550991b97e586c4bcca86facf3ad9dba049940)), closes [#5297](https://github.com/google/adk-python/issues/5297) +* **agents:** skip output_key processing on intermediate conversational text in task-mode LlmAgent ([54344ed](https://github.com/google/adk-python/commit/54344edfb95febc31ba2b80f5578205208f1efa1)) +* allow http_options.extra_body in generate_content_config ([4c6f22e](https://github.com/google/adk-python/commit/4c6f22e8e6daa8d55cadf3999f010bcb4303f8bf)) +* allow invocation-level rubrics ([67ab27f](https://github.com/google/adk-python/commit/67ab27f2547db48f7248b1689aab4c18502aee17)) +* **artifacts:** namespace file artifacts by app ([f72f0db](https://github.com/google/adk-python/commit/f72f0db58c5c1f4e8b8d4f04b8c592c9e49cbe9e)) +* assemble only the current turn ([c328cec](https://github.com/google/adk-python/commit/c328cec9462ac6a5459f19e6fb548c5b2e40cd39)) +* bound integration HTTP waits ([bf4143a](https://github.com/google/adk-python/commit/bf4143ac2269d3a23c4abc680c3a39f727db21bd)) +* bypass Protobuf Gencode/Runtime version check in antigravity integration ([91038f2](https://github.com/google/adk-python/commit/91038f2ba4b48b8916f58ab56a695b516222044c)) +* **caching:** prevent prompt cache invalidation when using dynamic tools ([509be09](https://github.com/google/adk-python/commit/509be09d342decab5abca29345aec3a187956c41)), closes [#3227](https://github.com/google/adk-python/issues/3227) +* Call mtls.should_use_mtls_endpoint with client_cert_available argument ([b315b00](https://github.com/google/adk-python/commit/b315b0024a903b91f7c8fa971eb4621133c47845)) +* canonicalize context cache fingerprint for stable hashing ([425dda1](https://github.com/google/adk-python/commit/425dda190457bffb159a1373e30ea396eba5f536)) +* **ci:** mark imported PRs as merged even if already closed ([2eca8b1](https://github.com/google/adk-python/commit/2eca8b11ceb890614370c64a2ad56a0164059c97)) +* **cli:** treat agent folder with subfolders as single agent in adk web ([40cf97b](https://github.com/google/adk-python/commit/40cf97bf2ce11b581e7b0409af299910b52773ea)) +* close A2A response stream when the caller stops consuming ([31392ba](https://github.com/google/adk-python/commit/31392bad27b911d78a3de124f03a372227777878)) +* close AsyncDaytona client in DaytonaEnvironment.close ([ecf6d13](https://github.com/google/adk-python/commit/ecf6d13f64d6df4b0860c1b32643d12cc1c0d381)) +* collect all tools so native tools aren't dropped ([c429d75](https://github.com/google/adk-python/commit/c429d7549d3780227630bac764c42f1627b981ae)), closes [#6091](https://github.com/google/adk-python/issues/6091) +* derive an SDK-conforming Antigravity conversation id ([64b758a](https://github.com/google/adk-python/commit/64b758ae8e2e325e830a2a112c18f3c1cc87635c)) +* do not mount a cluster credential into the GKE code sandbox ([8207880](https://github.com/google/adk-python/commit/8207880101292bdf1037cb3bfdf90d3a3691ce0c)) +* emit only new artifact parts on streaming artifact updates ([088be86](https://github.com/google/adk-python/commit/088be86040bd486cf42eae561e77b3b0ad979688)), closes [#6343](https://github.com/google/adk-python/issues/6343) +* emit resumability checkpoints from workflow graph nodes ([cecc1f9](https://github.com/google/adk-python/commit/cecc1f98d5837de3157a606e7a6409fece5deefc)) +* **eval:** support `get_agent_async` in `adk eval` ([ebaef9f](https://github.com/google/adk-python/commit/ebaef9f63291cb77b3ea54918cbb8a5b7926862a)) +* **evaluation:** support non-English responses in ROUGE-1 matching ([8200fae](https://github.com/google/adk-python/commit/8200faec6d42c10e5c3cf5f9613bcb6bd5d51240)) +* guard multimodal tool results plugin against empty contents ([5a248de](https://github.com/google/adk-python/commit/5a248de58f80c4b6574e5d0bd10838964f478218)) +* handle Windows paths in `adk eval` ([6f6106f](https://github.com/google/adk-python/commit/6f6106f672d26596f909bd555e954825ba4044b4)), closes [#6415](https://github.com/google/adk-python/issues/6415) +* harden A2A metadata serialization and parser type validation ([eee700a](https://github.com/google/adk-python/commit/eee700a01696cabb42583611766806de2d8574be)) +* include credential failure details ([13c7272](https://github.com/google/adk-python/commit/13c727291098947d8f44c532cd9b76a588a016cd)) +* isolate delegated task branches ([95feafa](https://github.com/google/adk-python/commit/95feafa3b23dbcce19c287cf749a2076fb9c5db9)), closes [#6457](https://github.com/google/adk-python/issues/6457) +* let httpx set the multipart boundary Content-Type ([3367b5b](https://github.com/google/adk-python/commit/3367b5bf72e2117c7e766c27ef4e35a33172affb)) +* make InvocationEvent.content optional so the Web UI can save eval cases ([b549ab4](https://github.com/google/adk-python/commit/b549ab41c906a1746e316788c2bd9366efb1cc0d)), closes [#6336](https://github.com/google/adk-python/issues/6336) +* make ParallelWorker concurrent failure exceptions deterministic ([d31b5e7](https://github.com/google/adk-python/commit/d31b5e7dcec3b1c8da8c35ad9a1d14d046f56cc3)) +* match rubric verdicts by echoed id so paraphrased rubrics are not dropped ([0b7355b](https://github.com/google/adk-python/commit/0b7355baa385270cf0be59e601dc7c1e898980c4)), closes [#6171](https://github.com/google/adk-python/issues/6171) +* **memory:** honor Vertex RAG top-k configuration ([d4d2f6e](https://github.com/google/adk-python/commit/d4d2f6e600590e80b4880313dbeb7ea5bacafdc1)) +* **memory:** make Vertex RAG uploads async-safe ([80a05b7](https://github.com/google/adk-python/commit/80a05b7f639216c8bf60a870598f0d44efcba339)) +* migrate v0 event timestamps as local time, not UTC ([6f1efe7](https://github.com/google/adk-python/commit/6f1efe7b2a70cef00ef89de6ea3690f0914b692f)) +* **models:** capture Anthropic thinking-block signatures during streaming ([bb56aa5](https://github.com/google/adk-python/commit/bb56aa56f79da843d50e0c01637f97e7f26435a2)) +* **models:** guard Gemini.client_kwargs against missing-field AttributeError ([df02689](https://github.com/google/adk-python/commit/df0268921cb8662fd4984e6ca40577b0953a083a)) +* **models:** populate finish_reason on AnthropicLlm responses ([716be89](https://github.com/google/adk-python/commit/716be893d201c25859c44cc0cab5b060c2977021)) +* narrow broad except in agent identity credential providers ([0598c9b](https://github.com/google/adk-python/commit/0598c9ba26e543b0a090b0e869eac3badea81c1e)) +* percent-encode context id fields so ids with separators round-trip ([fab5347](https://github.com/google/adk-python/commit/fab5347f56413d8b28b0d023fc56274e9bf9701c)) +* pin third-party github actions to commit shas ([4e16855](https://github.com/google/adk-python/commit/4e16855e0d6038d0e0011e3c1855deff314e5fef)) +* **plugins:** complete BigQuery Agent Analytics privacy and shutdown hardening ([9adf011](https://github.com/google/adk-python/commit/9adf0113ea7adb14044f010cbe1366f6eab53565)) +* **plugins:** correct lazy import path for ReflectAndRetryModelPlugin ([c59fd0e](https://github.com/google/adk-python/commit/c59fd0ed4f603305f3401ca0b9068b8274748925)) +* **plugins:** harden BigQuery agent analytics against fail-open privacy, GCS concurrency, and startup-loss gaps ([2919bf5](https://github.com/google/adk-python/commit/2919bf5b8d426f9a74bd5bd4e4a8a7927b3170bd)) +* **plugins:** restore failure counter properties on retry tool plugin ([66a7233](https://github.com/google/adk-python/commit/66a72337b3c8b366636cfbf09655569edc4122c0)) +* populate finish_reason on Anthropic LLM responses ([802a079](https://github.com/google/adk-python/commit/802a0793f0b1233d4b02c8037db3ebe927ab8b66)) +* populate function name in FunctionResponse parts ([540cfdf](https://github.com/google/adk-python/commit/540cfdf737fd878f87b57b00894fbfc58b97feaa)) +* present client certificate and use mTLS endpoint for API Hub calls ([2cf5433](https://github.com/google/adk-python/commit/2cf543322b397c865701b432ed6fd9867db187b0)) +* preserve ADK behavior on Windows ([6e9895c](https://github.com/google/adk-python/commit/6e9895c55cdc5b3b39a336c6c999bc1731273cdc)) +* preserve explicit false and zero OpenAPI query parameters ([b3abcb2](https://github.com/google/adk-python/commit/b3abcb2b28ccda194fd0499c9f32a6a783b6fde4)), closes [#6287](https://github.com/google/adk-python/issues/6287) +* preserve metadata in A2A artifact updates ([2316b83](https://github.com/google/adk-python/commit/2316b83468b6fa99405d8ee5c2fba4174846c466)) +* Prevent `adk deploy` from uploading `.adk/session.db` file ([dab351b](https://github.com/google/adk-python/commit/dab351beca00ef4e735f5bca0b72dd10be5bf69f)) +* Prevent continuation forgery in tool confirmation ([4b002c4](https://github.com/google/adk-python/commit/4b002c4b56e0b5fa83a0d989ef7663fbedf23211)) +* prevent transfer_to_agent loop on resumable invocation replay ([19df9b9](https://github.com/google/adk-python/commit/19df9b9b9b9153a2e1fcf3c96bb15f86a22835d7)) +* propagate custom metadata from RunConfig to InvocationContext ([79ba5af](https://github.com/google/adk-python/commit/79ba5aff17cf1203b6d4b0948e493802847dfae8)) +* raise SessionNotFoundError when appending to a missing session ([0a70337](https://github.com/google/adk-python/commit/0a70337f29fd76dba18da6b82e072621d60f9117)) +* register custom metrics from eval config in LocalEvalSampler ([79a879f](https://github.com/google/adk-python/commit/79a879f5833a223d3d3f0e78abc2fc6c5da8c1fa)), closes [#6177](https://github.com/google/adk-python/issues/6177) +* reject base_url and extra_body in generate_content_config ([472e463](https://github.com/google/adk-python/commit/472e4635fb4014f7ed2c77db7c2b97f17bbd45bf)) +* reject incomplete evaluation inputs ([8ca9128](https://github.com/google/adk-python/commit/8ca9128a6397389c171625fb6ac0475c516ccfae)) +* require patched async LangGraph runtime ([551372b](https://github.com/google/adk-python/commit/551372bcd88b21f38c2b68b4a9b9738484a9d0fd)) +* resolve tool confirmation resumption failure in production ([c427020](https://github.com/google/adk-python/commit/c4270203c657d4abb14188b90ed692465f1f36c9)) +* respect A2A Message.role in inbound event conversion ([968845f](https://github.com/google/adk-python/commit/968845fd5c9a8c60535f0d32cf14ea4db9c56c5e)), closes [#5186](https://github.com/google/adk-python/issues/5186) +* return empty list from _get_required_fields when no properties ([1890557](https://github.com/google/adk-python/commit/1890557584f58ec5094344a501e5041ef041694d)), closes [#5920](https://github.com/google/adk-python/issues/5920) +* scope replay sequence to the current invocation ([455853b](https://github.com/google/adk-python/commit/455853b5bca2dad68923a9100f9ba945845ad6d0)), closes [#6497](https://github.com/google/adk-python/issues/6497) +* scope the tool thread pool to its event loop ([a1792a7](https://github.com/google/adk-python/commit/a1792a712ae6b90dee4fecdee79cf0ddff1b5609)) +* send the bare input_schema payload from AgentTool again ([ba17367](https://github.com/google/adk-python/commit/ba1736783baee0c5340856cf0640d4f85f4a6026)) +* serialize eval criteria as their concrete subclass ([623da49](https://github.com/google/adk-python/commit/623da4930a43cbaa5386a096c5a54266bae02522)) +* serialize raw bytes as base64 in A2A converters ([e6604e1](https://github.com/google/adk-python/commit/e6604e1d2109261bb548975b0897b76dd3fcc8b2)) +* **sessions:** apply after_timestamp and num_recent_events together in VertexAiSessionService ([021f6f6](https://github.com/google/adk-python/commit/021f6f6c1e6b55f71e2144ec5857820cd010b6e4)) +* share one event id across the partial chunks of a streaming response ([8f98bcd](https://github.com/google/adk-python/commit/8f98bcd513df19aa67b0ac6ba2607f2bb1cb4695)), closes [#1006](https://github.com/google/adk-python/issues/1006) +* single-flight Discovery Engine mode detection ([3a9a88c](https://github.com/google/adk-python/commit/3a9a88c975117a8cfde1ef9d920a978eb4ffe701)), closes [#6101](https://github.com/google/adk-python/issues/6101) +* stop automatic function calling from failing on Vertex AI return types ([268815d](https://github.com/google/adk-python/commit/268815df6427b7cb0dc08f18dc8c51ab96ce0c1b)), closes [#3543](https://github.com/google/adk-python/issues/3543) +* stop tracing credentials passed via config.http_options ([761f1ac](https://github.com/google/adk-python/commit/761f1ac75d0479e21d9f3386189d0b54990d05b4)) +* strip markdown code fences before validating output_schema JSON ([28c649a](https://github.com/google/adk-python/commit/28c649a466c660635ab1ed8de163b5d294c3be00)) +* support nested agent paths in dot_adk_folder resolution ([d33ca5f](https://github.com/google/adk-python/commit/d33ca5fa7cd92d330d48e9e4b0ea1c3d280c557b)) +* surface sub-agent RPC errors from AgentTool ([e737f22](https://github.com/google/adk-python/commit/e737f22957b671000c60a690ebfaee68d3ca6f34)) +* tolerate a malformed traceparent header in the span processor ([de2e66e](https://github.com/google/adk-python/commit/de2e66e9836512aabe0431ee0c1c8169e0fc271a)) +* **tools:** bind JSONDecodeError in ToolConnectionAnalyzer.analyze ([a60d5b9](https://github.com/google/adk-python/commit/a60d5b95227bb63198e247acee880ea8b4f4cbd1)) +* treat GitHub content as untrusted in the adk_team sample agents ([f4979b5](https://github.com/google/adk-python/commit/f4979b5c78be9d709a29a7aa4175b31864fba7e6)) +* wait for in-flight BigQuery writes ([6e43800](https://github.com/google/adk-python/commit/6e43800fcb9263c9debdb570dab837bce6f51f31)) +* **workflow:** fix task agent resumption in nested workflows ([fd006db](https://github.com/google/adk-python/commit/fd006db9153fe6c51f281f36e35b40d243e5fca0)) +* wrap input_schema payload in ReAct prompt and propagate tool_choice to LiteLLM ([550189c](https://github.com/google/adk-python/commit/550189ce4f3cc1e69a108b1f38ba6c31f2f40e65)) + + +### Performance Improvements + +* avoid quadratic text/audio accumulation in streaming ([7fd8760](https://github.com/google/adk-python/commit/7fd876027049f86a4a580a56c45373c8e2908e24)) +* cache the FunctionTool declaration across LLM calls ([57f3af2](https://github.com/google/adk-python/commit/57f3af24a00de46096089bd3279791a6a98b5c48)) +* **import:** lazy-load heavy dependencies ([88b388e](https://github.com/google/adk-python/commit/88b388ecde41950d9bd9ac886df23a17a366ca2a)) +* **tools:** avoid double Schema serialization in Optional/Union dedup ([6264576](https://github.com/google/adk-python/commit/6264576784dd2e265b2092c759989db60162e34e)) + + +### Documentation + +* Add developer unit guide for ReflectAndRetryToolPlugin ([94832a5](https://github.com/google/adk-python/commit/94832a515161b97689a940e95f9f1119b3dd637c)) +* **agents:** add system-instruction sample and guide section ([597fac3](https://github.com/google/adk-python/commit/597fac3a4fa370a41df377cef023e571edac30c3)) +* document creating and using a custom managed agent ([52c3e9e](https://github.com/google/adk-python/commit/52c3e9eb60f9f087f01e97de52cddcd031f4b93a)) +* document workflow resumability model and direction ([d4804e2](https://github.com/google/adk-python/commit/d4804e20843f831c19a1f02afc4d2796138c36f4)) +* explain how an accepted pull request lands ([b6c2575](https://github.com/google/adk-python/commit/b6c257572bedfbe6e48902b351b23b48f6fb1779)) +* fix broken relative links in documentation ([096ecfc](https://github.com/google/adk-python/commit/096ecfcf56ad47a9a63da1d76a062f56d7586692)) + ## [2.5.0](https://github.com/google/adk-python/compare/v2.4.0...v2.5.0) (2026-07-16) diff --git a/src/google/adk/cli/_telemetry/_constants.py b/src/google/adk/cli/_telemetry/_constants.py index 806c741e344..177735a01b4 100644 --- a/src/google/adk/cli/_telemetry/_constants.py +++ b/src/google/adk/cli/_telemetry/_constants.py @@ -22,3 +22,5 @@ LOCK_FILE = os.path.expanduser("~/.adk/clearcut_lock") # Local JSONL file where command metric logs are queued before flushing. QUEUE_FILE = os.path.expanduser("~/.adk/telemetry_queue.jsonl") +# Local directory mapping terminal parent PIDs to their active sessions. +TELEMETRY_SESSIONS_DIR = os.path.expanduser("~/.adk/telemetry_sessions") diff --git a/src/google/adk/cli/_telemetry/_metrics_collector.py b/src/google/adk/cli/_telemetry/_metrics_collector.py index 3079806f013..0b1f19fa071 100644 --- a/src/google/adk/cli/_telemetry/_metrics_collector.py +++ b/src/google/adk/cli/_telemetry/_metrics_collector.py @@ -31,9 +31,11 @@ import click from google.adk.cli._telemetry import _constants -from google.adk.utils import _telemetry_config import google.adk.version +# 1 hour of quiet time marks the end of a logical work session +_SESSION_INACTIVITY_TIMEOUT_SECONDS = 3600 + # Constants protecting telemetry storage and preventing client/endpoint abuse. # Prevents haywire scripts or malicious inputs from flooding log files # or sending excessively bloated payloads to the public Clearcut server. @@ -45,20 +47,87 @@ logger = logging.getLogger("google_adk." + __name__) -class MetricsCollector: - """Singleton for collecting and reporting ADK CLI telemetry.""" +def _prune_expired_sessions() -> None: + """Prunes session files modified more than 1 hour ago.""" + if not os.path.exists(_constants.TELEMETRY_SESSIONS_DIR): + return + current_time = time.time() + try: + for filename in os.listdir(_constants.TELEMETRY_SESSIONS_DIR): + file_path = os.path.join(_constants.TELEMETRY_SESSIONS_DIR, filename) + try: + if filename.endswith(".json"): + try: + with open(file_path, "r", encoding="utf-8") as f: + info = json.load(f) + last_activity = info.get("last_activity", 0) + except (OSError, json.JSONDecodeError, KeyError, TypeError): + # If the JSON file is unreadable/corrupted, prune it. + last_activity = 0 + if ( + current_time - last_activity + ) > _SESSION_INACTIVITY_TIMEOUT_SECONDS: + os.remove(file_path) + elif filename.endswith(".tmp"): + os.remove(file_path) + except OSError: + pass + except OSError: + pass - _instance = None - _lock = threading.Lock() - @classmethod - def get_collector(cls) -> Optional["MetricsCollector"]: - if _telemetry_config.read_telemetry_consent() is not True: - return None - with cls._lock: - if not cls._instance: - cls._instance = cls() - return cls._instance +def _load_session_state() -> tuple[str, int]: + """Retrieves the session state, resetting it if idle for over an hour.""" + session_file = os.path.join( + _constants.TELEMETRY_SESSIONS_DIR, f"{os.getppid()}.json" + ) + try: + with open(session_file, "r", encoding="utf-8") as f: + info = json.load(f) + if isinstance(info, dict): + last_activity = info.get("last_activity", 0) + if (time.time() - last_activity) < _SESSION_INACTIVITY_TIMEOUT_SECONDS: + session_id = info.get("session_id") + if not session_id: + return str(uuid.uuid4()), 0 + return session_id, info.get("sequence_number", 0) + except (OSError, json.JSONDecodeError, KeyError, TypeError): + pass + return str(uuid.uuid4()), 0 + + +def _write_session_state(session_id: str, sequence_number: int) -> None: + """Saves the current session metadata back to local disk storage.""" + _prune_expired_sessions() + session_file = os.path.join( + _constants.TELEMETRY_SESSIONS_DIR, f"{os.getppid()}.json" + ) + temp_file = None + try: + info = { + "session_id": session_id, + "sequence_number": sequence_number, + "last_activity": time.time(), + } + + temp_file = f"{session_file}.{os.getpid()}.tmp" + os.makedirs(os.path.dirname(session_file), exist_ok=True) + with open(temp_file, "w", encoding="utf-8") as f: + json.dump(info, f) + os.replace(temp_file, session_file) + temp_file = None + except OSError: + pass + finally: + if temp_file is not None: + try: + os.remove(temp_file) + except OSError: + pass + + +class MetricsCollector: + """Collector for capturing and queueing ADK CLI telemetry.""" @staticmethod def _is_rate_limited() -> bool: @@ -75,26 +144,29 @@ def _is_rate_limited() -> bool: return True def __init__(self) -> None: - # Unique UUID per CLI run to group all events in this session. - self._session_id = str(uuid.uuid4()) - # Monotonically increasing counter to order events within this session. - self._sequence_number = 0 + self._lock = threading.Lock() + # Load session metadata matching parent terminal process + # We generate an ephemeral session ID and sequence number to group commands + # executed in the same terminal session for insightful metrics analytics. + self._session_id, self._sequence_number = _load_session_state() self._environment = { "os_type": platform.system().lower(), "language": "python", "language_version": platform.python_version(), "adk_version": google.adk.version.__version__, + "is_tty": sys.stdout.isatty() if sys.stdout else False, } logger.debug( - "Initialized ADK metrics collector with session %s", + "Initialized ADK metrics collector with session %s (seq %d)", self._session_id, + self._sequence_number, ) atexit.register(self.shutdown) @staticmethod def _gather_flags_from_click() -> Optional[List[str]]: - """Gathers used flags and argument names from Click context.""" + """Gathers used flags and positional argument names from Click context.""" ctx = click.get_current_context(silent=True) if not ctx: return None @@ -125,9 +197,11 @@ def record_command_run( duration_ms: int = 0, exception_type: str = "", ) -> None: - """Records a CLI command execution and appends to local disk queue.""" - self._sequence_number += 1 - + """Records a command execution and safely appends to local disk queue.""" + with self._lock: + self._sequence_number += 1 + # Save the updated sequence number back to the master file + _write_session_state(self._session_id, self._sequence_number) # Enforce string length constraints command = command[:_MAX_STRING_LENGTH] if command else "" subcommand = subcommand[:_MAX_STRING_LENGTH] if subcommand else "" diff --git a/src/google/adk/cli/_telemetry/_metrics_reporter.py b/src/google/adk/cli/_telemetry/_metrics_reporter.py index 5b11ef81362..6d8d1f2fddb 100644 --- a/src/google/adk/cli/_telemetry/_metrics_reporter.py +++ b/src/google/adk/cli/_telemetry/_metrics_reporter.py @@ -24,15 +24,16 @@ import urllib.request import uuid +try: + from google.adk.cli._telemetry import _constants +except ImportError: + import _constants # type: ignore[no-redef] + # Exponential backoff retry intervals (in seconds) for connection retries. _RETRY_BACKOFF_WAIT_TIMES = (1, 2) # Max network connection and read timeout (in seconds) for HTTP requests. _TIMEOUT_IN_SEC = 5 -try: - from google.adk.cli._telemetry import _constants -except ImportError: - import _constants # type: ignore[no-redef] # Clearcut registration details for Google ADK logs. _LOG_SOURCE_INT = 3007 diff --git a/src/google/adk/cli/browser/assets/config/runtime-config.json b/src/google/adk/cli/browser/assets/config/runtime-config.json index 873e88b1f1d..c8f49d882d5 100644 --- a/src/google/adk/cli/browser/assets/config/runtime-config.json +++ b/src/google/adk/cli/browser/assets/config/runtime-config.json @@ -1,4 +1,3 @@ { - "backendUrl": "", - "telemetry": null -} + "backendUrl": "" +} \ No newline at end of file diff --git a/src/google/adk/cli/browser/index.html b/src/google/adk/cli/browser/index.html index afd832ac864..95c5c17d852 100644 --- a/src/google/adk/cli/browser/index.html +++ b/src/google/adk/cli/browser/index.html @@ -23,12 +23,12 @@ - - + + - - + +