chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# composio-google
|
||||
|
||||
Adapts Composio tools to the Vertex AI SDK (`vertexai.generative_models`) as `FunctionDeclaration` objects for Gemini function calling.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-google google-cloud-aiplatform
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (create one at https://dashboard.composio.dev/settings) in your environment. Vertex AI authenticates with Google Cloud credentials; run `gcloud auth application-default login` or set `GOOGLE_APPLICATION_CREDENTIALS`.
|
||||
|
||||
## Quickstart
|
||||
|
||||
`GoogleProvider` is non-agentic: the model returns function calls, and `composio.provider.handle_response` executes every function call in the response and returns the results.
|
||||
|
||||
```python
|
||||
import vertexai
|
||||
from vertexai.generative_models import GenerativeModel, Tool
|
||||
|
||||
from composio import Composio
|
||||
from composio_google import GoogleProvider
|
||||
|
||||
vertexai.init(project="your-gcp-project", location="us-central1")
|
||||
|
||||
composio = Composio(provider=GoogleProvider())
|
||||
|
||||
# Create a session for your user
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
model = GenerativeModel(
|
||||
"gemini-2.0-flash",
|
||||
tools=[Tool(function_declarations=tools)],
|
||||
)
|
||||
chat = model.start_chat()
|
||||
|
||||
response = chat.send_message(
|
||||
"Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"
|
||||
)
|
||||
|
||||
# Execute the function calls the model requested
|
||||
results = composio.provider.handle_response(user_id="user_123", response=response)
|
||||
print(results)
|
||||
```
|
||||
|
||||
To execute a single call instead of the whole response, use `composio.provider.execute_tool_call(user_id="user_123", function_call=part.function_call)`.
|
||||
|
||||
## composio-google vs composio-gemini
|
||||
|
||||
This package targets the Vertex AI SDK (`vertexai.generative_models`, installed via `google-cloud-aiplatform`). [`composio-gemini`](../gemini) targets the newer `google-genai` SDK with Automatic Function Calling. For new projects, use `composio-gemini`.
|
||||
|
||||
## Links
|
||||
|
||||
- Google provider docs: https://docs.composio.dev/docs/providers/google
|
||||
- Composio docs: https://docs.composio.dev
|
||||
@@ -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
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Google AI Python Gemini demo.
|
||||
"""
|
||||
|
||||
import dotenv
|
||||
from composio_google import GoogleProvider
|
||||
from vertexai.generative_models import GenerativeModel
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Load environment variables from .env
|
||||
dotenv.load_dotenv()
|
||||
|
||||
# Initialize tools
|
||||
composio = Composio(provider=GoogleProvider())
|
||||
|
||||
# Get GitHub tools that are pre-configured
|
||||
tool = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Initialize the Gemini model
|
||||
model = GenerativeModel("gemini-1.5-pro", tools=[tool])
|
||||
|
||||
# Start a chat session
|
||||
chat = model.start_chat()
|
||||
|
||||
|
||||
def main():
|
||||
# Define task
|
||||
task = "Star a repo composiohq/composio on GitHub"
|
||||
|
||||
# Send a message to the model
|
||||
response = chat.send_message(task)
|
||||
|
||||
print("Model response:")
|
||||
print(response)
|
||||
|
||||
result = composio.provider.handle_response(user_id="default", response=response)
|
||||
print("Function call result:")
|
||||
print(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-google"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your Google AI Python Gemini model."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<4"
|
||||
authors = [
|
||||
{ name = "Composio", email = "tech@composio.dev" }
|
||||
]
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
dependencies = [
|
||||
"google-cloud-aiplatform>=1.158.0",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Setup configuration for Composio Google AI Python Gemini plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_google",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your Google AI Python Gemini model.",
|
||||
long_description=(Path(__file__).parent / "README.md").read_text(encoding="utf-8"),
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/ComposioHQ/composio",
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
python_requires=">=3.10,<4",
|
||||
install_requires=[
|
||||
"google-cloud-aiplatform>=1.158.0",
|
||||
"composio",
|
||||
],
|
||||
include_package_data=True,
|
||||
)
|
||||
Reference in New Issue
Block a user