Compare commits

..

96 Commits

Author SHA1 Message Date
Liang Wu c6cf11cb74 chore:Rename conformance create command to record
PiperOrigin-RevId: 822708044
2025-10-22 13:03:37 -07:00
Yifan Wang 2724819622 chore: update adk web, fixing cursor color, and thought bubble
PiperOrigin-RevId: 822693836
2025-10-22 12:27:28 -07:00
Alexey Guseynov 2f4f5611bd fix:Remove unnecessary Aclosing
PiperOrigin-RevId: 822496695
2025-10-22 02:40:07 -07:00
Wei Sun (Jack) 4df926388b fix: Returns dict as result from McpTool
The `BaseTool` expects the run_async to return a json-serializable object. By model_dump the McpTool result explicitly can allow what ADK runtime sees is identical to what is persisted in the session event list.

Before the change, runtime sees CallToolResult instance and Session persists its serialized dict.

https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/types.py#L916-L922

PiperOrigin-RevId: 822465432
2025-10-22 00:58:04 -07:00
Wei Sun (Jack) d4dc645478 chore: Fixes MCPToolset --> McpToolset in various places
PiperOrigin-RevId: 822377517
2025-10-21 19:42:37 -07:00
Wei Sun (Jack) 7d5c6b9acf fix: Fixes the identity prompt to be one line and add ending period after description statement
From

```
You are an agent. Your internal name is "agent".

 The description about you is "test description"
```

to

```
You are an agent. Your internal name is "agent". The description about you is "test description".
```

PiperOrigin-RevId: 822358196
2025-10-21 18:31:48 -07:00
Yifan Wang aab2504ebd chore: update adk web
PiperOrigin-RevId: 822341297
2025-10-21 17:31:20 -07:00
Shangjie Chen 391628fcdc feat: Add a service registry to provide a generic way to register custom service implementations to be used in FastAPI server
To register a custom service:
- Create a factory function that takes a URI and returns an instance of your custom service. This function will parse any details it needs from the URI.
- Register your factory with the global service registry. You need to define a unique URI scheme for your service (e.g., custom).

PiperOrigin-RevId: 822310466
2025-10-21 15:58:51 -07:00
Luis Pabon 409df1378f feat: Granular Per Agent Speech Configuration
Merge https://github.com/google/adk-python/pull/3170

Addresses Feature Request: #3116

This PR adds a `speech_config` to the **LLM Agent configuration** for the **live use case**. When an **asynchronous LLM** call is made to the **Gemini Live API**, it prioritizes the most specific agent configuration's speech_config. If that is null, it then uses the run configuration's speech_config. Unit tests have been added to verify this behavior.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3170 from qyuo:bidi_agent_speech_config af1bd277d4f95c4a7d9aa0b16828ba3de826ce08
PiperOrigin-RevId: 822305427
2025-10-21 15:44:00 -07:00
Shangjie Chen 2a901d12f4 chore: Raise AlreadyExistsError when trying to create a resource with same ID
Move the dedupe logic into session service so that the internal error can be surfaced to client

PiperOrigin-RevId: 822294430
2025-10-21 15:11:33 -07:00
Xuan Yang c850da3a07 fix: Fix the broken langchain importing caused their 1.0.0 release
PiperOrigin-RevId: 822279014
2025-10-21 14:30:34 -07:00
Parham MohammadAlizadeh ed37e343f0 feat(tools): support additional headers for google api toolset #non-breaking
Merge https://github.com/google/adk-python/pull/3194

Allow Google API toolsets to accept optional per-request headers
#3105

## Testing Plan

### Unit Tests
- âś… Added `test_init_with_additional_headers` in `test_google_api_tool.py` to verify headers are passed to RestApiTool
- âś… Added `test_prepare_request_params_merges_default_headers` in `test_rest_api_tool.py` to verify custom headers are merged into requests
- âś… Added `test_prepare_request_params_preserves_existing_headers` in `test_rest_api_tool.py` to verify critical headers (Content-Type, User-Agent) are not overridden by additional_headers
- âś… Updated `test_init` and `test_get_tools` in `test_google_api_toolset.py` to verify the parameter is properly stored and passed through

### Manual Testing
Tested with Google Ads API scenario (the original use case from issue #3105):

```python
import os
from google.adk.tools.google_api_tool import GoogleApiToolset

# Create toolset with developer-token header required by Google Ads API
google_ads_toolset = GoogleApiToolset(
    client_id=os.environ["CLIENT_ID"],
    client_secret=os.environ["CLIENT_SECRET"],
    api_name="googleads",
    api_version="v21",
    additional_headers={"developer-token": os.environ["GOOGLE_ADS_DEV_TOKEN"]}
)

# Verify headers are included in API requests
tools = await google_ads_toolset.get_tools()
# Successfully made requests with the developer-token header
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3194 from Prhmma:feature/google-api-toolset-additional-headers-3105 e10489e82bfde5cf2bfd3f1bced3e1f5cea1f8b2
PiperOrigin-RevId: 822273582
2025-10-21 14:16:37 -07:00
Kathy Wu ce3418a69d fix: Fix BuiltInCodeExecutor so that it can support visualizations
Previously BuiltInCodeExecutor was missing the logic to save output files from executed code as artifacts, so images/visualizations wouldn't show up in the UI. This fix will iterate through all parts of the LlmResponse, and if any of them are images, it will save the image data using artifact_service (similar to what is done in VertexAICodeExecutor).

This fixes the backend, but there are still UI bugs that should be fixed -- events without content are currently ignored, so the image doesn't appear even though it is saved. We will add the UI fix in a separate change.

PiperOrigin-RevId: 822245140
2025-10-21 13:06:43 -07:00
George Weale fe1fc75c15 chore: Improve hint message in agent loader
PiperOrigin-RevId: 822216833
2025-10-21 11:54:32 -07:00
George Weale dc4975dea9 fix: relax runner app-name enforcement
- let _enforce_app_name_alignment warn instead of raising while caching the hint that now augments the existing “Session not found …” error
- tighten _infer_agent_origin so it ignores hidden folders (like .venv)
- make AgentTool reuse the parent runner’s app_name, stopping internal runners from conflicting in multi-agent setups

PiperOrigin-RevId: 822205860
2025-10-21 11:30:21 -07:00
Google Team Member aeaec859bf feat: Adds Static User Simulator and User Simulator Provider
Details:
- Adds the `StaticUserSimulator` which implements the current functionality of supplying a fixed set of user prompts for an EvalCase.
- Adds the `UserSimulatorProvider` which determines the type of user simulator required for an EvalCase (StaticUserSimulator or LlmBackedUserSimulator).
- Integrates the UserSimulatorProvider and UserSimulator into the CLI and evaluation infrastructure.
- Updates and adds unit tests for the new functionality.
- Miscellaneous updates to lay groundwork for a full implementation of the LlmBackedUserSimulator in the future.
PiperOrigin-RevId: 822198401
2025-10-21 11:15:11 -07:00
Jaroslav Pantsjoha 4a842c5a13 fix(cli): Improve error message when adk web is run in wrong directory
Merge https://github.com/google/adk-python/pull/3196

## Summary
Enhances the `AgentLoader` error message to provide clear guidance when users run `adk web` from incorrect directories.

## Motivation
During internal workshops, multiple teams encountered confusion when starting `adk web` from the wrong directory. This often happened when:
- Running `adk web my_agent/` instead of `adk web .`
- Being inside an agent directory when executing the command
- Configuring incorrect start paths during development

## Changes
- **Smart detection**: Checks if `agent.py` or `root_agent.yaml` exists in the current directory
- **Visual diagram**: Shows expected directory structure with actual agent name
- **Explicit command**: Includes `adk web <agents_dir>` usage example
- **Conditional hint**: Provides targeted guidance when user is detected to be inside an agent directory

## Example Error Message

### Before
```
ValueError: No root_agent found for 'my_agent'. Searched in 'my_agent.agent.root_agent', 'my_agent.root_agent' and 'my_agent/root_agent.yaml'. Ensure 'path/my_agent' is structured correctly, an .env file can be loaded if present, and a root_agent is exposed.
```

### After
```
ValueError: No root_agent found for 'my_agent'. Searched in 'my_agent.agent.root_agent', 'my_agent.root_agent' and 'my_agent/root_agent.yaml'.

Expected directory structure:
  <agents_dir>/
    my_agent/
      agent.py (with root_agent) OR
      root_agent.yaml

Then run: adk web <agents_dir>

Ensure 'path/my_agent' is structured correctly, an .env file can be loaded if present, and a root_agent is exposed.

HINT: It looks like you might be running 'adk web' from inside an agent directory. Try running 'adk web .' from the parent directory that contains your agent folder, not from within the agent folder itself.
```

## Testing
- âś… Existing unit tests pass (17/22, with 5 pre-existing failures unrelated to this change)
- âś… `test_agent_not_found_error` passes, confirming error message enhancement works correctly
- âś… Code follows ADK contribution guidelines

## Type
- [x] Bug fix (improved error messaging)
- [ ] Feature
- [ ] Breaking change
- [ ] Documentation

## Related
Fixes #3195

---

**Tags**: #non-breaking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3196 from jpantsjoha:fix/improve-adk-web-error-message a73b190f5b021dbe0afa8426172696ee9eeae8da
PiperOrigin-RevId: 822186700
2025-10-21 10:47:30 -07:00
Wei Sun (Jack) 0bdba30263 docs: format README.md for samples
PiperOrigin-RevId: 822180731
2025-10-21 10:35:43 -07:00
Shangjie Chen 6a5eac0bdc feat: Allow passing extra kwargs to create_session of VertexAiSessionService
This can be used to set ttl and other configs.

PiperOrigin-RevId: 821782343
2025-10-20 13:34:07 -07:00
ejfn 0b73a6937b fix: Handle App objects in eval and graph endpoints
Merge https://github.com/google/adk-python/pull/3060

## Description

Fixes #3059

This PR fixes two endpoints in `adk web` that fail when using App objects instead of bare agents.

## Changes

- **Eval execution endpoint** (line ~969): Extract root_agent from App objects before passing to LocalEvalService
- **Graph visualization endpoint** (line ~1308): Extract root_agent from App objects before graph operations

Both endpoints now properly handle both BaseAgent and App objects by checking the type and extracting `.root_agent` when needed.

## Testing Plan

### Manual E2E Testing with ADK Web

Tested with an App object that includes context caching:

```python
from google.adk.apps import App
from google.adk.agents import LlmAgent

root_agent = LlmAgent(name="MyAgent", model="gemini-1.5-pro-002")
app = App(
    name="my_agent",
    root_agent=root_agent,
    context_cache_config=ContextCacheConfig(...)
)
```

**Before fix:**
- Graph visualization failed (tried to call agent methods on App object)
- Eval execution failed (LocalEvalService received App instead of agent)

**After fix:**
- Graph visualization works correctly
- Eval execution works correctly
- Both endpoints properly extract root_agent from App objects

## Checklist

- [x] Code follows project style (autoformat.sh passed)
- [x] Changes are focused and minimal
- [x] Issue #3059 created and referenced
- [x] Manual E2E testing completed

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3060 from ejfn:ejfn/bugfix-app-object-endpoints 01c30191bfd9487a8c8463ccf24b297cb9a4ce37
PiperOrigin-RevId: 821746910
2025-10-20 12:11:26 -07:00
Google Team Member ee39a89110 feat: introduces a new AgentEngineSandboxCodeExecutor class that supports executes agent generated code
The AgentEngineSandboxCodeExecutor uses the Vertex AI Code Execution Sandbox API to execute code

PiperOrigin-RevId: 821699641
2025-10-20 10:14:34 -07:00
Shangjie Chen d327538724 chore: Throw 409 already exist error when trying to create a session with existing id
Resolves https://github.com/google/adk-python/issues/2950

PiperOrigin-RevId: 821362008
2025-10-19 11:21:36 -07:00
Google Team Member a5b742b360 feat: introduces a new AgentEngineSandboxCodeExecutor class that supports executes agent generated code
The AgentEngineSandboxCodeExecutor uses the Vertex AI Code Execution Sandbox API to execute code

PiperOrigin-RevId: 821197794
2025-10-18 20:24:04 -07:00
Wei Sun (Jack) af74eba695 test: Skips test_langchain_tool.py temporarily after their new 1.0.0 release breaks existings deps of langchain
PiperOrigin-RevId: 820875043
2025-10-17 16:53:11 -07:00
Google Team Member dbd818be0b feat: introduces a new AgentEngineSandboxCodeExecutor class that supports executes agent generated code
The AgentEngineSandboxCodeExecutor uses the Vertex AI Code Execution Sandbox API to execute code

PiperOrigin-RevId: 820854185
2025-10-17 15:42:24 -07:00
Xiang (Sean) Zhou a2d9f13fa1 chore: Add span for context caching handling and new cache creation
PiperOrigin-RevId: 820852233
2025-10-17 15:37:35 -07:00
Wei Sun (Jack) 0df67599c0 chore: Checks gemini version for 2 and above for gemini-builtin tools
PiperOrigin-RevId: 820848897
2025-10-17 15:27:47 -07:00
Shangjie Chen 8b3ed059c2 chore: Refactor and fix state management in the session service
Also refactoring the test cases to focus on the expected behaviors

PiperOrigin-RevId: 820734484
2025-10-17 10:04:36 -07:00
Ankur Sharma cf3403231d chore: Fix evaluation test cases to only use pytest features
PiperOrigin-RevId: 820700378
2025-10-17 08:25:17 -07:00
Shangjie Chen 9dce06f9b0 feat: Add rewind_async to support rewinding the session to before a previous invocation
PiperOrigin-RevId: 820552460
2025-10-16 23:24:40 -07:00
George Weale 307896aece fix: Exclude additionalProperties from Gemini schemas
PiperOrigin-RevId: 820542466
2025-10-16 22:40:43 -07:00
Kathy Wu 6dcbb5aca6 feat: Support dynamic per-request headers in MCPToolset
Add a header_provider param which is a callable[ReadonlyContext, Dict[str, Any]] for users to build headers in MCPToolset
fix: https://github.com/google/adk-python/issues/3156
PiperOrigin-RevId: 820412372
2025-10-16 15:12:43 -07:00
Xiang (Sean) Zhou 2a8fdd94e1 chore: Add computer use sample agent
PiperOrigin-RevId: 820407078
2025-10-16 14:59:27 -07:00
Xuan Yang 37a153ef94 ci: Fix the logs importing for PR triaging agent
PiperOrigin-RevId: 820395414
2025-10-16 14:27:18 -07:00
Giovanni Galloro ce4638651f docs: Bump models in llms and llms-full to Gemini 2.5
Merge https://github.com/google/adk-python/pull/3166

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3166 from ggalloro:model-updates e741c8b266ef8cd7def203f94e8d9f8608c06685
PiperOrigin-RevId: 820381464
2025-10-16 13:53:45 -07:00
Doug Gebert e6e2767c39 chore: Update gemini_llm_connection.py - typo spelling correction
Merge https://github.com/google/adk-python/pull/3168

Fixed misspelling on line 228 from:
- logger.info('Redeived session...

To:
- logger.info('Received session...
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3168 from DHHHG:patch-1 90b76d72ba474cd0092b2cc8d955b918c18d05bd
PiperOrigin-RevId: 820369618
2025-10-16 13:23:58 -07:00
Kacper Jawoszek e50f05a9fc feat(otel): env variable for disabling llm_request and llm_response in spans
The default without the variable set is to keep the content in spans to keep backward compatible behavior for existing users.

This allows to enable tracing without potential PII data from request and response. Google GenAI instrumentation lib requires an explicit opt-in to enable request and response content - see https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation-genai/opentelemetry-instrumentation-google-genai#enabling-message-content.

PiperOrigin-RevId: 820351154
2025-10-16 13:07:27 -07:00
Xuan Yang 9dc00360e3 ci: Add state info for PR triaging agent
PiperOrigin-RevId: 820340584
2025-10-16 13:07:18 -07:00
Google Team Member 5a543c00df feat: implement new methods in in-memory artifact service
* save_artifact with custom_metadata
* list_artifact_versions
* get_artifact_version

PiperOrigin-RevId: 820321444
2025-10-16 13:07:09 -07:00
Alexey Guseynov 1e1d63f34c fix: Internal change
PiperOrigin-RevId: 820159440
2025-10-16 13:07:00 -07:00
Xuan Yang f51380f9ea feat: Extend ReflectAndRetryToolPlugin to support hallucinating function calls
PiperOrigin-RevId: 820051762
2025-10-16 13:06:51 -07:00
Xuan Yang 3734ceaa6c fix: Use the agent's model when creating Google search agent tool
PiperOrigin-RevId: 819980797
2025-10-16 13:06:41 -07:00
Google Team Member 86097afe49 feat: Update AgentEvaluator to handle async ADK agent definitions
AgentEvaluator should recognize root_agent and get_agent_async as valid structures for ADK agent definitions.

PiperOrigin-RevId: 819976635
2025-10-16 13:06:31 -07:00
Google Team Member a17f3b2e6d feat: Allow custom request and event converters in A2aAgentExecutor
This change introduces type aliases for request and event conversion functions:
- `A2ARequestToADKRunArgsConverter`: For converting A2A `RequestContext` to an `ADKRunArgs` Pydantic model.
- `AdkEventToA2AEventsConverter`: For converting ADK `Event` to a list of A2A `A2AEvent` objects.

The `convert_a2a_request_to_adk_run_args` function now returns a structured `ADKRunArgs` model instead of a generic dictionary, improving type safety.

These converter types can now be provided via the `A2aAgentExecutorConfig` to customize the conversion logic used by the `A2aAgentExecutor`. The executor defaults to the existing `convert_a2a_request_to_adk_run_args` and `convert_event_to_a2a_events` functions if no custom converters are specified.

This allows users to inject their own logic for handling request and event conversions, for example, to add custom metadata or transform data types, without modifying the core executor.

PiperOrigin-RevId: 819934960
2025-10-16 13:06:21 -07:00
Shangjie Chen 6ab1498aa0 fix: Add usage_metadata and citation_metadata to DatabaseSessionService
PiperOrigin-RevId: 819900773
2025-10-16 13:05:52 -07:00
Google Team Member 2424d6a3b1 feat: Reorder create_time and mime_type to ArtifactVersion
Before: http://sponge2/ba05f9ac-c13d-43b6-bb8a-3e1b029cc705(failed)
After: http://sponge2/a623ba76-62c1-4d17-b4b6-22044333a801
PiperOrigin-RevId: 819896989
2025-10-15 13:46:12 -07:00
Lin Nikaido 36c96ec5b3 fix: #2883 pickle data was truncated error in database session using MySql
Merge https://github.com/google/adk-python/pull/2884

closes: #2883
# Fix
When put leage data into event and load it. the _pickle.UnpicklingError was occurred.
The root caurse is `DynamicPickleType` mapping `BLOB` as default in case of MySql, not `LONGBLOB`. And learge data will be able to cut off tail of data. And raise pickle error.

# What todo
Defined `LONFBLOB` as default explicitly.

# Question
Where should we code the test code like this case? I cannot found the test code the DB and table was created expectedly.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2884 from Lin-Nikaido:fix/#2883-mysql-datatype-fix 2be9b38fc3f5d5083b0b6715a2bf7b4eff5d947b
PiperOrigin-RevId: 819891727
2025-10-15 13:33:22 -07:00
Shangjie Chen a985cc38ec feat: Support return all sessions when user id is none
PiperOrigin-RevId: 819884236
2025-10-15 13:14:24 -07:00
machache b650181384 feat: add support for ContetxtWindowCompressionConfig in RunConfig
Merge https://github.com/google/adk-python/pull/2206

### Summary

This PR adds support for `ContextWindowCompressionConfig` in `RunConfig`.
This enables context window compression using a `trigger_tokens` threshold and a sliding window with a `target_tokens` limit.

This feature is useful for managing long-running audio inputs.

### Related Issue

Closes #2188

### Testing Plan

- Added new unit test: `test_streaming_with_context_window_compression_config`

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2206 from ac-machache:support/add-context-compression-config c8a5b15cae2d2b72f331797d07ae0bbaf977ed3c
PiperOrigin-RevId: 819855786
2025-10-15 12:00:21 -07:00
Kathy Wu 78e74b5bf2 feat: Add require_confirmation param for MCP tool/toolset
This allows users to require human approval for using MCP tools.

PiperOrigin-RevId: 819800747
2025-10-15 09:58:31 -07:00
Ankur Sharma d82c492140 chore: Remove deprecated convert_session_to_eval_format function
This change removes the `convert_session_to_eval_format` function and its associated unit tests. New tests for `create_gcs_eval_managers_from_uri` are also added.

PiperOrigin-RevId: 819576620
2025-10-14 22:37:49 -07:00
Xuan Yang 05aa3fa38b fix: Remove the PR triaging agent's dependence on "bot triaged" label
PiperOrigin-RevId: 819427872
2025-10-14 15:35:59 -07:00
Google Team Member f9c09ef075 feat: Support returning all sessions when user_id is none in the request
resolves https://github.com/google/adk-python/issues/3154

PiperOrigin-RevId: 819417330
2025-10-14 15:10:41 -07:00
Shangjie Chen 141318f775 feat: Support returning all sessions when user_id is none in the request
resolves https://github.com/google/adk-python/issues/3154

PiperOrigin-RevId: 819401023
2025-10-14 14:30:47 -07:00
Google Team Member 85c95a8cbf feat: Add create_time and mime_type to ArtifactVersion
PiperOrigin-RevId: 819396924
2025-10-14 14:21:02 -07:00
Hangfei Lin cb6d583cad chore: Add RSVP link to ADK Community Call
This change adds a link to a Qualtrics form for RSVPing to the ADK Community Call in the README.

PiperOrigin-RevId: 819376219
2025-10-14 13:36:33 -07:00
Shangjie Chen 2c7a342593 feat: Add create_time and mime_type to ArtifactVersion
PiperOrigin-RevId: 819372593
2025-10-14 13:29:24 -07:00
Joseph Pagadora 9fbed0b15a fix: Overall eval status should be NOT_EVALUATED if no invocations were evaluated
PiperOrigin-RevId: 819322513
2025-10-14 11:36:33 -07:00
Xuan Yang bae21027d9 chore: Disable the scheduled execution for issue triage workflow
PiperOrigin-RevId: 819312327
2025-10-14 11:15:12 -07:00
George Weale 89344da813 chore: Update agent builder instructions and remove run command details
PiperOrigin-RevId: 819299339
2025-10-14 10:46:53 -07:00
Xiang (Sean) Zhou d22b8bf890 chore: Clarify how to use adk built-in tool in instruction
PiperOrigin-RevId: 818987709
2025-10-13 21:43:37 -07:00
George Weale dfb8638eae chore: fix the cleanup_unused_files tool in yaml agent to use the same directory as other tools
PiperOrigin-RevId: 818846060
2025-10-13 15:01:36 -07:00
George Weale 3c0b99a19e fix: rollback of add structured JSON logging with Cloud Trace correlation Close #1683
PiperOrigin-RevId: 818844025
2025-10-13 14:57:23 -07:00
George Weale d8548aabd2 feat: add structured JSON logging with Cloud Trace correlation Close #1683
- add a shared --structured_logs flag to adk web and adk api_server so users can opt into JSON-formatted output
- introduce CloudTraceJSONFormatter that emits structured entries and attaches current Cloud Trace/Span IDs when an OpenTelemetry context is active
- update CLI logging setup to clear duplicate stdout handlers when Cloud Logging is enabled and to reconfigure existing handlers (like from Uvicorn) so they also pick up the structured format and requested log level

With the flag disabled the CLIs keep their existing text logs; when enabled, the services now produce Cloud Logging–friendly JSON that can be correlated with distributed traces.

PiperOrigin-RevId: 818823818
2025-10-13 14:08:45 -07:00
Google Team Member df05ed6b3b feat: migrate invocation_context to callback_context
Update plugin manager and built-in plugins to prioritize CallbackContext. Keep InvocationContext access for legacy plugins with adapter. Change callback docs/tests to cover the new context.

PiperOrigin-RevId: 818822267
2025-10-13 14:05:44 -07:00
Google Team Member 2158b3c915 fix: correctly populate context_id in remote_a2a_agent library when constructing a2a request
PiperOrigin-RevId: 818813897
2025-10-13 13:45:54 -07:00
George Weale e2072af69f feat: migrate invocation_context to callback_context
Update plugin manager and built-in plugins to prioritize CallbackContext. Keep InvocationContext access for legacy plugins with adapter. Change callback docs/tests to cover the new context.

PiperOrigin-RevId: 818798087
2025-10-13 13:09:15 -07:00
Xiang (Sean) Zhou fa84bcb575 chore: Correct the callback signatures
PiperOrigin-RevId: 818781277
2025-10-13 12:30:37 -07:00
Shangjie Chen bb1ea74924 chore: Delegate the agent state reset logic to LoopAgent
This is so we don't need to worry about side effect of Loop in all agent type. Custom agent should do the same if there exists loop inside.

PiperOrigin-RevId: 818766305
2025-10-13 11:53:59 -07:00
Xiang (Sean) Zhou 214986ebeb chore: Adjust the instruction about default model
PiperOrigin-RevId: 818765464
2025-10-13 11:52:11 -07:00
Ankur Sharma 348e552ba6 chore: Remove deprecated run_evals from cli_eval.py
This change removes the `run_evals` function and its helper `_get_evaluator` from `cli_eval.py`, as they were marked as deprecated. Corresponding test mocks and patches in `test_fast_api.py` are also removed.

PiperOrigin-RevId: 818719422
2025-10-13 10:13:12 -07:00
Google Team Member e212ff558e feat: Add new methods in the artifact service interface
PiperOrigin-RevId: 818473733
2025-10-12 21:19:20 -07:00
Xuan Yang e63180cb62 feat: Add the support for returning struct_data.uri in DiscoveryEngineSearchTool
For https://github.com/google/adk-python/issues/3146

PiperOrigin-RevId: 818458080
2025-10-12 20:28:08 -07:00
Google Team Member 6da7274858 feat: Set default for bypass_multi_tools_limit to False for GoogleSearchTool and VertexAiSearchTool
PiperOrigin-RevId: 818053371
2025-10-11 09:57:36 -07:00
Xuan Yang b21d0a50d6 fix: Add more clear instruction to the doc updater agent about one PR for each recommended change
PiperOrigin-RevId: 817831087
2025-10-10 16:37:12 -07:00
Joe Fernandez 16b030b2b2 fix: Add a guideline to avoid content deletion
Directs agent to avoid deleting existing content

PiperOrigin-RevId: 817823999
2025-10-10 16:12:16 -07:00
Xinran (Sherry) Tang 59670d240e feat: Support resuming from a paused invocation starting from a sub-agent
PiperOrigin-RevId: 817766247
2025-10-10 13:24:02 -07:00
Google Team Member bddc70b5d0 fix: Better handling the A2A streaming tasks so calling Agent can tell whether it's in progress updates (thought) or the final response
PiperOrigin-RevId: 817682171
2025-10-10 09:46:54 -07:00
George Weale 85ed500871 fix: Add support for file URIs in LiteLLM content conversion to fix issue #3131
changed the LiteLLM content conversion so Part.file_data.file_uri (like the gs://…) becomes a file object with file_id, making sure GCS-backed files reach LiteLLM proxies instead of being dropped add unit tests covering both _get_content and _content_to_message_param paths for file URIs

PiperOrigin-RevId: 817658432
2025-10-10 08:39:17 -07:00
Ankur Sharma 998264a5b1 fix: Only exclude scores that are None
Scores with values 0 (or 0.0) were also getting excluded.

PiperOrigin-RevId: 817658059
2025-10-10 08:38:18 -07:00
Xuan Yang 9a6b8507f0 feat: Add bypass_multi_tools_limit option to GoogleSearchTool and VertexAiSearchTool
PiperOrigin-RevId: 817493869
2025-10-09 23:05:02 -07:00
Ankur Sharma 64646e0002 chore: Remove deprecated static methods from TrajectoryEvaluator
This change removes the `evaluate`, `_evaluate_row`, `are_tools_equal`, `_remove_tool_outputs`, `_report_failures`, and `_print_results` static methods from `TrajectoryEvaluator`, along with their corresponding unit tests. These methods were previously marked as deprecated.

PiperOrigin-RevId: 817477494
2025-10-09 22:02:24 -07:00
Hangfei Lin 81913c85f4 chore: Update README md with recent ADK features
This CL updates the "What's new" section to include Resumability, ReflectRetryToolPlugin, Context compaction, and Search tool support. It also moves "Agent Config" and "Tool Confirmation" from "What's new" to "Key Features".

PiperOrigin-RevId: 817469210
2025-10-09 21:23:37 -07:00
Xiang (Sean) Zhou 9e0b1fb62b fix: Create context cache only when prefix matches with previous request
PiperOrigin-RevId: 817468275
2025-10-09 21:19:28 -07:00
Hangfei Lin 731bb9078d chore: Announce the first ADK Community Call in the README
The added section provides details for the community call on Oct 15, 2025, including the agenda and links to join and add to calendars.

PiperOrigin-RevId: 817457276
2025-10-09 20:35:47 -07:00
Ankur Sharma ae139bb461 feat: ADK cli allows developers to create an eval set and add an eval case
Agent developers can now create an eval set and add eval cases through command line itself. Adding an eval case is limited only to specifying conversation scenarios.

Sample comamnds:
- Create an eval set:
adk eval_set create \
    contributing/samples/hello_world \
    set_01

- Add an eval case with scenario file
Content of scenarios.json file:
'{"scenarios": [{"starting_prompt": "hello", "conversation_plan": "world"}]}'

adk eval_set add_eval_case \
    contributing/samples/hello_world \
    set_01 \
    --scenarios scenarios.json

PiperOrigin-RevId: 817456117
2025-10-09 20:31:01 -07:00
Xinran (Sherry) Tang 9939e0b087 feat: Support resuming a parallel agent with multiple branches paused on tool confirmation requests
PiperOrigin-RevId: 817373403
2025-10-09 16:04:55 -07:00
Xiang (Sean) Zhou cc24d616f8 feat: Support ContentUnion as static instruction
PiperOrigin-RevId: 817278990
2025-10-09 11:52:24 -07:00
Wei Sun (Jack) 0aede9f1a1 docs: Update CHANGELOG with [847df16](https://github.com/google/adk-python/commit/847df1638cbf1686aa43e8e094121d4e23e40245)
PiperOrigin-RevId: 817267158
2025-10-09 11:24:48 -07:00
George Weale 847df1638c fix: handle App instances returned by agent_loader.load_agent
The `agent_loader.load_agent` method can now return an `App` object. This change unwraps the `App` to get its `root_agent` before passing it to the graph builder, makes sure a `BaseAgent` instance is always used

PiperOrigin-RevId: 817209601
2025-10-09 09:00:30 -07:00
Kacper Jawoszek 55aa6f669b feat(otel): set default_log_name for GCP logging exporter
Uses value of GCP_DEFAULT_LOG_NAME env var if it exists, defaults to literal adk-otel.

PiperOrigin-RevId: 817125337
2025-10-09 04:38:26 -07:00
Xuan Yang 9b8a4aad6f chore: Add an sample agent for the ReflectAndRetryToolPlugin
PiperOrigin-RevId: 817024977
2025-10-08 23:05:25 -07:00
Xiang (Sean) Zhou cac9fae829 chore: Don't label 'bot triaged' for PR
PiperOrigin-RevId: 816959715
2025-10-08 19:21:35 -07:00
Wei Sun (Jack) 24342e95f8 chore: Remove temp state deltas before appending an event
PiperOrigin-RevId: 816902208
2025-10-08 16:13:52 -07:00
Ankur Sharma cbe60c47aa feat: Adds data model to support UserSimulation
Details:
- Introduces a concept of `ConversationScenario` to represent a scenario that user simulator is supposed to follow.
- Introduces a `UserSimulator` interface, that one should implement. UserSimulator interface will be integrated with LocalEvalService in subsequent PRs.
PiperOrigin-RevId: 816883699
2025-10-08 15:23:57 -07:00
Xiang (Sean) Zhou 2efaa57575 chore: Don't label issue as bot triaged
PiperOrigin-RevId: 816873967
2025-10-08 14:58:49 -07:00
188 changed files with 13717 additions and 7129 deletions
+8 -2
View File
@@ -3,10 +3,16 @@ name: ADK Pull Request Triaging Agent
on:
pull_request_target:
types: [opened, reopened, edited]
workflow_dispatch:
inputs:
pr_number:
description: 'The Pull Request number to triage'
required: true
type: 'string'
jobs:
agent-triage-pull-request:
if: "!contains(github.event.pull_request.labels.*.name, 'bot triaged') && !contains(github.event.pull_request.labels.*.name, 'google-contributor')"
if: github.event_name == 'workflow_dispatch' || !contains(github.event.pull_request.labels.*.name, 'google-contributor')
runs-on: ubuntu-latest
permissions:
pull-requests: write
@@ -33,7 +39,7 @@ jobs:
GOOGLE_GENAI_USE_VERTEXAI: 0
OWNER: 'google'
REPO: 'adk-python'
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
INTERACTIVE: ${{ vars.PR_TRIAGE_INTERACTIVE }}
PYTHONPATH: contributing/samples
run: python -m adk_pr_triaging_agent.main
-2
View File
@@ -3,8 +3,6 @@ name: ADK Issue Triaging Agent
on:
issues:
types: [opened, reopened]
schedule:
- cron: '0 */6 * * *' # every 6h
jobs:
agent-triage-issues:
+1
View File
@@ -49,6 +49,7 @@
* Update remote_a2a_agent to better handle streaming events and avoid duplicate responses ([8e5f361](https://github.com/google/adk-python/commit/8e5f36126498f751171bb2639c7f5a9e7dca2558))
* Update the load_artifacts tool so that the model can reliably call it for follow up questions about the same artifact ([238472d](https://github.com/google/adk-python/commit/238472d083b5aa67551bde733fc47826ff062679))
* Fix VertexAiSessionService base_url override to preserve initialized http_options ([8110e41](https://github.com/google/adk-python/commit/8110e41b36cceddb8b92ba17cffaacf701706b36), [c51ea0b](https://github.com/google/adk-python/commit/c51ea0b52e63de8e43d3dccb24f9d20987784aa5))
* Handle `App` instances returned by `agent_loader.load_agent` ([847df16](https://github.com/google/adk-python/commit/847df1638cbf1686aa43e8e094121d4e23e40245))
### Improvements
+38 -3
View File
@@ -25,14 +25,44 @@
Agent Development Kit (ADK) is a flexible and modular framework for developing and deploying AI agents. While optimized for Gemini and the Google ecosystem, ADK is model-agnostic, deployment-agnostic, and is built for compatibility with other frameworks. ADK was designed to make agent development feel more like software development, to make it easier for developers to create, deploy, and orchestrate agentic architectures that range from simple tasks to complex workflows.
---
## 🔥 ADK's very first community call on Oct 15
Join our ADK Community Call! Our first virtual community call is on Oct 15!
Meet our team, and talk with us about our roadmap and how to contribute.
First Call Details:
Topic: ADK Roadmap
Date: October 15, 2025
Time: 9:30-10:30am PST
Meeting link:
[Join the call](http://meet.google.com/gjm-gfim-ctz)
Add to your calendar
[Event calendar invite](https://calendar.google.com/calendar/event?action=TEMPLATE&tmeid=MDUydWo1dHV1dHFtNzJuM3E0bmEyMW12ZnZfMjAyNTEwMTVUMTYzMDAwWiBjXzljNWVjODhhMmQyYWU5YjY5Mzk4ODU1MGZkNDA5MjVmYjgxYjM4MTI1NGNjYTgzNmRkMjMwNzRiMjNmYzcyZDVAZw&tmsrc=c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com), [.ics file](https://calendar.google.com/calendar/ical/c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com/public/basic.ics), [ADK community calendar](https://calendar.google.com/calendar/embed?src=c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com&ctz=America%2FLos_Angeles), [ADK Community Call RSVP](https://google.qualtrics.com/jfe/form/SV_3K0RJZ64H1BexqS)
Agenda:
[Julia] ADK Roadmap
[ Bo & Hangfei] Eng Deep Dive: Context Caching
[Kris] How to Contribute
[Shubham] Upcoming Events
---
## 🔥 What's new
- **Agent Config**: Build agents without code. Check out the
[Agent Config](https://google.github.io/adk-docs/agents/config/) feature.
- **Context compaction**: Supports context compaction to reduce context length. Here is a [sample](https://github.com/google/adk-python/blob/main/contributing/samples/hello_world_app/agent.py#L156) and [compaction config](https://github.com/google/adk-python/blob/main/src/google/adk/apps/app.py#L51).
- **Tool Confirmation**: A [tool confirmation flow(HITL)](https://google.github.io/adk-docs/tools/confirmation/) that can guard tool execution with explicit confirmation and custom input
- **Resumability**: Support pause and resume an invocation in ADK.
- **ReflectRetryToolPlugin**: Add [`ReflectRetryToolPlugin`](https://github.com/google/adk-python/blob/main/src/google/adk/plugins/reflect_retry_tool_plugin.py) to reflect from errors and retry with different arguments when tool errors.
- **Search tool**: Support using Google built-in search and built-in `VertexAiSearchTool` with other tools in the same agent.
## ✨ Key Features
@@ -43,6 +73,11 @@ Agent Development Kit (ADK) is a flexible and modular framework for developing a
- **Code-First Development**: Define agent logic, tools, and orchestration
directly in Python for ultimate flexibility, testability, and versioning.
- **Agent Config**: Build agents without code. Check out the
[Agent Config](https://google.github.io/adk-docs/agents/config/) feature.
- **Tool Confirmation**: A [tool confirmation flow(HITL)](https://google.github.io/adk-docs/tools/confirmation/) that can guard tool execution with explicit confirmation and custom input.
- **Modular Multi-Agent Systems**: Design scalable applications by composing
multiple specialized agents into flexible hierarchies.
@@ -64,7 +64,9 @@ Always reference this schema when creating configurations to ensure compliance.
- **MANDATORY CONFIRMATION**: Say "Please confirm what model you want to use" - do NOT assume or suggest defaults
- **EXAMPLES**: "gemini-2.5-flash", "gemini-2.5-pro", etc.
- **RATIONALE**: Only LlmAgent requires model specification; workflow agents do not
- **DEFAULT ONLY**: Use "{default_model}" only if user explicitly says "use default" or similar
- **DEFAULT MODEL**: If user says "use default" or "proceed with default model", use: {default_model}
* This is the actual model name, NOT the literal string "default"
* The default model for this session is: {default_model}
- **WORKFLOW**: Complete all Discovery steps (including this model selection) → Then proceed to Design Phase with model already chosen
### 2. Design Phase
@@ -127,6 +129,43 @@ Always reference this schema when creating configurations to ensure compliance.
* **Remember**: Always extract just the folder name after the last slash/separator
- No function declarations in YAML (handled automatically by ADK)
**🚨 CRITICAL: Built-in Tools vs Custom Tools**
**ADK Built-in Tools** (use directly, NO custom Python file needed):
- **Naming**: Use simple name WITHOUT dots (e.g., `google_search`, NOT `google.adk.tools.google_search`)
- **No custom code**: Do NOT create Python files for built-in tools
- **Available built-in tools**:
* `google_search` - Google Search tool
* `enterprise_web_search` - Enterprise web search
* `google_maps_grounding` - Google Maps grounding
* `url_context` - URL context fetching
* `VertexAiSearchTool` - Vertex AI Search (class name)
* `exit_loop` - Exit loop control
* `get_user_choice` - User choice interaction
* `load_artifacts` - Load artifacts
* `load_memory` - Load memory
* `preload_memory` - Preload memory
* `transfer_to_agent` - Transfer to another agent
**Example - Built-in Tool Usage (CORRECT):**
```yaml
tools:
- name: google_search
- name: url_context
```
**Example - Built-in Tool Usage (WRONG):**
```yaml
tools:
- name: cb.tools.google_search_tool.google_search_tool # ❌ WRONG - treating built-in as custom
```
**DO NOT create Python files like `tools/google_search_tool.py` for built-in tools!**
**Custom Tools** (require Python implementation):
- **Naming**: Use dotted path: `{{project_folder_name}}.tools.{{module_name}}.{{function_name}}`
- **Require Python file**: Must create actual Python file in `tools/` directory
- **Example**: `my_project.tools.dice_tool.roll_dice` → requires `tools/dice_tool.py` with `roll_dice()` function
**TOOL IMPLEMENTATION STRATEGY:**
- **For simple/obvious tools**: Implement them directly with actual working code
* Example: dice rolling, prime checking, basic math, file operations
@@ -286,11 +325,11 @@ from typing import Optional
from google.genai import types
from google.adk.agents.callback_context import CallbackContext
def content_filter_callback(context: CallbackContext) -> Optional[types.Content]:
def content_filter_callback(callback_context: CallbackContext) -> Optional[types.Content]:
"""After agent callback to filter sensitive content."""
# Access the response content through context
if hasattr(context, 'response') and context.response:
response_text = str(context.response)
# Access the response content through callback_context
if hasattr(callback_context, 'response') and callback_context.response:
response_text = str(callback_context.response)
if "confidential" in response_text.lower():
filtered_text = response_text.replace("confidential", "[FILTERED]")
return types.Content(parts=[types.Part(text=filtered_text)])
@@ -306,12 +345,12 @@ from google.adk.models.llm_request import LlmRequest
from google.adk.models.llm_response import LlmResponse
from google.adk.agents.callback_context import CallbackContext
def log_model_request(context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]:
def log_model_request(callback_context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]:
"""Before model callback to log requests."""
print(f"Model request: {{request.contents}}")
return None # Return None to proceed with original request
def modify_model_response(context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]:
def modify_model_response(callback_context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]:
"""After model callback to modify response."""
# Modify response if needed
return response # Return modified response or None for original
@@ -325,25 +364,25 @@ from typing import Any, Dict, Optional
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.tool_context import ToolContext
def validate_tool_input(tool: BaseTool, args: Dict[str, Any], context: ToolContext) -> Optional[Dict]:
def validate_tool_input(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext) -> Optional[Dict]:
"""Before tool callback to validate input."""
# Validate or modify tool arguments
if "unsafe_param" in args:
del args["unsafe_param"]
return args # Return modified args or None for original
if "unsafe_param" in tool_args:
del tool_args["unsafe_param"]
return tool_args # Return modified args or None for original
def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext, result: Dict) -> Optional[Dict]:
def log_tool_result(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext, result: Dict) -> Optional[Dict]:
"""After tool callback to log results."""
print(f"Tool {{tool.name}} executed with result: {{result}}")
return None # Return None to keep original result
```
## Callback Signature Summary:
- **Agent Callbacks**: `(CallbackContext) -> Optional[types.Content]`
- **Before Model**: `(CallbackContext, LlmRequest) -> Optional[LlmResponse]`
- **After Model**: `(CallbackContext, LlmResponse) -> Optional[LlmResponse]`
- **Before Tool**: `(BaseTool, Dict[str, Any], ToolContext) -> Optional[Dict]`
- **After Tool**: `(BaseTool, Dict[str, Any], ToolContext, Dict) -> Optional[Dict]`
- **Agent Callbacks**: `(callback_context: CallbackContext) -> Optional[types.Content]`
- **Before Model**: `(callback_context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]`
- **After Model**: `(callback_context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]`
- **Before Tool**: `(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext) -> Optional[Dict]`
- **After Tool**: `(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext, result: Dict) -> Optional[Dict]`
## Important ADK Requirements
@@ -369,7 +408,7 @@ def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext,
**ADK AgentConfig Schema Compliance:**
- Always reference the embedded ADK AgentConfig schema to verify field requirements
- **MODEL FIELD RULES**:
* **LlmAgent**: `model` field is REQUIRED (unless inherited from ancestor) - Ask user for preference only when LlmAgent is needed, use "{default_model}" if not specified
* **LlmAgent**: `model` field is REQUIRED (unless inherited from ancestor) - Ask user for preference only when LlmAgent is needed, use {default_model} if user says to use default
* **Workflow Agents**: `model` field is FORBIDDEN - Remove model field entirely for Sequential/Parallel/Loop agents
- Optional fields: description, instruction, tools, sub_agents as defined in ADK AgentConfig schema
@@ -404,7 +443,6 @@ def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext,
2. No redundant suggest_file_path calls for pre-approved paths
3. Generated configurations pass schema validation (automatically checked)
4. Follow ADK naming and organizational conventions
5. Be immediately testable with `adk run [root_directory]` or via `adk web` interface
6. Include clear, actionable instructions for each agent
7. Use appropriate tools for intended functionality
@@ -424,14 +462,3 @@ def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext,
6. **Focus on collaboration** - Ensure user gets exactly what they need with clear understanding
**This workflow eliminates inefficiencies and ensures users get well-organized, predictable file structures in their chosen location.**
## Running Generated Agents
**Correct ADK Commands:**
- `adk run [project_directory]` - Run agent from project directory (e.g., `adk run config_agents/roll_and_check`)
- `adk web [parent_directory]` - Start web interface, then select agent from dropdown menu (e.g., `adk web config_agents`)
- **Key Rule**: Always use the project directory for `adk run`, and parent directory for `adk web`
**Incorrect Commands to Avoid:**
- `adk run [project_directory]/root_agent.yaml` - Do NOT specify the YAML file directly
- `adk web` without parent directory - Must specify the parent folder containing the agent projects
@@ -14,16 +14,20 @@
"""Cleanup unused files tool for Agent Builder Assistant."""
from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from google.adk.tools.tool_context import ToolContext
from ..utils.resolve_root_directory import resolve_file_path
from ..utils.resolve_root_directory import resolve_file_paths
async def cleanup_unused_files(
root_directory: str,
used_files: List[str],
tool_context: ToolContext,
file_patterns: Optional[List[str]] = None,
exclude_patterns: Optional[List[str]] = None,
) -> Dict[str, Any]:
@@ -31,27 +35,32 @@ async def cleanup_unused_files(
This tool helps clean up unused tool files when agent configurations change.
It identifies files that match patterns but aren't referenced in used_files
list.
list. Paths are resolved automatically using the tool context.
Args:
root_directory: Root directory to scan for unused files
used_files: List of file paths currently in use (should not be deleted)
tool_context: Tool execution context (provides session state)
file_patterns: List of glob patterns to match files (default: ["*.py"])
exclude_patterns: List of patterns to exclude (default: ["__init__.py"])
Returns:
Dict containing cleanup results:
- success: bool indicating if scan succeeded
- root_directory: absolute path to scanned directory
- unused_files: list of unused files found
- deleted_files: list of files actually deleted
- backup_files: list of backup files created
- errors: list of error messages
- total_freed_space: total bytes freed by deletions
"""
session_state = tool_context.state
root_path = resolve_file_path(".", session_state)
try:
root_path = Path(root_directory).resolve()
used_files_set = {Path(f).resolve() for f in used_files}
root_path = root_path.resolve()
resolved_used_files = {
path.resolve()
for path in resolve_file_paths(used_files or [], session_state)
}
# Set defaults
if file_patterns is None:
@@ -61,7 +70,6 @@ async def cleanup_unused_files(
result = {
"success": False,
"root_directory": str(root_path),
"unused_files": [],
"deleted_files": [],
"backup_files": [],
@@ -85,7 +93,7 @@ async def cleanup_unused_files(
# Identify unused files
unused_files = []
for file_path in all_files:
if file_path not in used_files_set:
if file_path.resolve() not in resolved_used_files:
unused_files.append(file_path)
result["unused_files"] = [str(f) for f in unused_files]
@@ -99,7 +107,6 @@ async def cleanup_unused_files(
except Exception as e:
return {
"success": False,
"root_directory": root_directory,
"unused_files": [],
"deleted_files": [],
"backup_files": [],
@@ -85,7 +85,15 @@ root_agent = Agent(
- Use active voice phrasing in your doc updates.
- Use second person "you" form of address in your doc updates.
9. Create pull requests to update the ADK doc file using the `create_pull_request_from_changes` tool.
- For each recommended change, create a separate pull request.
- For each recommended change, create a separate pull request. Make sure the recommended change has exactly one pull request.
For example, if the ADK doc issue contains the following 2 recommended changes:
```
1. Title of recommended change 1
<content of recommended change 1>
2. Title of recommended change 2
<content of recommended change 2>
```
Then you should create 2 pull requests, one for each recommended change, even if each recommended change needs to update multiple ADK doc files.
- The title of the pull request should be "Update ADK doc according to issue #<issue number> - <change id>", where <issue number> is the number of the ADK docs issue and <change id> is the id of the recommended change (e.g. "1", "2", etc.).
- The body of the pull request should be the instructions about how to update the ADK docs.
- **{APPROVAL_INSTRUCTION}**
@@ -93,6 +101,7 @@ root_agent = Agent(
# 4. Guidelines & Rules
- **File Paths:** Always use absolute paths when calling the tools to read files, list directories, or search the codebase.
- **Tool Call Parallelism:** Execute multiple independent tool calls in parallel when feasible (i.e. searching the codebase).
- **Avoid deletion:** Do not delete any existing content unless specifically directed to do so.
- **Explaination:** Provide concise explanations for your actions and reasoning for each step.
- **Minimize changes:** When making updates to documentation pages, make the minimum amount of changes to achieve the communication goal. Only make changes that are necessary, and leave everything else as-is.
- **Avoid trivial code sample changes:** Update code samples only when adding or modifying functionality. Do not reformat code samples, change variable names, or change code syntax unless you are specifically directed to make those updates.
@@ -15,7 +15,6 @@
from pathlib import Path
from typing import Any
from adk_pr_triaging_agent.settings import BOT_LABEL
from adk_pr_triaging_agent.settings import GITHUB_BASE_URL
from adk_pr_triaging_agent.settings import IS_INTERACTIVE
from adk_pr_triaging_agent.settings import OWNER
@@ -70,8 +69,10 @@ def get_pull_request_details(pr_number: int) -> str:
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
id
number
title
body
state
author {
login
}
@@ -178,7 +179,7 @@ def add_label_and_reviewer_to_pr(pr_number: int, label: str) -> dict[str, Any]:
label_url = (
f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{pr_number}/labels"
)
label_payload = [label, BOT_LABEL]
label_payload = [label]
try:
response = post_request(label_url, label_payload)
@@ -299,7 +300,10 @@ root_agent = Agent(
# 4. Steps
When you are given a PR, here are the steps you should take:
- Call the `get_pull_request_details` tool to get the details of the PR.
- Skip the PR (i.e. do not label or comment) if the PR is closed or is labeled with "{BOT_LABEL}" or "google-contributior".
- Skip the PR (i.e. do not label or comment) if any of the following is true:
- the PR is closed
- the PR is labeled with "google-contributior"
- the PR is already labelled with the above labels (e.g. "documentation", "services", "tools", etc.) and has a reviewer assigned.
- Check if the PR is following the contribution guidelines.
- If it's not following the guidelines, recommend or add a comment to the PR that points to the contribution guidelines (https://github.com/google/adk-python/blob/main/CONTRIBUTING.md).
- If it's following the guidelines, recommend or add a label to the PR.
@@ -13,6 +13,7 @@
# limitations under the License.
import asyncio
import logging
import time
from adk_pr_triaging_agent import agent
@@ -21,11 +22,14 @@ from adk_pr_triaging_agent.settings import PULL_REQUEST_NUMBER
from adk_pr_triaging_agent.settings import REPO
from adk_pr_triaging_agent.utils import call_agent_async
from adk_pr_triaging_agent.utils import parse_number_string
from google.adk.cli.utils import logs
from google.adk.runners import InMemoryRunner
APP_NAME = "adk_pr_triaging_app"
USER_ID = "adk_pr_triaging_user"
logs.setup_adk_logger(level=logging.DEBUG)
async def main():
runner = InMemoryRunner(
@@ -27,7 +27,6 @@ if not GITHUB_TOKEN:
OWNER = os.getenv("OWNER", "google")
REPO = os.getenv("REPO", "adk-python")
BOT_LABEL = os.getenv("BOT_LABEL", "bot triaged")
PULL_REQUEST_NUMBER = os.getenv("PULL_REQUEST_NUMBER")
IS_INTERACTIVE = os.environ.get("INTERACTIVE", "1").lower() in ["true", "1"]
@@ -14,7 +14,6 @@
from typing import Any
from adk_triaging_agent.settings import BOT_LABEL
from adk_triaging_agent.settings import GITHUB_BASE_URL
from adk_triaging_agent.settings import IS_INTERACTIVE
from adk_triaging_agent.settings import OWNER
@@ -103,7 +102,7 @@ def add_label_and_owner_to_issue(
label_url = (
f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/labels"
)
label_payload = [label, BOT_LABEL]
label_payload = [label]
try:
response = post_request(label_url, label_payload)
@@ -26,7 +26,6 @@ if not GITHUB_TOKEN:
OWNER = os.getenv("OWNER", "google")
REPO = os.getenv("REPO", "adk-python")
BOT_LABEL = os.getenv("BOT_LABEL", "bot triaged")
EVENT_NAME = os.getenv("EVENT_NAME")
ISSUE_NUMBER = os.getenv("ISSUE_NUMBER")
ISSUE_TITLE = os.getenv("ISSUE_TITLE")
@@ -0,0 +1,18 @@
# OAuth Sample
## Introduction
This sample data science agent uses Agent Engine Code Execution Sandbox to execute LLM generated code.
## How to use
* 1. Follow https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/code-execution/overview to create a code execution sandbox environment.
* 2. Replace the SANDBOX_RESOURCE_NAME with the one you just created. If you dont want to create a new sandbox environment directly, the Agent Engine Code Execution Sandbox will create one for you by default using the AGENT_ENGINE_RESOURCE_NAME you specified, however, please ensure to clean up sandboxes after use, otherwise, it will consume quotas.
## Sample prompt
* Can you write a function that calculates the sum from 1 to 100.
* The dataset is given as below. Store,Date,Weekly_Sales,Holiday_Flag,Temperature,Fuel_Price,CPI,Unemployment Store 1,2023-06-01,1000,0,70,3.0,200,5 Store 2,2023-06-02,1200,1,80,3.5,210,6 Store 3,2023-06-03,1400,0,90,4.0,220,7 Store 4,2023-06-04,1600,1,70,4.5,230,8 Store 5,2023-06-05,1800,0,80,5.0,240,9 Store 6,2023-06-06,2000,1,90,5.5,250,10 Store 7,2023-06-07,2200,0,90,6.0,260,11 Plot a scatter plot showcasing the relationship between Weekly Sales and Temperature for each store, distinguishing stores with a Holiday Flag.
@@ -0,0 +1,15 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import agent
@@ -0,0 +1,95 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Data science agent."""
from google.adk.agents.llm_agent import Agent
from google.adk.code_executors.agent_engine_sandbox_code_executor import AgentEngineSandboxCodeExecutor
def base_system_instruction():
"""Returns: data science agent system instruction."""
return """
# Guidelines
**Objective:** Assist the user in achieving their data analysis goals within the context of a Python Colab notebook, **with emphasis on avoiding assumptions and ensuring accuracy.** Reaching that goal can involve multiple steps. When you need to generate code, you **don't** need to solve the goal in one go. Only generate the next step at a time.
**Code Execution:** All code snippets provided will be executed within the Colab environment.
**Statefulness:** All code snippets are executed and the variables stays in the environment. You NEVER need to re-initialize variables. You NEVER need to reload files. You NEVER need to re-import libraries.
**Output Visibility:** Always print the output of code execution to visualize results, especially for data exploration and analysis. For example:
- To look a the shape of a pandas.DataFrame do:
```tool_code
print(df.shape)
```
The output will be presented to you as:
```tool_outputs
(49, 7)
```
- To display the result of a numerical computation:
```tool_code
x = 10 ** 9 - 12 ** 5
print(f'{{x=}}')
```
The output will be presented to you as:
```tool_outputs
x=999751168
```
- You **never** generate ```tool_outputs yourself.
- You can then use this output to decide on next steps.
- Print just variables (e.g., `print(f'{{variable=}}')`.
**No Assumptions:** **Crucially, avoid making assumptions about the nature of the data or column names.** Base findings solely on the data itself. Always use the information obtained from `explore_df` to guide your analysis.
**Available files:** Only use the files that are available as specified in the list of available files.
**Data in prompt:** Some queries contain the input data directly in the prompt. You have to parse that data into a pandas DataFrame. ALWAYS parse all the data. NEVER edit the data that are given to you.
**Answerability:** Some queries may not be answerable with the available data. In those cases, inform the user why you cannot process their query and suggest what type of data would be needed to fulfill their request.
"""
root_agent = Agent(
model="gemini-2.0-flash-001",
name="agent_engine_code_execution_agent",
instruction=base_system_instruction() + """
You need to assist the user with their queries by looking at the data and the context in the conversation.
You final answer should summarize the code and code execution relevant to the user query.
You should include all pieces of data to answer the user query, such as the table from code execution results.
If you cannot answer the question directly, you should follow the guidelines above to generate the next step.
If the question can be answered directly with writing any code, you should do that.
If you doesn't have enough data to answer the question, you should ask for clarification from the user.
You should NEVER install any package on your own like `pip install ...`.
When plotting trends, you should make sure to sort and order the data by the x-axis.
""",
code_executor=AgentEngineSandboxCodeExecutor(
# Replace with your sandbox resource name if you already have one.
sandbox_resource_name="SANDBOX_RESOURCE_NAME",
# "projects/vertex-agent-loadtest/locations/us-central1/reasoningEngines/6842889780301135872/sandboxEnvironments/6545148628569161728",
# Replace with agent engine resource name used for creating sandbox if
# sandbox_resource_name is not set.
agent_engine_resource_name="AGENT_ENGINE_RESOURCE_NAME",
),
)
@@ -17,7 +17,7 @@ import random
from dotenv import load_dotenv
from google.adk import Agent
from google.adk.tools.google_search_tool import google_search
from google.adk.tools.google_search_tool import GoogleSearchTool
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.vertex_ai_search_tool import VertexAiSearchTool
@@ -57,7 +57,9 @@ root_agent = Agent(
""",
tools=[
roll_die,
VertexAiSearchTool(data_store_id=VERTEXAI_DATASTORE_ID),
google_search,
VertexAiSearchTool(
data_store_id=VERTEXAI_DATASTORE_ID, bypass_multi_tools_limit=True
),
GoogleSearchTool(bypass_multi_tools_limit=True),
],
)
@@ -0,0 +1,96 @@
# Computer Use Agent
This directory contains a computer use agent that can operate a browser to complete user tasks. The agent uses Playwright to control a Chromium browser and can interact with web pages by taking screenshots, clicking, typing, and navigating.
This agent is to demo the usage of ComputerUseToolset.
## Overview
The computer use agent consists of:
- `agent.py`: Main agent configuration using Google's gemini-2.5-computer-use-preview-10-2025 model
- `playwright.py`: Playwright-based computer implementation for browser automation
- `requirements.txt`: Python dependencies
## Setup
### 1. Install Python Dependencies
Install the required Python packages from the requirements file:
```bash
uv pip install -r internal/samples/computer_use/requirements.txt
```
### 2. Install Playwright Dependencies
Install Playwright's system dependencies for Chromium:
```bash
playwright install-deps chromium
```
### 3. Install Chromium Browser
Install the Chromium browser for Playwright:
```bash
playwright install chromium
```
## Usage
### Running the Agent
To start the computer use agent, run the following command from the project root:
```bash
adk web internal/samples
```
This will start the ADK web interface where you can interact with the computer_use agent.
### Example Queries
Once the agent is running, you can send queries like:
```
find me a flight from SF to Hawaii on next Monday, coming back on next Friday. start by navigating directly to flights.google.com
```
The agent will:
1. Open a browser window
2. Navigate to the specified website
3. Interact with the page elements to complete your task
4. Provide updates on its progress
### Other Example Tasks
- Book hotel reservations
- Search for products online
- Fill out forms
- Navigate complex websites
- Research information across multiple pages
## Technical Details
- **Model**: Uses Google's `gemini-2.5-computer-use-preview-10-2025` model for computer use capabilities
- **Browser**: Automated Chromium browser via Playwright
- **Screen Size**: Configured for 600x800 resolution
- **Tools**: Uses ComputerUseToolset for screen capture, clicking, typing, and scrolling
## Troubleshooting
If you encounter issues:
1. **Playwright not found**: Make sure you've run both `playwright install-deps chromium` and `playwright install chromium`
2. **Dependencies missing**: Verify all packages from `requirements.txt` are installed
3. **Browser crashes**: Check that your system supports Chromium and has sufficient resources
4. **Permission errors**: Ensure your user has permission to run browser automation tools
## Notes
- The agent operates in a controlled browser environment
- Screenshots are taken to help the agent understand the current state
- The agent will provide updates on its actions as it works
- Be patient as complex tasks may take some time to complete
+35
View File
@@ -0,0 +1,35 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from google.adk import Agent
from google.adk.models.google_llm import Gemini
from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
from typing_extensions import override
from .playwright import PlaywrightComputer
root_agent = Agent(
model='gemini-2.5-computer-use-preview-10-2025',
name='hello_world_agent',
description=(
'computer use agent that can operate a browser on a computer to finish'
' user tasks'
),
instruction="""
you are a computer use agent
""",
tools=[
ComputerUseToolset(computer=PlaywrightComputer(screen_size=(1280, 936)))
],
)
@@ -0,0 +1,317 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
import time
from typing import Literal
from google.adk.tools.computer_use.base_computer import BaseComputer
from google.adk.tools.computer_use.base_computer import ComputerEnvironment
from google.adk.tools.computer_use.base_computer import ComputerState
from playwright.async_api import async_playwright
import termcolor
from typing_extensions import override
# Define a mapping from the user-friendly key names to Playwright's expected key names.
# Playwright is generally good with case-insensitivity for these, but it's best to be canonical.
# See: https://playwright.dev/docs/api/class-keyboard#keyboard-press
# Keys like 'a', 'b', '1', '$' are passed directly.
PLAYWRIGHT_KEY_MAP = {
"backspace": "Backspace",
"tab": "Tab",
"return": "Enter", # Playwright uses 'Enter'
"enter": "Enter",
"shift": "Shift",
"control": "Control", # Or 'ControlOrMeta' for cross-platform Ctrl/Cmd
"alt": "Alt",
"escape": "Escape",
"space": "Space", # Can also just be " "
"pageup": "PageUp",
"pagedown": "PageDown",
"end": "End",
"home": "Home",
"left": "ArrowLeft",
"up": "ArrowUp",
"right": "ArrowRight",
"down": "ArrowDown",
"insert": "Insert",
"delete": "Delete",
"semicolon": ";", # For actual character ';'
"equals": "=", # For actual character '='
"multiply": "Multiply", # NumpadMultiply
"add": "Add", # NumpadAdd
"separator": "Separator", # Numpad specific
"subtract": "Subtract", # NumpadSubtract, or just '-' for character
"decimal": "Decimal", # NumpadDecimal, or just '.' for character
"divide": "Divide", # NumpadDivide, or just '/' for character
"f1": "F1",
"f2": "F2",
"f3": "F3",
"f4": "F4",
"f5": "F5",
"f6": "F6",
"f7": "F7",
"f8": "F8",
"f9": "F9",
"f10": "F10",
"f11": "F11",
"f12": "F12",
"command": "Meta", # 'Meta' is Command on macOS, Windows key on Windows
}
class PlaywrightComputer(BaseComputer):
"""Conputer that controls Chromium via Playwright."""
def __init__(
self,
screen_size: tuple[int, int],
initial_url: str = "https://www.google.com",
search_engine_url: str = "https://www.google.com",
highlight_mouse: bool = False,
):
self._initial_url = initial_url
self._screen_size = screen_size
self._search_engine_url = search_engine_url
self._highlight_mouse = highlight_mouse
@override
async def initialize(self):
print("Creating session...")
self._playwright = await async_playwright().start()
self._browser = await self._playwright.chromium.launch(
args=["--disable-blink-features=AutomationControlled"],
headless=False,
)
self._context = await self._browser.new_context(
viewport={
"width": self._screen_size[0],
"height": self._screen_size[1],
}
)
self._page = await self._context.new_page()
await self._page.goto(self._initial_url)
termcolor.cprint(
f"Started local playwright.",
color="green",
attrs=["bold"],
)
@override
async def environment(self):
return ComputerEnvironment.ENVIRONMENT_BROWSER
@override
async def close(self, exc_type, exc_val, exc_tb):
if self._context:
self._context.close()
try:
self._browser.close()
except Exception as e:
# Browser was already shut down because of SIGINT or such.
if (
"Browser.close: Connection closed while reading from the driver"
in str(e)
):
pass
else:
raise
self._playwright.stop()
async def open_web_browser(self) -> ComputerState:
return await self.current_state()
async def click_at(self, x: int, y: int):
await self.highlight_mouse(x, y)
await self._page.mouse.click(x, y)
await self._page.wait_for_load_state()
return await self.current_state()
async def hover_at(self, x: int, y: int):
await self.highlight_mouse(x, y)
await self._page.mouse.move(x, y)
await self._page.wait_for_load_state()
return await self.current_state()
async def type_text_at(
self,
x: int,
y: int,
text: str,
press_enter: bool = True,
clear_before_typing: bool = True,
) -> ComputerState:
await self.highlight_mouse(x, y)
await self._page.mouse.click(x, y)
await self._page.wait_for_load_state()
if clear_before_typing:
await self.key_combination(["Control", "A"])
await self.key_combination(["Delete"])
await self._page.keyboard.type(text)
await self._page.wait_for_load_state()
if press_enter:
await self.key_combination(["Enter"])
await self._page.wait_for_load_state()
return await self.current_state()
async def _horizontal_document_scroll(
self, direction: Literal["left", "right"]
) -> ComputerState:
# Scroll by 50% of the viewport size.
horizontal_scroll_amount = await self.screen_size()[0] // 2
if direction == "left":
sign = "-"
else:
sign = ""
scroll_argument = f"{sign}{horizontal_scroll_amount}"
# Scroll using JS.
await self._page.evaluate(f"window.scrollBy({scroll_argument}, 0); ")
await self._page.wait_for_load_state()
return await self.current_state()
async def scroll_document(
self, direction: Literal["up", "down", "left", "right"]
) -> ComputerState:
if direction == "down":
return await self.key_combination(["PageDown"])
elif direction == "up":
return await self.key_combination(["PageUp"])
elif direction in ("left", "right"):
return await self._horizontal_document_scroll(direction)
else:
raise ValueError("Unsupported direction: ", direction)
async def scroll_at(
self,
x: int,
y: int,
direction: Literal["up", "down", "left", "right"],
magnitude: int,
) -> ComputerState:
await self.highlight_mouse(x, y)
await self._page.mouse.move(x, y)
await self._page.wait_for_load_state()
dx = 0
dy = 0
if direction == "up":
dy = -magnitude
elif direction == "down":
dy = magnitude
elif direction == "left":
dx = -magnitude
elif direction == "right":
dx = magnitude
else:
raise ValueError("Unsupported direction: ", direction)
await self._page.mouse.wheel(dx, dy)
await self._page.wait_for_load_state()
return await self.current_state()
async def wait(self, seconds: int) -> ComputerState:
await asyncio.sleep(seconds)
return await self.current_state()
async def go_back(self) -> ComputerState:
await self._page.go_back()
await self._page.wait_for_load_state()
return await self.current_state()
async def go_forward(self) -> ComputerState:
await self._page.go_forward()
await self._page.wait_for_load_state()
return await self.current_state()
async def search(self) -> ComputerState:
return await self.navigate(self._search_engine_url)
async def navigate(self, url: str) -> ComputerState:
await self._page.goto(url)
await self._page.wait_for_load_state()
return await self.current_state()
async def key_combination(self, keys: list[str]) -> ComputerState:
# Normalize all keys to the Playwright compatible version.
keys = [PLAYWRIGHT_KEY_MAP.get(k.lower(), k) for k in keys]
for key in keys[:-1]:
await self._page.keyboard.down(key)
await self._page.keyboard.press(keys[-1])
for key in reversed(keys[:-1]):
await self._page.keyboard.up(key)
return await self.current_state()
async def drag_and_drop(
self, x: int, y: int, destination_x: int, destination_y: int
) -> ComputerState:
await self.highlight_mouse(x, y)
await self._page.mouse.move(x, y)
await self._page.wait_for_load_state()
await self._page.mouse.down()
await self._page.wait_for_load_state()
await self.highlight_mouse(destination_x, destination_y)
await self._page.mouse.move(destination_x, destination_y)
await self._page.wait_for_load_state()
await self._page.mouse.up()
return await self.current_state()
async def current_state(self) -> ComputerState:
await self._page.wait_for_load_state()
# Even if Playwright reports the page as loaded, it may not be so.
# Add a manual sleep to make sure the page has finished rendering.
time.sleep(0.5)
screenshot_bytes = await self._page.screenshot(type="png", full_page=False)
return ComputerState(screenshot=screenshot_bytes, url=self._page.url)
async def screen_size(self) -> tuple[int, int]:
return self._screen_size
async def highlight_mouse(self, x: int, y: int):
if not self._highlight_mouse:
return
await self._page.evaluate(f"""
() => {{
const element_id = "playwright-feedback-circle";
const div = document.createElement('div');
div.id = element_id;
div.style.pointerEvents = 'none';
div.style.border = '4px solid red';
div.style.borderRadius = '50%';
div.style.width = '20px';
div.style.height = '20px';
div.style.position = 'fixed';
div.style.zIndex = '9999';
document.body.appendChild(div);
div.hidden = false;
div.style.left = {x} - 10 + 'px';
div.style.top = {y} - 10 + 'px';
setTimeout(() => {{
div.hidden = true;
}}, 2000);
}}
""")
# Wait a bit for the user to see the cursor.
time.sleep(1)
@@ -0,0 +1,4 @@
termcolor==3.1.0
playwright==1.52.0
browserbase==1.3.0
rich

Some files were not shown because too many files have changed in this diff Show More