chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from composio_google.provider import GoogleProvider
|
||||
|
||||
__all__ = ("GoogleProvider",)
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Google AI Python Gemini tool spec.
|
||||
"""
|
||||
|
||||
import typing as t
|
||||
|
||||
from proto.marshal.collections.maps import MapComposite
|
||||
from vertexai.generative_models import (
|
||||
Content,
|
||||
FunctionDeclaration,
|
||||
GenerationResponse,
|
||||
Part,
|
||||
)
|
||||
|
||||
from composio.core.provider import NonAgenticProvider
|
||||
from composio.types import Modifiers, Tool, ToolExecutionResponse
|
||||
from composio.utils.shared import normalize_tool_arguments
|
||||
|
||||
|
||||
def _convert_map_composite(obj):
|
||||
if isinstance(obj, MapComposite):
|
||||
return {k: _convert_map_composite(v) for k, v in obj.items()}
|
||||
if isinstance(obj, (list, tuple)):
|
||||
return [_convert_map_composite(item) for item in obj]
|
||||
return obj
|
||||
|
||||
|
||||
class GoogleProvider(
|
||||
NonAgenticProvider[FunctionDeclaration, list[FunctionDeclaration]],
|
||||
name="google",
|
||||
):
|
||||
"""
|
||||
Composio toolset for Google AI Python Gemini framework.
|
||||
"""
|
||||
|
||||
def wrap_tool(self, tool: Tool) -> FunctionDeclaration:
|
||||
"""Wraps composio tool as Google AI Python Gemini FunctionDeclaration object."""
|
||||
# Clean up properties by removing 'examples' field
|
||||
properties = t.cast(
|
||||
dict[str, dict],
|
||||
tool.input_parameters.get("properties", {}),
|
||||
)
|
||||
cleaned_properties = {
|
||||
prop_name: {k: v for k, v in prop_schema.items() if k != "examples"}
|
||||
for prop_name, prop_schema in properties.items()
|
||||
}
|
||||
return FunctionDeclaration(
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": cleaned_properties,
|
||||
"required": tool.input_parameters.get("required", []),
|
||||
},
|
||||
)
|
||||
|
||||
def wrap_tools(self, tools: t.Sequence[Tool]) -> list[FunctionDeclaration]:
|
||||
return [self.wrap_tool(tool) for tool in tools]
|
||||
|
||||
def execute_tool_call(
|
||||
self,
|
||||
user_id: str,
|
||||
function_call: t.Any,
|
||||
modifiers: t.Optional[Modifiers] = None,
|
||||
) -> ToolExecutionResponse:
|
||||
"""
|
||||
Execute a function call.
|
||||
|
||||
:param function_call: Function call metadata from Gemini model response.
|
||||
:param user_id: User ID to use for executing the function call.
|
||||
:return: Object containing output data from the function call.
|
||||
"""
|
||||
# Gemini returns args as a MapComposite; normalize after converting to a
|
||||
# plain dict so a stringified payload is handled uniformly too (issue #2406).
|
||||
return self.execute_tool(
|
||||
slug=function_call.name,
|
||||
arguments=normalize_tool_arguments(
|
||||
_convert_map_composite(function_call.args)
|
||||
),
|
||||
modifiers=modifiers,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
def handle_response(
|
||||
self,
|
||||
user_id: str,
|
||||
response: GenerationResponse,
|
||||
modifiers: t.Optional[Modifiers] = None,
|
||||
) -> t.List[ToolExecutionResponse]:
|
||||
"""
|
||||
Handle response from Google AI Python Gemini model.
|
||||
|
||||
:param response: Generation response from the Gemini model.
|
||||
:param user_id: User ID to use for executing the function call.
|
||||
:return: A list of output objects from the function calls.
|
||||
"""
|
||||
outputs = []
|
||||
for candidate in response.candidates:
|
||||
if isinstance(candidate.content, Content) and candidate.content.parts:
|
||||
for part in candidate.content.parts:
|
||||
if isinstance(part, Part) and part.function_call:
|
||||
outputs.append(
|
||||
self.execute_tool_call(
|
||||
user_id=user_id,
|
||||
function_call=part.function_call,
|
||||
modifiers=modifiers,
|
||||
)
|
||||
)
|
||||
return outputs
|
||||
Reference in New Issue
Block a user