|
| 1 | +# Copyright 2026 Google LLC |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | +# |
| 15 | +"""Built-in tool catalog for Gemini Agent evaluation display. |
| 16 | +
|
| 17 | +The Gemini Agents API (``GET agents/{id}``) returns each tool as a bare type |
| 18 | +discriminator (e.g. ``{"type": "code_execution"}``) with no parameter schema |
| 19 | +or description. The authoritative, full-fidelity expansion lives server-side |
| 20 | +in ``cloud/ai/platform/evaluation/utils/interaction_converter.py``. |
| 21 | +
|
| 22 | +This module is a **display-only duplicate** of that server catalog, kept here |
| 23 | +so ``show()`` can render tools with full names and descriptions without a |
| 24 | +server round-trip. Parameter schemas are intentionally omitted to avoid |
| 25 | +publishing internal tool contract details. |
| 26 | +
|
| 27 | +**If the server catalog changes, this SDK-side copy must be updated to match.** |
| 28 | +""" |
| 29 | + |
| 30 | +from typing import Any, Optional |
| 31 | + |
| 32 | +from google.genai import types as genai_types |
| 33 | + |
| 34 | + |
| 35 | +# Maps a built-in Gemini Agent tool type to the concrete FunctionDeclarations |
| 36 | +# the agent actually exposes for that type. |
| 37 | +# |
| 38 | +# Source of truth: interaction_converter.py, _BUILTIN_TOOL_FUNCTION_DECLARATIONS |
| 39 | +BUILTIN_TOOL_DECLARATIONS: dict[str, list[genai_types.FunctionDeclaration]] = { |
| 40 | + "code_execution": [ |
| 41 | + genai_types.FunctionDeclaration( |
| 42 | + name="run_command", |
| 43 | + description="Runs a shell command on the sandbox VM.", |
| 44 | + ), |
| 45 | + ], |
| 46 | + "filesystem": [ |
| 47 | + genai_types.FunctionDeclaration( |
| 48 | + name="view_file", |
| 49 | + description="Reads the content of a workspace file.", |
| 50 | + ), |
| 51 | + genai_types.FunctionDeclaration( |
| 52 | + name="create_file", |
| 53 | + description="Writes content to a new or existing file.", |
| 54 | + ), |
| 55 | + genai_types.FunctionDeclaration( |
| 56 | + name="edit_file", |
| 57 | + description="Replaces a specific block of text in a file.", |
| 58 | + ), |
| 59 | + genai_types.FunctionDeclaration( |
| 60 | + name="list_dir", |
| 61 | + description="Lists the files in a directory.", |
| 62 | + ), |
| 63 | + genai_types.FunctionDeclaration( |
| 64 | + name="delete_file", |
| 65 | + description="Removes a file from the workspace.", |
| 66 | + ), |
| 67 | + genai_types.FunctionDeclaration( |
| 68 | + name="move_file", |
| 69 | + description="Renames or moves a file.", |
| 70 | + ), |
| 71 | + ], |
| 72 | +} |
| 73 | + |
| 74 | + |
| 75 | +# Sandbox-environment orchestration tool declarations. |
| 76 | +# |
| 77 | +# Source of truth: interaction_converter.py, _SANDBOX_FUNCTION_DECLARATIONS |
| 78 | +SANDBOX_DECLARATIONS: list[genai_types.FunctionDeclaration] = [ |
| 79 | + genai_types.FunctionDeclaration( |
| 80 | + name="provision_sandbox", |
| 81 | + description="Provisions a sandbox environment.", |
| 82 | + ), |
| 83 | + genai_types.FunctionDeclaration( |
| 84 | + name="load_sandbox", |
| 85 | + description="Loads a previously provisioned sandbox environment.", |
| 86 | + ), |
| 87 | +] |
| 88 | + |
| 89 | + |
| 90 | +def agent_tools_to_config_tools( |
| 91 | + agent_tools: Optional[list[Any]], |
| 92 | + has_environment: bool = False, |
| 93 | +) -> Optional[list[genai_types.Tool]]: |
| 94 | + """Maps Gemini Agents API tools to ``genai_types.Tool`` for display. |
| 95 | +
|
| 96 | + Expands built-in agent tool types into their concrete function declarations |
| 97 | + using ``BUILTIN_TOOL_DECLARATIONS`` (a display-only duplicate of the |
| 98 | + server-side catalog in ``interaction_converter.py``). |
| 99 | +
|
| 100 | + Mapping rules: |
| 101 | + * ``code_execution`` is expanded to ``run_command``. |
| 102 | + * ``filesystem`` is expanded to ``view_file``, ``create_file``, |
| 103 | + ``edit_file``, ``list_dir``, ``delete_file``, ``move_file``. |
| 104 | + * ``google_search`` and ``url_context`` are mapped to their typed |
| 105 | + ``genai_types.Tool`` variant. |
| 106 | + * ``mcp_server`` is represented as a named declaration with a |
| 107 | + human-readable label. |
| 108 | + * Tools carrying explicit ``function_declarations`` are passed through. |
| 109 | + * When ``has_environment`` is True, sandbox orchestration tools |
| 110 | + (``provision_sandbox``, ``load_sandbox``) are appended. |
| 111 | +
|
| 112 | + Args: |
| 113 | + agent_tools: The ``tools`` list from a fetched Gemini agent dict. |
| 114 | + has_environment: Whether the agent has a sandbox environment configured. |
| 115 | +
|
| 116 | + Returns: |
| 117 | + A list of ``genai_types.Tool``, or ``None`` if there are no mappable |
| 118 | + tools. |
| 119 | + """ |
| 120 | + if not agent_tools and not has_environment: |
| 121 | + return None |
| 122 | + tools: list[genai_types.Tool] = [] |
| 123 | + for tool in agent_tools or []: |
| 124 | + if not isinstance(tool, dict): |
| 125 | + continue |
| 126 | + tool_type = tool.get("type") |
| 127 | + remainder = {k: v for k, v in tool.items() if k != "type"} |
| 128 | + |
| 129 | + # Check the built-in catalog first (code_execution, filesystem). |
| 130 | + catalog_decls = BUILTIN_TOOL_DECLARATIONS.get(tool_type or "") |
| 131 | + if catalog_decls: |
| 132 | + tools.append(genai_types.Tool(function_declarations=list(catalog_decls))) |
| 133 | + elif tool_type == "google_search": |
| 134 | + tools.append(genai_types.Tool(google_search=genai_types.GoogleSearch())) |
| 135 | + elif tool_type == "url_context": |
| 136 | + tools.append(genai_types.Tool(url_context=genai_types.UrlContext())) |
| 137 | + elif "function_declarations" in remainder: |
| 138 | + # Real function tool with explicit declarations. |
| 139 | + tools.append(genai_types.Tool.model_validate(remainder)) |
| 140 | + elif tool_type == "mcp_server": |
| 141 | + label = remainder.get("name") or remainder.get("url") |
| 142 | + description = f"MCP server: {label}" if label else "MCP server." |
| 143 | + tools.append( |
| 144 | + genai_types.Tool( |
| 145 | + function_declarations=[ |
| 146 | + genai_types.FunctionDeclaration( |
| 147 | + name="mcp_server", description=description |
| 148 | + ) |
| 149 | + ] |
| 150 | + ) |
| 151 | + ) |
| 152 | + elif tool_type: |
| 153 | + # Unknown built-in: show by name so it isn't silently dropped. |
| 154 | + tools.append( |
| 155 | + genai_types.Tool( |
| 156 | + function_declarations=[ |
| 157 | + genai_types.FunctionDeclaration(name=tool_type) |
| 158 | + ] |
| 159 | + ) |
| 160 | + ) |
| 161 | + elif remainder: |
| 162 | + tools.append(genai_types.Tool.model_validate(remainder)) |
| 163 | + |
| 164 | + if has_environment: |
| 165 | + tools.append(genai_types.Tool(function_declarations=list(SANDBOX_DECLARATIONS))) |
| 166 | + |
| 167 | + return tools or None |
0 commit comments