From 08565985a99fbf06cc8f7dbca5f1da82e38194df Mon Sep 17 00:00:00 2001 From: Mohab Fekry Date: Tue, 17 Feb 2026 10:55:09 +0100 Subject: [PATCH 1/5] fix: harden file handling for brand_aligner_agent Move GCS URI construction to tools rather than agent instructions, further reducing cognitive load and ensuring reliable GCS file logic. --- .../brand-aligner/brand_aligner_agent/agent.py | 13 ++++++------- .../brand-aligner/brand_aligner_agent/tools.py | 18 +++++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/python/agents/brand-aligner/brand_aligner_agent/agent.py b/python/agents/brand-aligner/brand_aligner_agent/agent.py index a017dbefa..95c51f5f8 100644 --- a/python/agents/brand-aligner/brand_aligner_agent/agent.py +++ b/python/agents/brand-aligner/brand_aligner_agent/agent.py @@ -60,12 +60,11 @@ 1. Announce to the user that you are starting the asset evaluation step. For example: "Step 2: Evaluating Assets..." 2. Iterate through the `session.state['asset_files']` list. For EACH asset file: a. Prepare an asset dictionary containing: - * `asset_uri`: The GCS URI of the asset. - * `asset_name`: The name of the asset file extracted from the URI. + * `asset_name`: The name of the asset file from the state. * `asset_id`: A string representation of the index of the current asset (e.g., '1', '2', '3'). - * `category`: 'IMAGE' or 'VIDEO' based on filename/type. + * `category`: 'IMAGE' or 'VIDEO' based on file name / type. * `asset_prompt`: User provided prompt or empty string. - * `video_reference_image_uris`: Reference image URIs for video assets (if any), else empty list. + * `video_reference_image_files`: Reference image file names for video assets (if any), else empty list. b. Call `asset_evaluator_tool` with this single asset dictionary. c. The tool will return a formatted Markdown report. d. Present the report to the user. @@ -87,7 +86,7 @@ **WORKFLOW:** 1. Announce to the user that you are starting the guideline processing step. For example: "Step 1: Processing Guidelines..." 2. Iterate through `session.state['guideline_files']`. For EACH file: - a. Call `guideline_processor_tool` with the single GCS URI. + a. Call `guideline_processor_tool` with the file name from the state. b. The tool will return a formatted Markdown report. c. Present the report to the user. 3. After processing ALL guidelines, call the `asset_evaluator_agent` to proceed to the next step. @@ -115,7 +114,7 @@ 2. **Handle and Categorize Files:** * **File Handling:** After the user uploads files or asks to find existing files, your first step is to call `save_artifacts_to_gcs_tool` to save any in-session files to persistent storage. After that, call `search_user_files_tool` to get a complete list of all user files. - * **Display:** The tool returns a list of dictionaries, each containing `filename` (for display) and `uri` (for execution). ALWAYS use the `filename` when talking to the user. ALWAYS use the `uri` when calling tools or saving state. + * **Display:** The tool returns a list of file names which you can display to the user. * **Confirmation:** After retrieving the complete list of files, classify them as **guideline files** or **asset files** depending on the file extension (PDF, MD and TEXT files are guidelines, while image and video files are assets), then interact with the user to confirm the exact list of **guideline files** and **asset files** to be used in the evaluation. 3. **Create and Confirm Plan:** @@ -128,7 +127,7 @@ 4. Provide a final summary." 4. **Store Plan:** - * After user confirmation, call `save_plan_to_state_tool` with the categorized lists of GCS URIs. + * After user confirmation, call `save_plan_to_state_tool` with the categorized lists of file names. 5. **Execute Plan:** * If there are assets to evaluate, your final step is to call the `guideline_processor_agent` sub-agent to begin the execution chain. diff --git a/python/agents/brand-aligner/brand_aligner_agent/tools.py b/python/agents/brand-aligner/brand_aligner_agent/tools.py index 73581ccbc..2f9c0019d 100644 --- a/python/agents/brand-aligner/brand_aligner_agent/tools.py +++ b/python/agents/brand-aligner/brand_aligner_agent/tools.py @@ -262,7 +262,7 @@ async def save_artifacts_to_gcs_tool(tool_context: ToolContext) -> str: async def search_user_files_tool( tool_context: ToolContext, -) -> dict[str, list[dict[str, str]]]: +) -> dict[str, list[str]]: """Lists all available files for the current user.""" continue_processing, user_id = _get_user_id(tool_context) if not continue_processing: @@ -272,7 +272,7 @@ async def search_user_files_tool( logger.info("Searching for files in GCS with prefix: %s", prefix) blobs = storage_client.list_blobs(GCS_BUCKET, prefix=prefix, delimiter="/") files = [ - {"filename": os.path.basename(blob.name), "uri": blob.name} + os.path.basename(blob.name) for blob in blobs if not blob.name.endswith("/") and not blob.name.startswith(f"{prefix}processed_guideline_") @@ -410,13 +410,14 @@ def _format_guideline_string(guideline_obj: Guideline) -> str: async def guideline_processor_tool( - gcs_uri: str, tool_context: ToolContext + filename: str, tool_context: ToolContext ) -> str: """Processes a single guideline file from GCS and returns a formatted report.""" continue_processing, user_id = _get_user_id(tool_context) if not continue_processing: return user_id # This is the pending auth message + gcs_uri = f"{tool_context.session.app_name}/{user_id}/{user_id}/{filename}" result = await _process_single_guideline(user_id, gcs_uri, tool_context) guideline_data = result["guideline"] @@ -606,18 +607,21 @@ async def asset_evaluator_tool( if not continue_processing: return user_id # This is the pending auth message + prefix = f"{tool_context.session.app_name}/{user_id}/{user_id}" + tasks = [ _evaluate_single_asset( tool_context=tool_context, user_id=user_id, - asset_uri=asset["asset_uri"], + asset_uri=f"{prefix}/{asset['asset_name']}", asset_name=asset["asset_name"], asset_id=asset["asset_id"], category=asset["category"], asset_prompt=asset.get("asset_prompt", ""), - video_reference_image_uris=asset.get( - "video_reference_image_uris", [] - ), + video_reference_image_uris=[ + f"{prefix}/{filename}" + for filename in asset.get("video_reference_image_files", []) + ], ) for asset in assets ] From d4dd81ae178e85e18a242fa2ba2888b6cbfe2383 Mon Sep 17 00:00:00 2001 From: Mohab Fekry Date: Fri, 6 Mar 2026 20:15:02 +0100 Subject: [PATCH 2/5] fix: fix ruff --- .../agents/bigquery-data-agent/tests/test_agent.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/agents/bigquery-data-agent/tests/test_agent.py b/python/agents/bigquery-data-agent/tests/test_agent.py index 85391c0df..6385358ae 100644 --- a/python/agents/bigquery-data-agent/tests/test_agent.py +++ b/python/agents/bigquery-data-agent/tests/test_agent.py @@ -1,6 +1,6 @@ -import pytest from bigquery_data_agent.agent import app + def test_app_initialization(): """Test that the application and agent initialize correctly.""" assert app is not None @@ -12,18 +12,20 @@ def test_plugins_and_tools(): """Test that the BigQuery analytics plugin and BigQuery toolset are configured.""" # Ensure plugins are attached assert len(app.plugins) > 0 - + # Ensure BigQueryToolset is attached to the root agent assert len(app.root_agent.tools) > 0 def test_plugin_configuration(): """Test that the BigQuery analytics plugin is properly configured.""" - from google.adk.plugins.bigquery_agent_analytics_plugin import BigQueryAgentAnalyticsPlugin - + from google.adk.plugins.bigquery_agent_analytics_plugin import ( # noqa: PLC0415 + BigQueryAgentAnalyticsPlugin, + ) + # Assert that at least one plugin is the analytics plugin analytics_plugins = [p for p in app.plugins if isinstance(p, BigQueryAgentAnalyticsPlugin)] assert len(analytics_plugins) == 1 - + plugin = analytics_plugins[0] assert plugin.dataset_id is not None assert plugin.project_id is not None From 04a7f7c26e76b937a13f0f9b71d6fb7341276d41 Mon Sep 17 00:00:00 2001 From: Mohab Fekry Date: Fri, 6 Mar 2026 20:17:45 +0100 Subject: [PATCH 3/5] fix: revert changes to bigquery-data-agent --- python/agents/bigquery-data-agent/tests/test_agent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/agents/bigquery-data-agent/tests/test_agent.py b/python/agents/bigquery-data-agent/tests/test_agent.py index 6385358ae..ed2d6cd47 100644 --- a/python/agents/bigquery-data-agent/tests/test_agent.py +++ b/python/agents/bigquery-data-agent/tests/test_agent.py @@ -1,3 +1,5 @@ +import pytest + from bigquery_data_agent.agent import app @@ -18,7 +20,7 @@ def test_plugins_and_tools(): def test_plugin_configuration(): """Test that the BigQuery analytics plugin is properly configured.""" - from google.adk.plugins.bigquery_agent_analytics_plugin import ( # noqa: PLC0415 + from google.adk.plugins.bigquery_agent_analytics_plugin import ( BigQueryAgentAnalyticsPlugin, ) From 76a490172da930c64c46540f502f48c2820ba8fc Mon Sep 17 00:00:00 2001 From: Mohab Fekry Date: Fri, 6 Mar 2026 20:20:33 +0100 Subject: [PATCH 4/5] fix: revert changes to bigquery-data-agent --- .../bigquery-data-agent/tests/test_agent.py | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/python/agents/bigquery-data-agent/tests/test_agent.py b/python/agents/bigquery-data-agent/tests/test_agent.py index ed2d6cd47..d6d7da04e 100644 --- a/python/agents/bigquery-data-agent/tests/test_agent.py +++ b/python/agents/bigquery-data-agent/tests/test_agent.py @@ -1,33 +1,34 @@ import pytest - from bigquery_data_agent.agent import app def test_app_initialization(): - """Test that the application and agent initialize correctly.""" - assert app is not None - assert app.name == "bigquery_data_agent" - assert app.root_agent is not None - assert app.root_agent.name == "bigquery_data_agent" + """Test that the application and agent initialize correctly.""" + assert app is not None + assert app.name == "bigquery_data_agent" + assert app.root_agent is not None + assert app.root_agent.name == "bigquery_data_agent" + def test_plugins_and_tools(): - """Test that the BigQuery analytics plugin and BigQuery toolset are configured.""" - # Ensure plugins are attached - assert len(app.plugins) > 0 + """Test that the BigQuery analytics plugin and BigQuery toolset are configured.""" + # Ensure plugins are attached + assert len(app.plugins) > 0 + + # Ensure BigQueryToolset is attached to the root agent + assert len(app.root_agent.tools) > 0 - # Ensure BigQueryToolset is attached to the root agent - assert len(app.root_agent.tools) > 0 def test_plugin_configuration(): - """Test that the BigQuery analytics plugin is properly configured.""" - from google.adk.plugins.bigquery_agent_analytics_plugin import ( - BigQueryAgentAnalyticsPlugin, - ) - - # Assert that at least one plugin is the analytics plugin - analytics_plugins = [p for p in app.plugins if isinstance(p, BigQueryAgentAnalyticsPlugin)] - assert len(analytics_plugins) == 1 - - plugin = analytics_plugins[0] - assert plugin.dataset_id is not None - assert plugin.project_id is not None + """Test that the BigQuery analytics plugin is properly configured.""" + from google.adk.plugins.bigquery_agent_analytics_plugin import BigQueryAgentAnalyticsPlugin + + # Assert that at least one plugin is the analytics plugin + analytics_plugins = [ + p for p in app.plugins if isinstance(p, BigQueryAgentAnalyticsPlugin) + ] + assert len(analytics_plugins) == 1 + + plugin = analytics_plugins[0] + assert plugin.dataset_id is not None + assert plugin.project_id is not None From 1e3285442354a32b86f22ae5fbcbf7be0bada044 Mon Sep 17 00:00:00 2001 From: Mohab Fekry Date: Fri, 6 Mar 2026 20:22:14 +0100 Subject: [PATCH 5/5] fix: revert changes to bigquery-data-agent tests --- .../bigquery-data-agent/tests/test_agent.py | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/python/agents/bigquery-data-agent/tests/test_agent.py b/python/agents/bigquery-data-agent/tests/test_agent.py index d6d7da04e..85391c0df 100644 --- a/python/agents/bigquery-data-agent/tests/test_agent.py +++ b/python/agents/bigquery-data-agent/tests/test_agent.py @@ -1,34 +1,29 @@ import pytest from bigquery_data_agent.agent import app - def test_app_initialization(): - """Test that the application and agent initialize correctly.""" - assert app is not None - assert app.name == "bigquery_data_agent" - assert app.root_agent is not None - assert app.root_agent.name == "bigquery_data_agent" - + """Test that the application and agent initialize correctly.""" + assert app is not None + assert app.name == "bigquery_data_agent" + assert app.root_agent is not None + assert app.root_agent.name == "bigquery_data_agent" def test_plugins_and_tools(): - """Test that the BigQuery analytics plugin and BigQuery toolset are configured.""" - # Ensure plugins are attached - assert len(app.plugins) > 0 - - # Ensure BigQueryToolset is attached to the root agent - assert len(app.root_agent.tools) > 0 - + """Test that the BigQuery analytics plugin and BigQuery toolset are configured.""" + # Ensure plugins are attached + assert len(app.plugins) > 0 + + # Ensure BigQueryToolset is attached to the root agent + assert len(app.root_agent.tools) > 0 def test_plugin_configuration(): - """Test that the BigQuery analytics plugin is properly configured.""" - from google.adk.plugins.bigquery_agent_analytics_plugin import BigQueryAgentAnalyticsPlugin - - # Assert that at least one plugin is the analytics plugin - analytics_plugins = [ - p for p in app.plugins if isinstance(p, BigQueryAgentAnalyticsPlugin) - ] - assert len(analytics_plugins) == 1 - - plugin = analytics_plugins[0] - assert plugin.dataset_id is not None - assert plugin.project_id is not None + """Test that the BigQuery analytics plugin is properly configured.""" + from google.adk.plugins.bigquery_agent_analytics_plugin import BigQueryAgentAnalyticsPlugin + + # Assert that at least one plugin is the analytics plugin + analytics_plugins = [p for p in app.plugins if isinstance(p, BigQueryAgentAnalyticsPlugin)] + assert len(analytics_plugins) == 1 + + plugin = analytics_plugins[0] + assert plugin.dataset_id is not None + assert plugin.project_id is not None