Compare commits

..

23 Commits

Author SHA1 Message Date
Google Team Member f13a11e1dc feat: Propagate application_name set for the BigQuery Tools as BigQuery job labels
This change will help the tools user identify per agent job usage in BQ console and INFORMATION_SCHEMA views. This change fulfills the feature request #3582. Here is a demo after change: screen/C6YB4ge2FM2ZREi.

PiperOrigin-RevId: 834480140
2025-11-19 16:02:32 -08:00
George Weale 131d39c3db fix: Change name for builder agent
When this agent was built before the agent name was changed in the build script and accepted like that from the VB so changed to set into that form

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834479335
2025-11-19 16:00:51 -08:00
George Weale 12db84f5cd fix: Remove app name from FileArtifactService directory structure
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834462462
2025-11-19 15:12:48 -08:00
Ankur Sharma dc3f60cc93 chore: Plumb memory service from LocalEvalService to EvaluationGenerator
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 834398581
2025-11-19 12:28:52 -08:00
Yifan Wang 14e3802643 chore: update adk web to match main branch
Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 834378696
2025-11-19 11:37:29 -08:00
Shangjie Chen ffbab4cf4e chore: Add BigQuery related label handling
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 834375503
2025-11-19 11:29:40 -08:00
George Weale b2b7f2d6aa chore: Move adk_agent_builder_assistant to built_in_agents
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834371390
2025-11-19 11:18:37 -08:00
Hangfei Lin 3ad30a58f9 fix: Add transcription fields to session events
This change introduces `input_transcription` and `output_transcription` fields to session events, enabling the storage and retrieval of transcription data in both the database and Vertex AI session services.

Closes #3172

Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 834366848
2025-11-19 11:08:38 -08:00
George Weale 0ac35b23dc docs: Add path sanitization for model-generated file paths
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 834344352
2025-11-19 10:13:16 -08:00
Mimi Sun 857de04deb feat: update save_files_as_artifacts_plugin to never keep inline data
PiperOrigin-RevId: 834328794
2025-11-19 09:36:42 -08:00
Max Ind e15e19da05 fix: remove hardcoded google-cloud-aiplatform version in agent engine requirements
This fixes e.g. `--trace_to_cloud flag`

Co-authored-by: Max Ind <maxind@google.com>
PiperOrigin-RevId: 834190152
2025-11-19 02:01:34 -08:00
Michael Jones 0cc3d6d6d5 Feat/expose mcps streamable http custom httpx factory parameter (#2997)
* feat: Add support for custom HTTPX client factory in StreamableHTTPConnectionParams

* Update src/google/adk/tools/mcp_tool/mcp_session_manager.py

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* unit tested mock

* provide default - httpx client factory can't be none

* feat: Enhance StreamableHTTPConnectionParams with httpx_client_factory attribute

* fmt

* fmt

* refactor: Rename test_init_with_streamable_http_none_httpx_factory to test_init_with_streamable_http_default_httpx_factory for clarity

* isort

* fmt

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Kathy Wu <108756731+wukath@users.noreply.github.com>
2025-11-18 11:09:48 -08:00
Hangfei Lin b5f5df9fa8 fix(runners): Ensure event compaction completes by awaiting task
Fixes https://github.com/google/adk-python/issues/3174

The event compaction process, configured via `EventsCompactionConfig`, was
previously scheduled as a background task using `asyncio.create_task`.
Because Python's `asyncio.create_task` only holds a weak reference to
the created task and no strong reference was maintained by ADK, the
compaction task could be garbage-collected before it finished executing.
This resulted in event compaction failing silently or only partially
running, preventing session history from being summarized.

### Approaches Considered

Two approaches were considered to fix this:

1.  **`asyncio.create_task` + Reference:** Create the task with `create_task` and store a strong reference to it (e.g., in a `set` on the [Runner](http://_vscodecontentref_/0) instance), removing it only when complete via `task.add_done_callback()`.
    *   **Pros:** The [run_async](http://_vscodecontentref_/1) async generator finishes immediately after yielding the last agent event.
    *   **Cons:** Adds complexity to the Runner state; background task failures are silent to the [run_async](http://_vscodecontentref_/2) caller; requires enhancement to `runner.close()` to correctly manage pending tasks on shutdown.
2.  **`await`:** Directly `await` the compaction coroutine at the end of [run_async](http://_vscodecontentref_/3) after all agent events have been yielded.
    *   **Pros:** Simple to implement; ensures compaction runs to completion; failures during compaction propagate immediately to the [run_async](http://_vscodecontentref_/4) caller, making them visible and easier to debug.
    *   **Cons:** The `async for` loop iterating over [run_async](http://_vscodecontentref_/5) will not terminate until compaction finishes.

### Decision

This change implements the `await` approach. Although it means the `async for` loop takes longer to terminate when compaction occurs, it was chosen for its **simplicity and robustness**. Ensuring that compaction either succeeds or fails visibly is preferable to silent background failures.

All agent response events are yielded *before* compaction starts, so there is **no user-perceived delay in receiving the agent's answer** for the current turn.

### Integration Note for Users

Because compaction is now awaited, code consuming events via `async for event in runner.run_async(...)` will only finish iterating *after* compaction is complete (if compaction is triggered for that invocation).

If your application only needs the agent's response to proceed (e.g., displaying a message in a UI and allowing the user to reply), you can process events as they arrive and initiate the next turn without waiting for the `async for` loop to fully terminate. A new call to [run_async](http://_vscodecontentref_/6) for the next user query can be made immediately and will execute concurrently.

**Example:**

```python
async def handle_agent_turn(runner, message):
    print("Agent is thinking...")
    async for event in runner.run_async(user_id='...', session_id='...', new_message=message):
        # Stream events to UI, log, etc.
        if event.author == 'model' and event.content and event.content.parts[0].text:
            print(f"Agent response: {event.content.parts[0].text}")
            # The agent has provided a text response.
            # The application can now enable user input for the next turn,
            # even though this async for loop might not finish immediately
            # if compaction is running.
    print("Invocation complete (including compaction if any).")

# In your application:
# A new call to handle_agent_turn(runner, next_message) can be made
# as soon as the user provides the next input, without waiting for
# the previous call's generator to be exhausted.

Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 833885375
2025-11-18 10:57:50 -08:00
George Weale a12ae812d3 feat: Add service factory for configurable session and artifact backends
this creates service_factory to handle .adk folder changes (including per-agent .adk defaults and in-memory/custom URI handling)

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 833875524
2025-11-18 10:35:31 -08:00
Shangjie Chen 8eb1bdbc58 chore: Add demo for rewind
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 833871446
2025-11-18 10:26:15 -08:00
George Weale 236f562cd2 fix: Load agent/app before creating session
This change loads the agent or app from the specified directory before creating the session. This allows using the correct application name (from the `App` object if applicable) when initializing the session, rather than always defaulting to the folder name. The variable `root_agent` is also renamed to `agent_or_app` to better reflect that it can be either an Agent or an App

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 833839070
2025-11-18 09:09:22 -08:00
Google Team Member 4dd28a3970 feat: Add id and custom_metadata fields to MemoryEntry
PiperOrigin-RevId: 833581243
2025-11-17 18:34:03 -08:00
Ankur Sharma b2c45f8d91 chore: Enhance the messaging with possible fixes for RESOURCE_EXHAUSTED errors from Gemini
Co-authored-by: Ankur Sharma <ankusharma@google.com>
PiperOrigin-RevId: 833538475
2025-11-17 16:15:56 -08:00
Google Team Member 5ac5129fb0 feat: Enhance BQ Plugin Schema, Error Handling, and Logging
This update enhances the BigQuery agent analytics plugin:

*   **Enhanced Error Logging:** Improved error messages for schema mismatches.
*   **Reordered Logging Content:** Prioritized metadata in `before_model_callback`.

PiperOrigin-RevId: 833508755
2025-11-17 15:00:38 -08:00
Google Team Member c642f13f21 feat: Thread custom_metadata through forwarding artifact service
PiperOrigin-RevId: 833496193
2025-11-17 14:25:30 -08:00
Kathy Wu a48a1a9e88 fix: Improve logic for checking if a MCP session is disconnected
Currently logic to check for a disconnected session only checks for certain headers but doesn't detect all cases, leading to situations where it tries to connect to a session that is down. This adds logic so that we ping the server to check if it is disconnected. Fixes https://github.com/google/adk-python/issues/3321.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 833487825
2025-11-17 14:05:02 -08:00
Xuan Yang a5ac1d5e14 feat: Add progressive SSE streaming feature
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 833483804
2025-11-17 13:55:38 -08:00
George Weale 0ec01956e8 fix: keep vertex session event history intact
Close #3504

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 833417574
2025-11-17 11:13:08 -08:00
62 changed files with 5823 additions and 4600 deletions
+252 -200
View File
File diff suppressed because it is too large Load Diff
@@ -39,6 +39,7 @@ LABEL_TO_OWNER = {
"core": "Jacksunwei",
"web": "wyf7107",
"a2a": "seanzhou1023",
"bq": "shobsi",
}
LABEL_GUIDELINES = """
@@ -63,6 +64,7 @@ LABEL_GUIDELINES = """
sandbox, `agent_engine_id`). If the issue does not explicitly mention
Agent Engine concepts, do not use this label—choose "core" instead.
- "a2a": Agent-to-agent workflows, coordination logic, or A2A protocol.
- "bq": BigQuery integration or general issues related to BigQuery.
When unsure between labels, prefer the most specific match. If a label
cannot be assigned confidently, do not call the labeling tool.
@@ -214,6 +216,7 @@ root_agent = Agent(
- Use "agent engine" only when the issue clearly references Vertex AI Agent Engine deployment artifacts (for example `.agent_engine_config.json`, `ae_ignore`, `agent_engine_id`, or Agent Engine sandbox errors).
- If it's about Model Context Protocol (e.g. MCP tool, MCP toolset, MCP session management etc.), label it with both "mcp" and "tools".
- If it's about A2A integrations or workflows, label it with "a2a".
- If it's about BigQuery integrations, label it with "bq".
- If you can't find an appropriate labels for the issue, follow the previous instruction that starts with "IMPORTANT:".
Call the `add_label_and_owner_to_issue` tool to label the issue, which will also assign the issue to the owner of the label.
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env python3
"""Simple test script for Rewind Session agent."""
# 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 logging
import agent
from google.adk.agents.run_config import RunConfig
from google.adk.cli.utils import logs
from google.adk.events.event import Event
from google.adk.runners import InMemoryRunner
from google.genai import types
APP_NAME = "rewind_test_app"
USER_ID = "test_user"
logs.setup_adk_logger(level=logging.ERROR)
logging.getLogger("google_genai.types").setLevel(logging.ERROR)
# ANSI color codes for terminal output
COLOR_RED = "\x1b[31m"
COLOR_BLUE = "\x1b[34m"
COLOR_YELLOW = "\x1b[33m"
COLOR_BOLD = "\x1b[1m"
RESET = "\x1b[0m"
def highlight(text: str) -> str:
"""Adds color highlights to tool responses and agent text."""
text = str(text)
return (
text.replace("'red'", f"'{COLOR_RED}red{RESET}'")
.replace('"red"', f'"{COLOR_RED}red{RESET}"')
.replace("'blue'", f"'{COLOR_BLUE}blue{RESET}'")
.replace('"blue"', f'"{COLOR_BLUE}blue{RESET}"')
.replace("'version1'", f"'{COLOR_BOLD}{COLOR_YELLOW}version1{RESET}'")
.replace("'version2'", f"'{COLOR_BOLD}{COLOR_YELLOW}version2{RESET}'")
)
async def call_agent_async(
runner: InMemoryRunner, user_id: str, session_id: str, prompt: str
) -> list[Event]:
"""Helper function to call the agent and return events."""
print(f"\n👤 User: {prompt}")
content = types.Content(
role="user", parts=[types.Part.from_text(text=prompt)]
)
events = []
try:
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=content,
run_config=RunConfig(),
):
events.append(event)
if event.content and event.author and event.author != "user":
for part in event.content.parts:
if part.text:
print(f" 🤖 Agent: {highlight(part.text)}")
elif part.function_call:
print(f" 🛠️ Tool Call: {part.function_call.name}")
elif part.function_response:
print(
" 📦 Tool Response:"
f" {highlight(part.function_response.response)}"
)
except Exception as e:
print(f"❌ Error during agent call: {e}")
raise
return events
async def main():
"""Demonstrates session rewind."""
print("🚀 Testing Rewind Session Feature")
print("=" * 50)
runner = InMemoryRunner(
agent=agent.root_agent,
app_name=APP_NAME,
)
# Create a session
session = await runner.session_service.create_session(
app_name=APP_NAME, user_id=USER_ID
)
print(f"Created session: {session.id}")
# 1. Initial agent calls to set state and artifact
print("\n\n===== INITIALIZING STATE AND ARTIFACT =====")
await call_agent_async(
runner, USER_ID, session.id, "set state `color` to red"
)
await call_agent_async(
runner, USER_ID, session.id, "save artifact file1 with content version1"
)
# 2. Check current state and artifact
print("\n\n===== STATE BEFORE UPDATE =====")
await call_agent_async(
runner, USER_ID, session.id, "what is the value of state `color`?"
)
await call_agent_async(runner, USER_ID, session.id, "load artifact file1")
# 3. Update state and artifact - THIS IS THE POINT WE WILL REWIND BEFORE
print("\n\n===== UPDATING STATE AND ARTIFACT =====")
events_update_state = await call_agent_async(
runner, USER_ID, session.id, "update state key color to blue"
)
rewind_invocation_id = events_update_state[0].invocation_id
print(f"Will rewind before invocation: {rewind_invocation_id}")
await call_agent_async(
runner, USER_ID, session.id, "save artifact file1 with content version2"
)
# 4. Check state and artifact after update
print("\n\n===== STATE AFTER UPDATE =====")
await call_agent_async(
runner, USER_ID, session.id, "what is the value of state key color?"
)
await call_agent_async(runner, USER_ID, session.id, "load artifact file1")
# 5. Perform rewind
print(f"\n\n===== REWINDING SESSION to before {rewind_invocation_id} =====")
await runner.rewind_async(
user_id=USER_ID,
session_id=session.id,
rewind_before_invocation_id=rewind_invocation_id,
)
print("✅ Rewind complete.")
# 6. Check state and artifact after rewind
print("\n\n===== STATE AFTER REWIND =====")
await call_agent_async(
runner, USER_ID, session.id, "what is the value of state `color`?"
)
await call_agent_async(runner, USER_ID, session.id, "load artifact file1")
print("\n" + "=" * 50)
print("✨ Rewind testing complete!")
print(
"🔧 If rewind was successful, color should be 'red' and file1 content"
" should contain 'version1' in the final check."
)
if __name__ == "__main__":
asyncio.run(main())
@@ -188,20 +188,18 @@ class FileArtifactService(BaseArtifactService):
# Storage layout matches the cloud and in-memory services:
# root/
# ── apps/
# └── {app_name}/
# ── users/
# └── {user_id}/
# ── sessions/
#└── {session_id}/
# │ │ └── artifacts/
#└── {artifact_path}/ # derived from filename
#└── versions/
#└── {version}/
# │ ├── {original_filename}
# │ │ └── metadata.json
# │ └── artifacts/
# │ └── {artifact_path}/...
# ── users/
# └── {user_id}/
# ── sessions/
# └── {session_id}/
# ── artifacts/
# │ └── {artifact_path}/ # derived from filename
# └── versions/
# │ └── {version}/
# │ ├── {original_filename}
# │ └── metadata.json
# └── artifacts/
# └── {artifact_path}/...
#
# Artifact paths are derived from the provided filenames: separators create
# nested directories, and path traversal is rejected to keep the layout
@@ -217,19 +215,18 @@ class FileArtifactService(BaseArtifactService):
self.root_dir = Path(root_dir).expanduser().resolve()
self.root_dir.mkdir(parents=True, exist_ok=True)
def _base_root(self, app_name: str, user_id: str) -> Path:
"""Returns the artifacts root directory for an app/user combination."""
return self.root_dir / "apps" / app_name / "users" / user_id
def _base_root(self, user_id: str, /) -> Path:
"""Returns the artifacts root directory for a user."""
return self.root_dir / "users" / user_id
def _scope_root(
self,
app_name: str,
user_id: str,
session_id: Optional[str],
filename: str,
) -> Path:
"""Returns the directory that represents the artifact scope."""
base = self._base_root(app_name, user_id)
base = self._base_root(user_id)
if _is_user_scoped(session_id, filename):
return _user_artifacts_dir(base)
if not session_id:
@@ -240,14 +237,12 @@ class FileArtifactService(BaseArtifactService):
def _artifact_dir(
self,
app_name: str,
user_id: str,
session_id: Optional[str],
filename: str,
) -> Path:
"""Builds the directory path for an artifact."""
scope_root = self._scope_root(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -258,7 +253,6 @@ class FileArtifactService(BaseArtifactService):
def _build_artifact_version(
self,
*,
app_name: str,
user_id: str,
session_id: Optional[str],
filename: str,
@@ -270,7 +264,6 @@ class FileArtifactService(BaseArtifactService):
metadata.canonical_uri
if metadata and metadata.canonical_uri
else self._canonical_uri(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -289,7 +282,6 @@ class FileArtifactService(BaseArtifactService):
def _canonical_uri(
self,
*,
app_name: str,
user_id: str,
session_id: Optional[str],
filename: str,
@@ -297,7 +289,6 @@ class FileArtifactService(BaseArtifactService):
) -> str:
"""Builds the canonical file:// URI for an artifact payload."""
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -336,7 +327,6 @@ class FileArtifactService(BaseArtifactService):
"""
return await asyncio.to_thread(
self._save_artifact_sync,
app_name,
user_id,
filename,
artifact,
@@ -346,7 +336,6 @@ class FileArtifactService(BaseArtifactService):
def _save_artifact_sync(
self,
app_name: str,
user_id: str,
filename: str,
artifact: types.Part,
@@ -355,7 +344,6 @@ class FileArtifactService(BaseArtifactService):
) -> int:
"""Saves an artifact to disk and returns its version."""
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -386,7 +374,6 @@ class FileArtifactService(BaseArtifactService):
raise ValueError("Artifact must have either inline_data or text content.")
canonical_uri = self._canonical_uri(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -421,7 +408,6 @@ class FileArtifactService(BaseArtifactService):
) -> Optional[types.Part]:
return await asyncio.to_thread(
self._load_artifact_sync,
app_name,
user_id,
filename,
session_id,
@@ -430,7 +416,6 @@ class FileArtifactService(BaseArtifactService):
def _load_artifact_sync(
self,
app_name: str,
user_id: str,
filename: str,
session_id: Optional[str],
@@ -438,7 +423,6 @@ class FileArtifactService(BaseArtifactService):
) -> Optional[types.Part]:
"""Loads an artifact from disk."""
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -493,21 +477,19 @@ class FileArtifactService(BaseArtifactService):
) -> list[str]:
return await asyncio.to_thread(
self._list_artifact_keys_sync,
app_name,
user_id,
session_id,
)
def _list_artifact_keys_sync(
self,
app_name: str,
user_id: str,
session_id: Optional[str],
) -> list[str]:
"""Lists artifact filenames for the given session/user."""
filenames: set[str] = set()
base_root = self._base_root(app_name, user_id)
base_root = self._base_root(user_id)
if session_id:
session_root = _session_artifacts_dir(base_root, session_id)
@@ -550,7 +532,6 @@ class FileArtifactService(BaseArtifactService):
"""
await asyncio.to_thread(
self._delete_artifact_sync,
app_name,
user_id,
filename,
session_id,
@@ -558,13 +539,11 @@ class FileArtifactService(BaseArtifactService):
def _delete_artifact_sync(
self,
app_name: str,
user_id: str,
filename: str,
session_id: Optional[str],
) -> None:
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -585,7 +564,6 @@ class FileArtifactService(BaseArtifactService):
"""Lists all versions stored for an artifact."""
return await asyncio.to_thread(
self._list_versions_sync,
app_name,
user_id,
filename,
session_id,
@@ -593,13 +571,11 @@ class FileArtifactService(BaseArtifactService):
def _list_versions_sync(
self,
app_name: str,
user_id: str,
filename: str,
session_id: Optional[str],
) -> list[int]:
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -618,7 +594,6 @@ class FileArtifactService(BaseArtifactService):
"""Lists metadata for each artifact version on disk."""
return await asyncio.to_thread(
self._list_artifact_versions_sync,
app_name,
user_id,
filename,
session_id,
@@ -626,13 +601,11 @@ class FileArtifactService(BaseArtifactService):
def _list_artifact_versions_sync(
self,
app_name: str,
user_id: str,
filename: str,
session_id: Optional[str],
) -> list[ArtifactVersion]:
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -644,7 +617,6 @@ class FileArtifactService(BaseArtifactService):
metadata = _read_metadata(metadata_path)
artifact_versions.append(
self._build_artifact_version(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -667,7 +639,6 @@ class FileArtifactService(BaseArtifactService):
"""Gets metadata for a specific artifact version."""
return await asyncio.to_thread(
self._get_artifact_version_sync,
app_name,
user_id,
filename,
session_id,
@@ -676,14 +647,12 @@ class FileArtifactService(BaseArtifactService):
def _get_artifact_version_sync(
self,
app_name: str,
user_id: str,
filename: str,
session_id: Optional[str],
version: Optional[int],
) -> Optional[ArtifactVersion]:
artifact_dir = self._artifact_dir(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
@@ -701,7 +670,6 @@ class FileArtifactService(BaseArtifactService):
metadata_path = _metadata_path(artifact_dir, version_to_read)
metadata = _read_metadata(metadata_path)
return self._build_artifact_version(
app_name=app_name,
user_id=user_id,
session_id=session_id,
filename=filename,
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -18,9 +18,10 @@ This package provides an intelligent assistant for building multi-agent systems
using YAML configurations. It can be used directly as an agent or integrated
with ADK tools and web interfaces.
"""
from __future__ import annotations
from . import agent # Import to make agent.root_agent available
from .agent_builder_assistant import AgentBuilderAssistant
from .adk_agent_builder_assistant import AgentBuilderAssistant
__all__ = [
'AgentBuilderAssistant',
@@ -13,6 +13,7 @@
# limitations under the License.
"""Agent factory for creating Agent Builder Assistant with embedded schema."""
from __future__ import annotations
from pathlib import Path
import textwrap
@@ -415,3 +416,8 @@ class AgentBuilderAssistant:
with open(template_path, "r", encoding="utf-8") as f:
return f.read()
# Expose a module-level root_agent so the AgentLoader can find this built-in
# assistant when requested as "__adk_agent_builder_assistant".
root_agent = AgentBuilderAssistant.create_agent()
@@ -13,8 +13,9 @@
# limitations under the License.
"""Agent Builder Assistant instance for ADK web testing."""
from __future__ import annotations
from .agent_builder_assistant import AgentBuilderAssistant
from .adk_agent_builder_assistant import AgentBuilderAssistant
# Create the agent instance using the factory
# The root_agent variable is what ADK looks for when loading agents
@@ -75,6 +75,9 @@ Always reference this schema when creating configurations to ensure compliance.
- **CRITICAL TIMING**: Ask for model selection IMMEDIATELY after determining LlmAgent is needed, BEFORE presenting any design
- **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.
- **ALLOWED MODELS ONLY**: Only mention or propose "gemini-2.5-flash" or
"gemini-2.5-pro". Treat any request for gemini-1.5-* or older models as
unsupported and redirect to one of the 2.5 options.
- **RATIONALE**: Only LlmAgent requires model specification; workflow agents do not
- **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"
@@ -13,6 +13,7 @@
# limitations under the License.
"""Sub-agents for Agent Builder Assistant."""
from __future__ import annotations
from .google_search_agent import create_google_search_agent
from .url_context_agent import create_url_context_agent
@@ -13,6 +13,7 @@
# limitations under the License.
"""Sub-agent for Google Search functionality."""
from __future__ import annotations
from google.adk.agents import LlmAgent
from google.adk.tools import google_search
@@ -13,6 +13,7 @@
# limitations under the License.
"""Sub-agent for URL context fetching functionality."""
from __future__ import annotations
from google.adk.agents import LlmAgent
from google.adk.tools import url_context
@@ -13,6 +13,7 @@
# limitations under the License.
"""Tools for Agent Builder Assistant."""
from __future__ import annotations
from .cleanup_unused_files import cleanup_unused_files
from .delete_files import delete_files
@@ -13,6 +13,7 @@
# limitations under the License.
"""File deletion tool for Agent Builder Assistant."""
from __future__ import annotations
from datetime import datetime
from pathlib import Path

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