feat(py/plugins/google-genai): Deep Research, Antigravity, and Lyria interaction models - #5856
feat(py/plugins/google-genai): Deep Research, Antigravity, and Lyria interaction models#5856cabljac wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for Google AI Interactions models, including the new Antigravity and Deep Research models, and refactors the existing Lyria model to use the Interactions API. It also centralizes model metadata in a new registry and adds comprehensive unit tests. The review feedback highlights a few important issues: ensuring that Pydantic models are dumped using aliases (by_alias=True) so that camelCase keys are preserved for the API, avoiding redundant dictionary wrapping, and copying the mutable module-level DEFAULT_ENVIRONMENT dictionary to prevent unintended side effects from shared state mutation.
| if config.file_search is not None: | ||
| tools.append({'type': 'file_search', **config.file_search.model_dump(exclude_none=True)}) | ||
| for mcp_server in config.mcp_servers or []: | ||
| tools.append({'type': 'mcp_server', **mcp_server.model_dump(exclude_none=True)}) |
There was a problem hiding this comment.
The FileSearchConfig and McpServerConfig models define an alias_generator to produce camelCase keys (e.g., fileSearchStoreNames, allowedTools) for the wire format. However, calling model_dump(exclude_none=True) without by_alias=True serializes these configurations with snake_case keys (e.g., file_search_store_names, allowed_tools). This will cause the Google AI Interactions API to reject or ignore these settings. Passing by_alias=True ensures they are correctly serialized to camelCase.
| if config.file_search is not None: | |
| tools.append({'type': 'file_search', **config.file_search.model_dump(exclude_none=True)}) | |
| for mcp_server in config.mcp_servers or []: | |
| tools.append({'type': 'mcp_server', **mcp_server.model_dump(exclude_none=True)}) | |
| if config.file_search is not None: | |
| tools.append({'type': 'file_search', **config.file_search.model_dump(by_alias=True, exclude_none=True)}) | |
| for mcp_server in config.mcp_servers or []: | |
| tools.append({'type': 'mcp_server', **mcp_server.model_dump(by_alias=True, exclude_none=True)}) |
There was a problem hiding this comment.
The suggestion would break these requests rather than fix them. This body goes over a raw HTTP client verbatim, and the whole create payload is snake_case (agent_config, previous_interaction_id, user_input, mime_type in the content blocks), so camelCasing only these two nested blocks would produce a mixed-casing body. The SDK's own FileSearchDict/McpServerDict types also take snake_case keys. The alias_generator on these models handles the input direction (camelCased request config), not serialization.
| if request.tools: | ||
| tools.extend(dict(to_interaction_tool(tool_def)) for tool_def in request.tools) |
There was a problem hiding this comment.
to_interaction_tool(tool_def) returns a FunctionParam which is a TypedDict (inheriting from dict at runtime). Wrapping it in dict(...) is redundant and creates an unnecessary shallow copy of the dictionary. You can safely pass the returned dictionary directly to tools.extend.
| if request.tools: | |
| tools.extend(dict(to_interaction_tool(tool_def)) for tool_def in request.tools) | |
| if request.tools: | |
| tools.extend(to_interaction_tool(tool_def) for tool_def in request.tools) |
There was a problem hiding this comment.
Correct that the copy is a no-op (to_interaction_tool builds a fresh dict per call). No functional difference; will drop it in the next tidy commit on this slice.
| # Default missing environment to remote; the API rejects unsupported values. | ||
| create_kwargs.setdefault('environment', DEFAULT_ENVIRONMENT) |
There was a problem hiding this comment.
DEFAULT_ENVIRONMENT is a mutable module-level dictionary. Using it directly in setdefault means that if any downstream code or library mutates the environment dictionary inside create_kwargs, it will inadvertently mutate the shared DEFAULT_ENVIRONMENT constant, affecting all subsequent requests. It is safer to use .copy() to avoid shared mutable state.
| # Default missing environment to remote; the API rejects unsupported values. | |
| create_kwargs.setdefault('environment', DEFAULT_ENVIRONMENT) | |
| # Default missing environment to remote; the API rejects unsupported values. | |
| create_kwargs.setdefault('environment', DEFAULT_ENVIRONMENT.copy()) |
There was a problem hiding this comment.
Nothing mutates the environment dict downstream today (the body goes straight to JSON serialization), but the shared-reference risk is real and the fix is cheap. Will take this.
01b294e to
65d52d6
Compare
…interaction models Model layer extracted from #5806: Deep Research background model with cancel support, Antigravity and Lyria foreground models backed by the Interactions transport, and a shared registry of known models with capability metadata. Rewrites lyria.py from the legacy Vertex-only LyriaModel to the Interactions-backed implementation. Plugin wiring into GoogleAI init/resolve/list_actions lands in the next slice of the stack, together with its integration tests. Co-authored-by: Jeff Huang <huangjeff@google.com>
65d52d6 to
a4b2a47
Compare
Third slice of the #5806 split: Deep Research background model with cancel support, Antigravity and Lyria foreground models on the Interactions transport, and a registry of known models with capability metadata. Rewrites lyria.py from the legacy Vertex-only LyriaModel. Model-level tests included here; plugin wiring and its integration tests land in the next slice.
Part of the stack splitting #5806; recombined it reproduces that PR byte-for-byte. Co-authored with @huangjeff5.
Breaking changes
LyriaModelis removed fromgenkit_google_genai.models.lyria; Lyria is now served by the Interactions-backed implementation (create_lyria_action). The package-level exportsLyriaConfigandLyriaVersionare unchanged.