chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# AGENTS.md
|
||||
|
||||
Python provider guidance.
|
||||
|
||||
## Scope
|
||||
|
||||
Each child directory is a Python provider package that adapts Composio to a framework or agent runtime.
|
||||
|
||||
## Skill Routing
|
||||
|
||||
Use `python-providers` for provider implementation and `python-testing` for nox/pytest verification. Use `cross-sdk-parity` when the provider mirrors a TypeScript package.
|
||||
|
||||
## Commands
|
||||
|
||||
Run from `python/`:
|
||||
|
||||
```bash
|
||||
make create-provider name=<provider-name>
|
||||
make create-provider name=<provider-name> agentic=true
|
||||
make chk
|
||||
make tst
|
||||
make type_inference
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- Keep provider dependencies in the provider package metadata unless shared tooling needs them.
|
||||
- Preserve Python naming conventions and public import paths.
|
||||
- Add provider tests and type-inference coverage when public return types change.
|
||||
- New providers or renamed provider packages usually need explicit entries in `python/noxfile.py`'s `type_inference` install list and checked-file list.
|
||||
- Verify package metadata before release-facing changes.
|
||||
@@ -0,0 +1,73 @@
|
||||
# composio-anthropic
|
||||
|
||||
Adapts Composio tools to the Claude Messages API tool format and executes the tool calls Claude returns.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-anthropic anthropic
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (create one at https://dashboard.composio.dev/settings) and `ANTHROPIC_API_KEY` in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
`AnthropicProvider` is non-agentic: Claude returns `tool_use` blocks, `handle_tool_calls` executes them, and you send the results back as `tool_result` blocks.
|
||||
|
||||
```python
|
||||
import json
|
||||
import anthropic
|
||||
from composio import Composio
|
||||
from composio_anthropic import AnthropicProvider
|
||||
|
||||
composio = Composio(provider=AnthropicProvider())
|
||||
client = anthropic.Anthropic()
|
||||
|
||||
# Create a session for your user
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
messages = [
|
||||
{"role": "user", "content": "Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"}
|
||||
]
|
||||
|
||||
response = client.messages.create(
|
||||
model="claude-opus-4-6",
|
||||
max_tokens=4096,
|
||||
tools=tools,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
# Agentic loop: keep executing tool calls until the model responds with text
|
||||
while response.stop_reason == "tool_use":
|
||||
tool_use_blocks = [block for block in response.content if block.type == "tool_use"]
|
||||
results = composio.provider.handle_tool_calls(user_id="user_123", response=response)
|
||||
messages.append({"role": "assistant", "content": response.content})
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "tool_result", "tool_use_id": tool_use_blocks[i].id, "content": json.dumps(result)}
|
||||
for i, result in enumerate(results)
|
||||
]
|
||||
})
|
||||
response = client.messages.create(
|
||||
model="claude-opus-4-6",
|
||||
max_tokens=4096,
|
||||
tools=tools,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
# Print final response
|
||||
for block in response.content:
|
||||
if block.type == "text":
|
||||
print(block.text)
|
||||
```
|
||||
|
||||
`handle_tool_calls` extracts every `tool_use` block from the response, executes the matching Composio tools, and returns the raw results in order. Claude occasionally emits tool input as a JSON string instead of an object; the provider normalizes this before execution.
|
||||
|
||||
Building on the Claude Agent SDK instead of the Messages API? Use [`composio-claude-agent-sdk`](../claude_agent_sdk).
|
||||
|
||||
## Links
|
||||
|
||||
- Anthropic provider docs: https://docs.composio.dev/docs/providers/anthropic
|
||||
- Composio docs: https://docs.composio.dev
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Anthropic claude demo.7
|
||||
"""
|
||||
|
||||
import anthropic
|
||||
from composio_anthropic import AnthropicProvider
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize tools.
|
||||
anthropic_client = anthropic.Anthropic()
|
||||
composio = Composio(provider=AnthropicProvider())
|
||||
|
||||
# Get GitHub tools that are pre-configured
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Get response from the LLM
|
||||
response = anthropic_client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
max_tokens=1024,
|
||||
tools=tools,
|
||||
messages=[
|
||||
{"role": "user", "content": "Star me composiohq/composio repo in github."},
|
||||
],
|
||||
)
|
||||
|
||||
# Execute the function calls.
|
||||
result = composio.provider.handle_tool_calls(user_id="default", response=response)
|
||||
print(result)
|
||||
@@ -0,0 +1,3 @@
|
||||
from composio_anthropic.provider import AnthropicProvider
|
||||
|
||||
__all__ = ("AnthropicProvider",)
|
||||
@@ -0,0 +1,95 @@
|
||||
import typing as t
|
||||
|
||||
from anthropic.types.beta.beta_tool_use_block import BetaToolUseBlock
|
||||
from anthropic.types.message import Message as ToolsBetaMessage
|
||||
from anthropic.types.tool_param import ToolParam
|
||||
from anthropic.types.tool_use_block import ToolUseBlock
|
||||
|
||||
from composio.core.provider import NonAgenticProvider
|
||||
from composio.types import Modifiers, Tool, ToolExecutionResponse
|
||||
from composio.utils.shared import (
|
||||
ToolSchemaAliases,
|
||||
alias_tool_input_schema,
|
||||
normalize_tool_arguments,
|
||||
)
|
||||
|
||||
|
||||
class AnthropicProvider(
|
||||
NonAgenticProvider[ToolParam, list[ToolParam]],
|
||||
name="anthropic",
|
||||
):
|
||||
"""
|
||||
Composio toolset for Anthropic Claude platform.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs: t.Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self._aliases: dict[str, ToolSchemaAliases] = {}
|
||||
|
||||
def wrap_tool(self, tool: Tool) -> ToolParam:
|
||||
aliases = alias_tool_input_schema(tool.input_parameters or {})
|
||||
self._aliases[tool.slug] = aliases
|
||||
return ToolParam(
|
||||
input_schema=aliases.schema,
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
)
|
||||
|
||||
def wrap_tools(self, tools: t.Sequence[Tool]) -> list[ToolParam]:
|
||||
return [self.wrap_tool(tool) for tool in tools]
|
||||
|
||||
def execute_tool_call(
|
||||
self,
|
||||
user_id: str,
|
||||
tool_call: ToolUseBlock,
|
||||
modifiers: t.Optional[Modifiers] = None,
|
||||
) -> ToolExecutionResponse:
|
||||
"""
|
||||
Execute a tool call.
|
||||
|
||||
:param user_id: User ID to use for executing function calls.
|
||||
:param tool_call: Tool call metadata.
|
||||
:param modifiers: Modifiers to use for executing function calls.
|
||||
:return: Object containing output data from the tool call.
|
||||
"""
|
||||
# Models occasionally emit tool input as a JSON string rather than a dict (issue #2406).
|
||||
arguments = normalize_tool_arguments(tool_call.input)
|
||||
aliases = self._aliases.get(tool_call.name)
|
||||
if aliases is not None:
|
||||
arguments = aliases.restore_arguments(arguments)
|
||||
return self.execute_tool(
|
||||
slug=tool_call.name,
|
||||
arguments=arguments,
|
||||
modifiers=modifiers,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
def handle_tool_calls(
|
||||
self,
|
||||
user_id: str,
|
||||
response: t.Union[dict, ToolsBetaMessage],
|
||||
modifiers: t.Optional[Modifiers] = None,
|
||||
) -> t.List[ToolExecutionResponse]:
|
||||
"""
|
||||
Handle tool calls from Anthropic Claude chat completion object.
|
||||
|
||||
:param response: Chat completion object from
|
||||
`anthropic.Anthropic.beta.tools.messages.create` function call.
|
||||
:param user_id: User ID to use for executing function calls.
|
||||
:param modifiers: Modifiers to use for executing function calls.
|
||||
:return: A list of output objects from the tool calls.
|
||||
"""
|
||||
if isinstance(response, dict):
|
||||
response = ToolsBetaMessage(**response)
|
||||
|
||||
outputs = []
|
||||
for content in response.content:
|
||||
if isinstance(content, (ToolUseBlock, BetaToolUseBlock)):
|
||||
outputs.append(
|
||||
self.execute_tool_call(
|
||||
user_id=user_id,
|
||||
tool_call=content,
|
||||
modifiers=modifiers,
|
||||
)
|
||||
)
|
||||
return outputs
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-anthropic"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your Anthropic LLMs."
|
||||
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 = [
|
||||
"anthropic>=0.109.2",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Setup configuration for Composio Claude plugin.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_anthropic",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your Anthropic LLMs.",
|
||||
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=["anthropic>=0.109.2", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
# composio-autogen
|
||||
|
||||
Adapts Composio tools to AutoGen `FunctionTool` objects and registers them with your caller and executor agents, so your AutoGen agents can take action across 1000+ apps.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-autogen autogen-agentchat
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (from the [dashboard](https://dashboard.composio.dev/settings)) and `OPENAI_API_KEY` in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```python
|
||||
from autogen import AssistantAgent, UserProxyAgent
|
||||
from composio import Composio
|
||||
from composio_autogen import AutogenProvider
|
||||
|
||||
composio = Composio(provider=AutogenProvider())
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
chatbot = AssistantAgent(
|
||||
"chatbot",
|
||||
system_message="Reply TERMINATE when the task is done or when user's content is empty",
|
||||
llm_config={"config_list": [{"model": "gpt-5.2"}]},
|
||||
)
|
||||
|
||||
user_proxy = UserProxyAgent(
|
||||
"user_proxy",
|
||||
is_termination_msg=lambda msg: "TERMINATE" in (msg.get("content", "") or ""),
|
||||
human_input_mode="NEVER",
|
||||
code_execution_config={"use_docker": False},
|
||||
)
|
||||
|
||||
# Register tools with both agents
|
||||
composio.provider.register_tools(caller=chatbot, executor=user_proxy, tools=tools)
|
||||
|
||||
response = user_proxy.initiate_chat(
|
||||
chatbot,
|
||||
message="Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'",
|
||||
)
|
||||
|
||||
print(response.chat_history)
|
||||
```
|
||||
|
||||
For multi-turn use, store `session.session_id` and reuse it with `composio.use(session_id)` instead of calling `create()` again.
|
||||
|
||||
## Provider specifics
|
||||
|
||||
AutoGen needs tools registered with two agents, not passed once. Call `composio.provider.register_tools(caller=..., executor=..., tools=tools)`: the `caller` decides which tool to invoke, and the `executor` runs it. This method is unique to the AutoGen provider.
|
||||
|
||||
AutoGen caps function names at 64 characters, so the provider hashes and truncates long tool slugs to stay under the limit. The registered name will not always match the original Composio slug.
|
||||
|
||||
## Links
|
||||
|
||||
- [AutoGen provider docs](https://docs.composio.dev/docs/providers/autogen)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,60 @@
|
||||
import os
|
||||
|
||||
import dotenv
|
||||
from autogen import AssistantAgent, UserProxyAgent
|
||||
from composio_autogen import AutogenProvider
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Load environment variables from .env
|
||||
dotenv.load_dotenv()
|
||||
|
||||
|
||||
def is_termination_msg(content: dict) -> bool:
|
||||
"""Check if a message contains termination message."""
|
||||
return "TERMINATE" in (content.get("content", "") or "")
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize tools.
|
||||
chatbot = AssistantAgent(
|
||||
"chatbot",
|
||||
system_message="Reply TERMINATE when the task is done or when user's content is empty",
|
||||
llm_config={
|
||||
"config_list": [
|
||||
{"model": "gpt-5", "api_key": os.environ["OPENAI_API_KEY"]},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Create a user proxy agent
|
||||
user_proxy = UserProxyAgent(
|
||||
"user_proxy",
|
||||
is_termination_msg=is_termination_msg,
|
||||
human_input_mode="NEVER",
|
||||
code_execution_config={"use_docker": False},
|
||||
)
|
||||
|
||||
# Get composio tools
|
||||
composio = Composio(provider=AutogenProvider())
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Register the preferred Applications, with right executor.
|
||||
composio.provider.register_tools(
|
||||
caller=chatbot,
|
||||
executor=user_proxy,
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
# Define task.
|
||||
task = "Star a repo composiohq/composio on GitHub"
|
||||
|
||||
# Execute task.
|
||||
response = user_proxy.initiate_chat(chatbot, message=task)
|
||||
|
||||
# Print response
|
||||
print(response.chat_history)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,3 @@
|
||||
from .provider import AutogenProvider
|
||||
|
||||
__all__ = ("AutogenProvider",)
|
||||
@@ -0,0 +1,119 @@
|
||||
import hashlib
|
||||
import types
|
||||
import typing as t
|
||||
from inspect import Signature
|
||||
|
||||
import autogen
|
||||
from autogen.agentchat.conversable_agent import ConversableAgent
|
||||
from autogen_core.tools import FunctionTool
|
||||
|
||||
from composio.client.types import Tool
|
||||
from composio.core.provider import AgenticProvider
|
||||
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
||||
from composio.utils.shared import (
|
||||
get_signature_format_from_schema_params,
|
||||
normalize_tool_arguments,
|
||||
reinstate_reserved_python_keywords,
|
||||
substitute_reserved_python_keywords,
|
||||
)
|
||||
|
||||
|
||||
class AutogenProvider(
|
||||
AgenticProvider[FunctionTool, list[FunctionTool]],
|
||||
name="autogen",
|
||||
):
|
||||
"""
|
||||
Composio toolset for Autogen framework.
|
||||
"""
|
||||
|
||||
def _process_function_name_for_registration(
|
||||
self,
|
||||
input_string: str,
|
||||
max_allowed_length: int = 64,
|
||||
num_hash_char: int = 10,
|
||||
):
|
||||
"""
|
||||
Process function name for proxy registration under given character length limitation.
|
||||
"""
|
||||
hash_hex = hashlib.sha256(input_string.encode(encoding="utf-8")).hexdigest()
|
||||
hash_chars_to_attach = hash_hex[:10]
|
||||
num_input_str_char = max_allowed_length - (num_hash_char + 1)
|
||||
input_str_to_attach = input_string[-num_input_str_char:]
|
||||
processed_name = input_str_to_attach + "_" + hash_chars_to_attach
|
||||
return processed_name
|
||||
|
||||
def register_tools(
|
||||
self,
|
||||
caller: ConversableAgent,
|
||||
executor: ConversableAgent,
|
||||
tools: t.List[FunctionTool],
|
||||
) -> None:
|
||||
"""
|
||||
Register tools to the proxy agents.
|
||||
|
||||
:param executor: Executor agent.
|
||||
:param caller: Caller agent.
|
||||
:param tools: List of tools to register.
|
||||
"""
|
||||
for tool in tools:
|
||||
autogen.agentchat.register_function(
|
||||
f=tool._func,
|
||||
caller=caller,
|
||||
executor=executor,
|
||||
name=tool.name,
|
||||
description=tool.description,
|
||||
)
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> FunctionTool:
|
||||
"""Wraps a composio tool as an Autogen FunctionTool."""
|
||||
schema_params, keywords = substitute_reserved_python_keywords(
|
||||
schema=tool.input_parameters
|
||||
)
|
||||
|
||||
def execute_action(**kwargs: t.Any) -> t.Dict:
|
||||
"""Placeholder function for executing action."""
|
||||
kwargs = reinstate_reserved_python_keywords(
|
||||
request=kwargs, keywords=keywords
|
||||
)
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
return execute_tool(
|
||||
slug=tool.slug, arguments=normalize_tool_arguments(kwargs)
|
||||
)
|
||||
|
||||
# Create function with proper signature
|
||||
function = types.FunctionType(
|
||||
code=execute_action.__code__,
|
||||
globals=globals(),
|
||||
closure=execute_action.__closure__,
|
||||
name=self._process_function_name_for_registration(input_string=tool.slug),
|
||||
)
|
||||
|
||||
# Set signature and annotations
|
||||
params = get_signature_format_from_schema_params(
|
||||
schema_params=schema_params,
|
||||
skip_default=self.skip_default,
|
||||
)
|
||||
function.__doc__ = tool.description
|
||||
setattr(function, "__signature__", Signature(parameters=params))
|
||||
setattr(
|
||||
function,
|
||||
"__annotations__",
|
||||
{p.name: p.annotation for p in params} | {"return": t.Dict[str, t.Any]},
|
||||
)
|
||||
return FunctionTool(
|
||||
func=function,
|
||||
description=tool.description,
|
||||
name=self._process_function_name_for_registration(input_string=tool.slug),
|
||||
)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> list[FunctionTool]:
|
||||
"""Wraps array of composio tools as an Autogen FunctionTool."""
|
||||
return [self.wrap_tool(tool=tool, execute_tool=execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,23 @@
|
||||
[project]
|
||||
name = "composio-autogen"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your autogen agent."
|
||||
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 = [
|
||||
"ag2>=0.14,<1.0",
|
||||
"flaml==2.6.0",
|
||||
"autogen_core>=0.7.5",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Setup configuration for Composio Gemin plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_autogen",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your Autogen agent.",
|
||||
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=[
|
||||
"ag2>=0.14,<1.0",
|
||||
"flaml==2.6.0",
|
||||
"autogen_core>=0.7.5",
|
||||
"composio",
|
||||
],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,64 @@
|
||||
# composio-claude-agent-sdk
|
||||
|
||||
Adapts Composio tools to the [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/python), exposing them to Claude through an in-process MCP server.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-claude-agent-sdk claude-agent-sdk
|
||||
```
|
||||
|
||||
The Claude Agent SDK also requires the Claude Code CLI: `npm install -g @anthropic-ai/claude-code`.
|
||||
|
||||
Set `COMPOSIO_API_KEY` (from the [dashboard](https://dashboard.composio.dev/settings)) and `ANTHROPIC_API_KEY` in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from composio import Composio
|
||||
from composio_claude_agent_sdk import ClaudeAgentSDKProvider
|
||||
from claude_agent_sdk import (
|
||||
AssistantMessage,
|
||||
ClaudeAgentOptions,
|
||||
TextBlock,
|
||||
create_sdk_mcp_server,
|
||||
query,
|
||||
)
|
||||
|
||||
composio = Composio(provider=ClaudeAgentSDKProvider())
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
server = create_sdk_mcp_server(name="composio", version="1.0.0", tools=tools)
|
||||
|
||||
|
||||
async def main():
|
||||
options = ClaudeAgentOptions(
|
||||
system_prompt="You are a helpful assistant. Use tools to complete tasks.",
|
||||
permission_mode="bypassPermissions",
|
||||
mcp_servers={"composio": server},
|
||||
)
|
||||
async for message in query(prompt="Summarize my emails from today", options=options):
|
||||
if isinstance(message, AssistantMessage):
|
||||
for block in message.content:
|
||||
if isinstance(block, TextBlock):
|
||||
print(block.text)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
For multi-turn use, store `session.session_id` and reuse it with `composio.use(session_id)` instead of calling `create()` again.
|
||||
|
||||
## How tools are exposed
|
||||
|
||||
`session.tools()` returns `SdkMcpTool` objects that plug straight into `create_sdk_mcp_server`. The provider also ships `composio.provider.create_mcp_server(tools)` if you prefer a preconfigured server (name `composio`, customizable via `ClaudeAgentSDKProvider(server_name=..., server_version=...)`).
|
||||
|
||||
## Links
|
||||
|
||||
- [Quickstart](https://docs.composio.dev/docs/quickstart)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from composio_claude_agent_sdk.provider import ClaudeAgentSDKProvider
|
||||
|
||||
__all__ = ("ClaudeAgentSDKProvider",)
|
||||
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Claude Agent SDK Provider for Composio
|
||||
|
||||
This provider enables integration with Claude Agent SDK,
|
||||
allowing Composio tools to be used as MCP tools within Claude agents.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import typing as t
|
||||
|
||||
from claude_agent_sdk import (
|
||||
McpSdkServerConfig,
|
||||
SdkMcpTool,
|
||||
create_sdk_mcp_server,
|
||||
)
|
||||
from claude_agent_sdk import (
|
||||
tool as sdk_tool,
|
||||
)
|
||||
|
||||
from composio.core.provider import AgenticProvider
|
||||
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
||||
from composio.types import Tool
|
||||
from composio.utils.shared import alias_tool_input_schema, normalize_tool_arguments
|
||||
|
||||
|
||||
class ClaudeAgentSDKProvider(
|
||||
AgenticProvider[SdkMcpTool, list[SdkMcpTool]],
|
||||
name="claude_agent_sdk",
|
||||
):
|
||||
"""
|
||||
Composio provider for Claude Agent SDK.
|
||||
|
||||
This provider wraps Composio tools as MCP tools that can be used with
|
||||
the Claude Agent SDK's `query()` function via the `mcp_servers` option.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import asyncio
|
||||
from composio import Composio
|
||||
from composio_claude_agent_sdk import ClaudeAgentSDKProvider
|
||||
from claude_agent_sdk import query, ClaudeAgentOptions
|
||||
|
||||
composio = Composio(provider=ClaudeAgentSDKProvider())
|
||||
|
||||
async def main():
|
||||
tools = composio.tools.get(user_id="default", toolkits=["gmail"])
|
||||
mcp_server = composio.provider.create_mcp_server(tools)
|
||||
|
||||
async for message in query(
|
||||
prompt="Fetch my latest email",
|
||||
options=ClaudeAgentOptions(
|
||||
mcp_servers={"composio": mcp_server},
|
||||
permission_mode="bypassPermissions",
|
||||
),
|
||||
):
|
||||
print(message)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
server_name: str = "composio",
|
||||
server_version: str = "1.0.0",
|
||||
) -> None:
|
||||
"""
|
||||
Initialize the Claude Agent SDK provider.
|
||||
|
||||
Args:
|
||||
server_name: Name for the MCP server (default: "composio")
|
||||
server_version: Version for the MCP server (default: "1.0.0")
|
||||
"""
|
||||
super().__init__()
|
||||
self.server_name = server_name
|
||||
self.server_version = server_version
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> SdkMcpTool:
|
||||
"""
|
||||
Wrap a Composio tool as a Claude Agent SDK MCP tool.
|
||||
|
||||
Args:
|
||||
tool: The Composio tool to wrap
|
||||
execute_tool: Function to execute the tool
|
||||
|
||||
Returns:
|
||||
A Claude Agent SDK MCP tool definition
|
||||
"""
|
||||
aliases = alias_tool_input_schema(tool.input_parameters or {})
|
||||
|
||||
@sdk_tool(
|
||||
tool.slug,
|
||||
tool.description or f"Execute {tool.slug}",
|
||||
aliases.schema,
|
||||
)
|
||||
async def tool_handler(args: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
|
||||
"""Execute the Composio tool with the given arguments."""
|
||||
try:
|
||||
arguments = aliases.restore_arguments(normalize_tool_arguments(args))
|
||||
# Run the synchronous execute_tool in a thread
|
||||
result = await asyncio.to_thread(
|
||||
execute_tool,
|
||||
tool.slug,
|
||||
arguments,
|
||||
)
|
||||
# Format the result for Claude Agent SDK
|
||||
result_text = result if isinstance(result, str) else json.dumps(result)
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": result_text,
|
||||
}
|
||||
]
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": json.dumps(
|
||||
{
|
||||
"successful": False,
|
||||
"error": str(e),
|
||||
"data": None,
|
||||
}
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return tool_handler
|
||||
|
||||
def _json_schema_to_simple_schema(self, schema: dict) -> dict:
|
||||
"""Return the provider-visible schema for a Composio JSON schema."""
|
||||
return alias_tool_input_schema(schema or {}).schema
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> list[SdkMcpTool]:
|
||||
"""
|
||||
Wrap multiple Composio tools as Claude Agent SDK MCP tools.
|
||||
|
||||
Args:
|
||||
tools: Sequence of Composio tools to wrap
|
||||
execute_tool: Function to execute the tools
|
||||
|
||||
Returns:
|
||||
List of Claude Agent SDK MCP tool definitions
|
||||
"""
|
||||
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
||||
|
||||
def create_mcp_server(
|
||||
self,
|
||||
wrapped_tools: list[SdkMcpTool],
|
||||
) -> McpSdkServerConfig:
|
||||
"""
|
||||
Create an MCP server configuration for use with Claude Agent SDK.
|
||||
|
||||
This is the primary method for integrating Composio tools with Claude agents.
|
||||
The returned configuration can be passed directly to the `mcp_servers` option.
|
||||
|
||||
Args:
|
||||
wrapped_tools: List of wrapped Claude Agent SDK MCP tools
|
||||
(from composio.tools.get())
|
||||
|
||||
Returns:
|
||||
MCP server configuration for Claude Agent SDK
|
||||
|
||||
Example:
|
||||
```python
|
||||
tools = composio.tools.get(user_id="default", toolkits=["gmail"])
|
||||
mcp_server = composio.provider.create_mcp_server(tools)
|
||||
|
||||
async for message in query(
|
||||
prompt="Send an email",
|
||||
options=ClaudeAgentOptions(
|
||||
mcp_servers={"composio": mcp_server},
|
||||
),
|
||||
):
|
||||
print(message)
|
||||
```
|
||||
"""
|
||||
return create_sdk_mcp_server(
|
||||
name=self.server_name,
|
||||
version=self.server_version,
|
||||
tools=wrapped_tools,
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-claude-agent-sdk"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with Claude Code Agents SDK."
|
||||
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 = [
|
||||
"claude-agent-sdk>=0.2.103",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
Setup configuration for Composio Claude Agent SDK plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name="composio_claude_agent_sdk",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get array of tools for Claude Agent SDK",
|
||||
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",
|
||||
packages=find_packages(),
|
||||
install_requires=["claude-agent-sdk>=0.2.103", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,310 @@
|
||||
"""
|
||||
Tests for Claude Code Agents Provider
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from composio_claude_agent_sdk.provider import ClaudeAgentSDKProvider
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def provider():
|
||||
"""Create a ClaudeAgentSDKProvider instance."""
|
||||
return ClaudeAgentSDKProvider()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def custom_provider():
|
||||
"""Create a ClaudeAgentSDKProvider with custom options."""
|
||||
return ClaudeAgentSDKProvider(
|
||||
server_name="custom-server",
|
||||
server_version="2.0.0",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_tool():
|
||||
"""Create a mock Composio tool."""
|
||||
return MagicMock(
|
||||
slug="GMAIL_SEND_EMAIL",
|
||||
name="Gmail Send Email",
|
||||
description="Send an email via Gmail",
|
||||
input_parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"to": {
|
||||
"type": "string",
|
||||
"description": "Recipient email address",
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
"description": "Email subject",
|
||||
},
|
||||
"body": {
|
||||
"type": "string",
|
||||
"description": "Email body content",
|
||||
},
|
||||
},
|
||||
"required": ["to", "subject", "body"],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_execute_tool():
|
||||
"""Create a mock execute_tool function."""
|
||||
return MagicMock(
|
||||
return_value={
|
||||
"data": {"result": "success"},
|
||||
"error": None,
|
||||
"successful": True,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class TestClaudeAgentSDKProviderInit:
|
||||
"""Tests for provider initialization."""
|
||||
|
||||
def test_default_options(self, provider):
|
||||
"""Test provider initializes with default options."""
|
||||
assert provider.server_name == "composio"
|
||||
assert provider.server_version == "1.0.0"
|
||||
|
||||
def test_custom_options(self, custom_provider):
|
||||
"""Test provider initializes with custom options."""
|
||||
assert custom_provider.server_name == "custom-server"
|
||||
assert custom_provider.server_version == "2.0.0"
|
||||
|
||||
def test_name_property(self, provider):
|
||||
"""Test provider has correct name."""
|
||||
assert provider.name == "claude_agent_sdk"
|
||||
|
||||
|
||||
class TestJsonSchemaConversion:
|
||||
"""Tests for JSON Schema conversion."""
|
||||
|
||||
def test_simple_schema_passthrough(self, provider):
|
||||
"""Test that JSON Schema is passed through unchanged."""
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"count": {"type": "integer"},
|
||||
},
|
||||
}
|
||||
result = provider._json_schema_to_simple_schema(schema)
|
||||
assert result == schema
|
||||
|
||||
def test_empty_schema(self, provider):
|
||||
"""Test handling of empty schema."""
|
||||
result = provider._json_schema_to_simple_schema({})
|
||||
assert result == {}
|
||||
|
||||
|
||||
class TestWrapTool:
|
||||
"""Tests for wrap_tool method."""
|
||||
|
||||
def test_wrap_tool_returns_sdk_mcp_tool(
|
||||
self, provider, mock_tool, mock_execute_tool
|
||||
):
|
||||
"""Test that wrap_tool returns an SdkMcpTool."""
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
mock_sdk_tool.return_value = lambda fn: fn
|
||||
provider.wrap_tool(mock_tool, mock_execute_tool)
|
||||
|
||||
# Verify sdk_tool was called with correct arguments
|
||||
mock_sdk_tool.assert_called_once_with(
|
||||
mock_tool.slug,
|
||||
mock_tool.description,
|
||||
mock_tool.input_parameters,
|
||||
)
|
||||
|
||||
def test_wrap_tool_without_description(self, provider, mock_execute_tool):
|
||||
"""Test wrapping a tool without description."""
|
||||
tool_without_desc = MagicMock(
|
||||
slug="TEST_TOOL",
|
||||
description=None,
|
||||
input_parameters={"type": "object", "properties": {}},
|
||||
)
|
||||
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
mock_sdk_tool.return_value = lambda fn: fn
|
||||
provider.wrap_tool(tool_without_desc, mock_execute_tool)
|
||||
|
||||
# Should use default description
|
||||
mock_sdk_tool.assert_called_once()
|
||||
call_args = mock_sdk_tool.call_args[0]
|
||||
assert call_args[1] == "Execute TEST_TOOL"
|
||||
|
||||
def test_wrap_tool_without_input_parameters(self, provider, mock_execute_tool):
|
||||
"""Test wrapping a tool without input parameters."""
|
||||
tool_without_params = MagicMock(
|
||||
slug="TEST_TOOL",
|
||||
description="Test tool",
|
||||
input_parameters=None,
|
||||
)
|
||||
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
mock_sdk_tool.return_value = lambda fn: fn
|
||||
provider.wrap_tool(tool_without_params, mock_execute_tool)
|
||||
|
||||
# Should use empty schema
|
||||
mock_sdk_tool.assert_called_once()
|
||||
call_args = mock_sdk_tool.call_args[0]
|
||||
assert call_args[2] == {}
|
||||
|
||||
|
||||
class TestWrapTools:
|
||||
"""Tests for wrap_tools method."""
|
||||
|
||||
def test_wrap_multiple_tools(self, provider, mock_tool, mock_execute_tool):
|
||||
"""Test wrapping multiple tools."""
|
||||
another_tool = MagicMock(
|
||||
slug="SLACK_POST_MESSAGE",
|
||||
description="Post a message to Slack",
|
||||
input_parameters={"type": "object", "properties": {}},
|
||||
)
|
||||
tools = [mock_tool, another_tool]
|
||||
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
mock_sdk_tool.return_value = lambda fn: fn
|
||||
wrapped = provider.wrap_tools(tools, mock_execute_tool)
|
||||
|
||||
assert len(wrapped) == 2
|
||||
assert mock_sdk_tool.call_count == 2
|
||||
|
||||
def test_wrap_empty_tools_list(self, provider, mock_execute_tool):
|
||||
"""Test wrapping an empty tools list."""
|
||||
wrapped = provider.wrap_tools([], mock_execute_tool)
|
||||
assert wrapped == []
|
||||
|
||||
|
||||
class TestCreateMcpServer:
|
||||
"""Tests for create_mcp_server method."""
|
||||
|
||||
def test_create_mcp_server_with_default_options(self, provider):
|
||||
"""Test creating MCP server with default options."""
|
||||
mock_tools = [MagicMock(), MagicMock()]
|
||||
|
||||
with patch(
|
||||
"composio_claude_agent_sdk.provider.create_sdk_mcp_server"
|
||||
) as mock_create:
|
||||
mock_create.return_value = {"type": "sdk", "name": "composio"}
|
||||
provider.create_mcp_server(mock_tools)
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
name="composio",
|
||||
version="1.0.0",
|
||||
tools=mock_tools,
|
||||
)
|
||||
|
||||
def test_create_mcp_server_with_custom_options(self, custom_provider):
|
||||
"""Test creating MCP server with custom options."""
|
||||
mock_tools = [MagicMock()]
|
||||
|
||||
with patch(
|
||||
"composio_claude_agent_sdk.provider.create_sdk_mcp_server"
|
||||
) as mock_create:
|
||||
mock_create.return_value = {"type": "sdk", "name": "custom-server"}
|
||||
custom_provider.create_mcp_server(mock_tools)
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
name="custom-server",
|
||||
version="2.0.0",
|
||||
tools=mock_tools,
|
||||
)
|
||||
|
||||
def test_create_mcp_server_with_empty_tools(self, provider):
|
||||
"""Test creating MCP server with empty tools list."""
|
||||
with patch(
|
||||
"composio_claude_agent_sdk.provider.create_sdk_mcp_server"
|
||||
) as mock_create:
|
||||
mock_create.return_value = {"type": "sdk", "name": "composio"}
|
||||
provider.create_mcp_server([])
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
name="composio",
|
||||
version="1.0.0",
|
||||
tools=[],
|
||||
)
|
||||
|
||||
|
||||
class TestToolHandlerExecution:
|
||||
"""Tests for tool handler execution."""
|
||||
|
||||
def test_tool_handler_success(self, provider, mock_tool):
|
||||
"""Test successful tool execution."""
|
||||
mock_execute = MagicMock(return_value={"data": "success", "successful": True})
|
||||
|
||||
# Create a real wrapped tool to test the handler
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
# Capture the decorated function
|
||||
captured_handler = None
|
||||
|
||||
def capture_decorator(name, desc, schema):
|
||||
def decorator(fn):
|
||||
nonlocal captured_handler
|
||||
captured_handler = fn
|
||||
return fn
|
||||
|
||||
return decorator
|
||||
|
||||
mock_sdk_tool.side_effect = capture_decorator
|
||||
provider.wrap_tool(mock_tool, mock_execute)
|
||||
|
||||
# Execute the captured handler
|
||||
result = asyncio.run(captured_handler({"to": "test@example.com"}))
|
||||
|
||||
assert result["content"][0]["type"] == "text"
|
||||
assert "success" in result["content"][0]["text"]
|
||||
|
||||
def test_tool_handler_error(self, provider, mock_tool):
|
||||
"""Test tool execution with error."""
|
||||
mock_execute = MagicMock(side_effect=Exception("Test error"))
|
||||
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
captured_handler = None
|
||||
|
||||
def capture_decorator(name, desc, schema):
|
||||
def decorator(fn):
|
||||
nonlocal captured_handler
|
||||
captured_handler = fn
|
||||
return fn
|
||||
|
||||
return decorator
|
||||
|
||||
mock_sdk_tool.side_effect = capture_decorator
|
||||
provider.wrap_tool(mock_tool, mock_execute)
|
||||
|
||||
result = asyncio.run(captured_handler({"to": "test@example.com"}))
|
||||
|
||||
assert result["content"][0]["type"] == "text"
|
||||
response = json.loads(result["content"][0]["text"])
|
||||
assert response["successful"] is False
|
||||
assert "Test error" in response["error"]
|
||||
|
||||
def test_tool_handler_string_result(self, provider, mock_tool):
|
||||
"""Test tool execution returning string result."""
|
||||
mock_execute = MagicMock(return_value="Simple string result")
|
||||
|
||||
with patch("composio_claude_agent_sdk.provider.sdk_tool") as mock_sdk_tool:
|
||||
captured_handler = None
|
||||
|
||||
def capture_decorator(name, desc, schema):
|
||||
def decorator(fn):
|
||||
nonlocal captured_handler
|
||||
captured_handler = fn
|
||||
return fn
|
||||
|
||||
return decorator
|
||||
|
||||
mock_sdk_tool.side_effect = capture_decorator
|
||||
provider.wrap_tool(mock_tool, mock_execute)
|
||||
|
||||
result = asyncio.run(captured_handler({}))
|
||||
|
||||
assert result["content"][0]["type"] == "text"
|
||||
assert result["content"][0]["text"] == "Simple string result"
|
||||
+1271
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
# composio-crewai
|
||||
|
||||
Adapts Composio tools to CrewAI's `BaseTool` format so your crew's agents can act across 1000+ apps through a single Composio session.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-crewai crewai
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (get one from [dashboard.composio.dev/settings](https://dashboard.composio.dev/settings)) and `OPENAI_API_KEY` in your environment:
|
||||
|
||||
```bash
|
||||
export COMPOSIO_API_KEY=xxxxxxxxx
|
||||
export OPENAI_API_KEY=xxxxxxxxx
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
Create a session for your user, fetch its tools, and pass them to an `Agent`. CrewAI runs the task end to end; each tool executes itself through Composio.
|
||||
|
||||
```python
|
||||
from crewai import Agent, Crew, Task
|
||||
from composio import Composio
|
||||
from composio_crewai import CrewAIProvider
|
||||
|
||||
composio = Composio(provider=CrewAIProvider())
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
agent = Agent(
|
||||
role="Email Agent",
|
||||
goal="Send emails on behalf of the user",
|
||||
backstory="You are an AI agent that sends emails using Gmail.",
|
||||
tools=tools,
|
||||
llm="gpt-5.2",
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description="Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'",
|
||||
agent=agent,
|
||||
expected_output="Confirmation that the email was sent",
|
||||
)
|
||||
|
||||
crew = Crew(agents=[agent], tasks=[task])
|
||||
result = crew.kickoff()
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
Each Composio tool becomes a CrewAI `BaseTool` whose `args_schema` is built from the tool's input schema, so CrewAI validates arguments before running anything. When validation fails, the tool does not raise; it returns a structured result:
|
||||
|
||||
```python
|
||||
{"successful": False, "error": "<validation message>", "data": None}
|
||||
```
|
||||
|
||||
Check `successful` in your task output instead of wrapping calls in `try`/`except`.
|
||||
|
||||
## Links
|
||||
|
||||
- [CrewAI provider docs](https://docs.composio.dev/docs/providers/crewai)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from composio_crewai.providers import CrewAIProvider
|
||||
|
||||
__all__ = ("CrewAIProvider",)
|
||||
@@ -0,0 +1,53 @@
|
||||
import typing as t
|
||||
|
||||
import pydantic
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
|
||||
from composio.types import Tool
|
||||
from composio.utils.pydantic import parse_pydantic_error
|
||||
from composio.utils.shared import json_schema_to_model, normalize_tool_arguments
|
||||
|
||||
|
||||
class CrewAIProvider(AgenticProvider[BaseTool, list[BaseTool]], name="crewai"):
|
||||
"""
|
||||
Composio toolset for CrewiAI framework.
|
||||
"""
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> BaseTool:
|
||||
"""Wrap a tool as a CrewAI tool."""
|
||||
|
||||
class Wrapper(BaseTool):
|
||||
def _run(self, **kwargs):
|
||||
try:
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
return execute_tool(
|
||||
slug=tool.slug, arguments=normalize_tool_arguments(kwargs)
|
||||
)
|
||||
except pydantic.ValidationError as e:
|
||||
return {
|
||||
"successful": False,
|
||||
"error": parse_pydantic_error(e),
|
||||
"data": None,
|
||||
}
|
||||
|
||||
return Wrapper(
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
args_schema=json_schema_to_model(
|
||||
json_schema=tool.input_parameters,
|
||||
skip_default=self.skip_default,
|
||||
),
|
||||
)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> list[BaseTool]:
|
||||
"""Wrap a list of tools as a list of CrewAI tools."""
|
||||
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
CrewAI demo.
|
||||
"""
|
||||
|
||||
from composio_crewai import CrewAIProvider
|
||||
from crewai import Agent, Crew, Task
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize tools.
|
||||
openai_client = ChatOpenAI()
|
||||
composio = Composio(provider=CrewAIProvider())
|
||||
|
||||
# Get All the tools
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Define agent
|
||||
crewai_agent = Agent(
|
||||
role="Github Agent",
|
||||
goal="""You take action on Github using Github APIs""",
|
||||
backstory=(
|
||||
"You are AI agent that is responsible for taking actions on Github "
|
||||
"on users behalf. You need to take action on Github using Github APIs"
|
||||
),
|
||||
verbose=True,
|
||||
tools=tools,
|
||||
llm=openai_client,
|
||||
)
|
||||
|
||||
# Define task
|
||||
task = Task(
|
||||
description=(
|
||||
"Star a repo composiohq/composio on GitHub, if the action is successful "
|
||||
"include Action executed successfully"
|
||||
),
|
||||
agent=crewai_agent,
|
||||
expected_output="if the star happened",
|
||||
)
|
||||
|
||||
my_crew = Crew(agents=[crewai_agent], tasks=[task])
|
||||
result = my_crew.kickoff()
|
||||
print(result)
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-crewai"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your CrewAI agent."
|
||||
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 = [
|
||||
"crewai>=0.134.0,<1.16.0",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Setup configuration for the composio crewai toolset"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_crewai",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your CrewAI agent.",
|
||||
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=["crewai>=0.134.0,<1.16.0", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
# composio-gemini
|
||||
|
||||
Adapts Composio tools to the [`google-genai`](https://pypi.org/project/google-genai/) SDK as Python callables compatible with Gemini's Automatic Function Calling.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-gemini google-genai
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (create one at https://dashboard.composio.dev/settings) and `GOOGLE_API_KEY` (from https://aistudio.google.com/apikey) in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
`GeminiProvider` wraps each Composio tool as a typed Python callable. Pass the callables to `GenerateContentConfig(tools=...)` and the `google-genai` SDK derives function declarations from their signatures and executes tool calls automatically inside the chat loop; there is no manual tool-call handling.
|
||||
|
||||
```python
|
||||
from composio import Composio
|
||||
from composio_gemini import GeminiProvider
|
||||
from google import genai
|
||||
from google.genai import types
|
||||
|
||||
composio = Composio(provider=GeminiProvider())
|
||||
client = genai.Client()
|
||||
|
||||
# Create a session for your user
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
config = types.GenerateContentConfig(tools=tools)
|
||||
chat = client.chats.create(model="gemini-3-pro-preview", config=config)
|
||||
|
||||
response = chat.send_message(
|
||||
"Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"
|
||||
)
|
||||
print(response.text)
|
||||
```
|
||||
|
||||
If you disable Automatic Function Calling and handle function calls yourself, `composio.provider.handle_response(response)` executes the function calls in a Gemini response and returns `Part` objects ready to send back.
|
||||
|
||||
## composio-gemini vs composio-google
|
||||
|
||||
This package targets the `google-genai` SDK (`from google import genai`). [`composio-google`](../google) targets the older Vertex AI SDK (`vertexai.generative_models`). For new projects, Google recommends `google-genai`, so use this package.
|
||||
|
||||
## Links
|
||||
|
||||
- Google provider docs: https://docs.composio.dev/docs/providers/google
|
||||
- Composio docs: https://docs.composio.dev
|
||||
@@ -0,0 +1,3 @@
|
||||
from .provider import GeminiProvider
|
||||
|
||||
__all__ = ("GeminiProvider",)
|
||||
@@ -0,0 +1,205 @@
|
||||
"""Gemini provider for Composio SDK.
|
||||
|
||||
Returns Python callables compatible with google-genai's Automatic Function
|
||||
Calling (AFC). The SDK can introspect the callable's signature to derive
|
||||
FunctionDeclaration schemas and auto-execute tool calls in the chat loop.
|
||||
"""
|
||||
|
||||
import types as pytypes
|
||||
import typing as t
|
||||
from inspect import Parameter, Signature
|
||||
|
||||
from composio.client.types import Tool
|
||||
from composio.core.provider import AgenticProvider
|
||||
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
||||
from composio.utils.shared import (
|
||||
ToolSchemaAliases,
|
||||
alias_tool_input_schema,
|
||||
get_pydantic_signature_format_from_schema_params,
|
||||
normalize_tool_arguments,
|
||||
)
|
||||
|
||||
# google-genai is only needed for handle_response (backward compat)
|
||||
try:
|
||||
from google.genai import types as genai_types
|
||||
|
||||
HAS_GENAI = True
|
||||
except ImportError:
|
||||
genai_types = None # type: ignore
|
||||
HAS_GENAI = False
|
||||
|
||||
|
||||
def _to_serializable(value: t.Any) -> t.Any:
|
||||
"""Recursively convert Pydantic models (and other non-JSON types) to plain dicts/lists.
|
||||
|
||||
The google-genai SDK's AFC pipeline calls ``convert_if_exist_pydantic_model``
|
||||
on function arguments, turning nested dicts into dynamically-generated
|
||||
Pydantic ``GeneratedModel`` instances. These are not JSON-serializable, so
|
||||
the Composio ``execute_tool`` call fails. This helper normalises them back
|
||||
to plain Python primitives before handing off to the API.
|
||||
"""
|
||||
# Pydantic v2 BaseModel
|
||||
if hasattr(value, "model_dump"):
|
||||
return value.model_dump()
|
||||
# Pydantic v1 BaseModel
|
||||
if hasattr(value, "dict") and hasattr(value, "__fields__"):
|
||||
return value.dict()
|
||||
if isinstance(value, dict):
|
||||
return {k: _to_serializable(v) for k, v in value.items()}
|
||||
if isinstance(value, (list, tuple)):
|
||||
return [_to_serializable(v) for v in value]
|
||||
return value
|
||||
|
||||
|
||||
def _process_execution_result(result: t.Any) -> t.Dict:
|
||||
"""Process a tool execution result into a dict suitable for Gemini function responses."""
|
||||
if not isinstance(result, dict):
|
||||
return {"result": result}
|
||||
|
||||
if result.get("successful", True) and "data" in result:
|
||||
data = result["data"]
|
||||
return data if isinstance(data, dict) else {"result": data}
|
||||
|
||||
if not result.get("successful", True):
|
||||
return {
|
||||
"error": result.get("error", "Tool execution failed"),
|
||||
"details": result,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class GeminiProvider(AgenticProvider[t.Callable, list[t.Callable]], name="gemini"):
|
||||
"""Composio toolset for Google AI Python Gemini framework.
|
||||
|
||||
Returns Python callables compatible with google-genai's Automatic Function
|
||||
Calling (AFC). Pass the result of ``wrap_tools()`` directly to
|
||||
``GenerateContentConfig(tools=...)`` and the SDK will auto-execute tool
|
||||
calls in the ``chat.send_message()`` loop.
|
||||
"""
|
||||
|
||||
__schema_skip_defaults__ = True
|
||||
|
||||
def __init__(self, **kwargs: t.Any):
|
||||
super().__init__(**kwargs)
|
||||
self._executors: t.Dict[
|
||||
str, t.Tuple[AgenticProviderExecuteFn, ToolSchemaAliases]
|
||||
] = {}
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> t.Callable:
|
||||
"""Wrap a Composio tool as a Python callable for google-genai AFC.
|
||||
|
||||
The returned function has ``__name__``, ``__doc__``, ``__signature__``
|
||||
and ``__annotations__`` set so the google-genai SDK can:
|
||||
|
||||
1. Derive a ``FunctionDeclaration`` schema via ``from_callable()``
|
||||
2. Store it in the AFC ``function_map`` for automatic execution
|
||||
"""
|
||||
aliases = alias_tool_input_schema(schema=tool.input_parameters)
|
||||
self._executors[tool.slug] = (execute_tool, aliases)
|
||||
|
||||
def function(**kwargs: t.Any) -> t.Dict:
|
||||
"""Composio tool execution wrapper."""
|
||||
kwargs = _to_serializable(kwargs)
|
||||
kwargs = aliases.restore_arguments(kwargs)
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
result = execute_tool(tool.slug, normalize_tool_arguments(kwargs))
|
||||
return _process_execution_result(result)
|
||||
|
||||
# Create a real function object (passes inspect.isfunction)
|
||||
action_func = pytypes.FunctionType(
|
||||
function.__code__,
|
||||
globals=globals(),
|
||||
name=tool.slug,
|
||||
closure=function.__closure__,
|
||||
)
|
||||
|
||||
# Build typed signature from JSON schema.
|
||||
# Uses get_pydantic_signature_format_from_schema_params (not
|
||||
# get_signature_format_from_schema_params) because the pydantic variant
|
||||
# goes through json_schema_to_pydantic_type() which produces
|
||||
# parameterized generics (e.g. List[str] instead of bare List).
|
||||
# The google-genai SDK requires parameterized array types — bare List
|
||||
# generates {"type": "ARRAY"} without "items", which the API rejects.
|
||||
sig_params = get_pydantic_signature_format_from_schema_params(
|
||||
schema_params=aliases.schema,
|
||||
skip_default=True,
|
||||
)
|
||||
action_func.__signature__ = Signature(parameters=sig_params) # type: ignore
|
||||
action_func.__doc__ = tool.description or f"Execute {tool.slug}"
|
||||
|
||||
# Build __annotations__ for typing.get_type_hints() compatibility
|
||||
annotations: t.Dict[str, t.Any] = {}
|
||||
for param in sig_params:
|
||||
if param.annotation is not Parameter.empty:
|
||||
annotations[param.name] = param.annotation
|
||||
annotations["return"] = dict
|
||||
action_func.__annotations__ = annotations
|
||||
|
||||
return action_func
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> list[t.Callable]:
|
||||
"""Wrap multiple Composio tools as Python callables for google-genai AFC."""
|
||||
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
||||
|
||||
# --- Backward compatibility: manual function calling ---
|
||||
|
||||
def handle_response(self, response: t.Any) -> tuple[list, bool]:
|
||||
"""Manually handle function calls in a Gemini response.
|
||||
|
||||
Provided for backward compatibility with code that uses manual function
|
||||
calling instead of AFC. For new code, pass the callables from
|
||||
``wrap_tools()`` to ``GenerateContentConfig(tools=...)`` and AFC will
|
||||
handle execution automatically.
|
||||
|
||||
Returns:
|
||||
tuple: ``(function_responses, executed)`` where *function_responses*
|
||||
are ``genai_types.Part`` objects ready to send back, and *executed*
|
||||
is ``True`` if any functions were executed.
|
||||
"""
|
||||
if not HAS_GENAI:
|
||||
return [], False
|
||||
|
||||
if not (hasattr(response, "candidates") and response.candidates):
|
||||
return [], False
|
||||
|
||||
candidate = response.candidates[0]
|
||||
if not (hasattr(candidate, "content") and candidate.content.parts):
|
||||
return [], False
|
||||
|
||||
function_responses: list = []
|
||||
executed = False
|
||||
|
||||
for part in candidate.content.parts:
|
||||
if not (hasattr(part, "function_call") and part.function_call):
|
||||
continue
|
||||
|
||||
fc = part.function_call
|
||||
if fc.name not in self._executors:
|
||||
continue
|
||||
|
||||
execute_tool, aliases = self._executors[fc.name]
|
||||
arguments = aliases.restore_arguments(dict(fc.args))
|
||||
result = execute_tool(
|
||||
slug=fc.name, arguments=normalize_tool_arguments(arguments)
|
||||
)
|
||||
processed = _process_execution_result(result)
|
||||
|
||||
function_responses.append(
|
||||
genai_types.Part(
|
||||
function_response=genai_types.FunctionResponse(
|
||||
name=fc.name, response=processed
|
||||
)
|
||||
)
|
||||
)
|
||||
executed = True
|
||||
|
||||
return function_responses, executed
|
||||
@@ -0,0 +1,26 @@
|
||||
from composio_gemini import GeminiProvider
|
||||
from google import genai
|
||||
from google.genai import types
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Create composio client
|
||||
composio = Composio(provider=GeminiProvider())
|
||||
|
||||
# Create google client
|
||||
client = genai.Client()
|
||||
|
||||
# Create genai client config
|
||||
config = types.GenerateContentConfig(
|
||||
tools=composio.tools.get(
|
||||
user_id="default",
|
||||
tools=[
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER",
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
# Use the chat interface.
|
||||
chat = client.chats.create(model="gemini-2.0-flash", config=config)
|
||||
response = chat.send_message("Can you star composiohq/composio repository on github")
|
||||
print(response.text)
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-gemini"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your Gemini agent."
|
||||
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-genai>=2.8.0",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Setup configuration for Composio Gemin plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_gemini",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your Gemini agent.",
|
||||
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-genai>=2.8.0",
|
||||
"composio",
|
||||
],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
# composio-google-adk
|
||||
|
||||
Adapts Composio tools to [Google ADK](https://google.github.io/adk-docs/) `FunctionTool` objects, so your ADK agents can take action across 1000+ apps.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-google-adk google-adk
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (from the [dashboard](https://dashboard.composio.dev/settings)) and `GOOGLE_API_KEY` in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```python
|
||||
from composio import Composio
|
||||
from composio_google_adk import GoogleAdkProvider
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.runners import Runner
|
||||
from google.adk.sessions import InMemorySessionService
|
||||
from google.genai import types
|
||||
|
||||
composio = Composio(provider=GoogleAdkProvider())
|
||||
|
||||
# Each Composio session is scoped to one of your users
|
||||
composio_session = composio.create(user_id="user_123")
|
||||
tools = composio_session.tools()
|
||||
|
||||
agent = Agent(
|
||||
name="personal_assistant",
|
||||
model="gemini-2.0-flash",
|
||||
instruction="You are a helpful assistant. Use Composio tools to take action.",
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
session_service = InMemorySessionService()
|
||||
session_service.create_session_sync(
|
||||
app_name="personal_assistant",
|
||||
user_id="user_123",
|
||||
session_id="1234",
|
||||
)
|
||||
runner = Runner(
|
||||
agent=agent,
|
||||
app_name="personal_assistant",
|
||||
session_service=session_service,
|
||||
)
|
||||
|
||||
events = runner.run(
|
||||
user_id="user_123",
|
||||
session_id="1234",
|
||||
new_message=types.Content(
|
||||
role="user",
|
||||
parts=[types.Part(text="Star the repository composiohq/composio on GitHub")],
|
||||
),
|
||||
)
|
||||
for event in events:
|
||||
if event.is_final_response() and event.content and event.content.parts:
|
||||
print(event.content.parts[0].text)
|
||||
```
|
||||
|
||||
For multi-turn use, store `composio_session.session_id` and reuse it with `composio.use(session_id)` instead of calling `create()` again.
|
||||
|
||||
## How tools are wrapped
|
||||
|
||||
`GoogleAdkProvider` turns each Composio tool into a `google.adk.tools.FunctionTool` with a Python signature and docstring generated from the tool's schema, so ADK can pass them to Gemini as regular function declarations.
|
||||
|
||||
## Links
|
||||
|
||||
- [Quickstart](https://docs.composio.dev/docs/quickstart)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from .provider import GoogleAdkProvider
|
||||
|
||||
__all__ = ("GoogleAdkProvider",)
|
||||
@@ -0,0 +1,85 @@
|
||||
import types
|
||||
import typing as t
|
||||
from inspect import Signature
|
||||
|
||||
from google.adk.tools import FunctionTool
|
||||
|
||||
from composio.client.types import Tool
|
||||
from composio.core.provider import AgenticProvider
|
||||
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
||||
from composio.utils.openapi import function_signature_from_jsonschema
|
||||
from composio.utils.shared import alias_tool_input_schema, normalize_tool_arguments
|
||||
|
||||
|
||||
class GoogleAdkProvider(
|
||||
AgenticProvider[FunctionTool, list[FunctionTool]], name="google_adk"
|
||||
):
|
||||
"""
|
||||
Composio toolset for Google ADK framework.
|
||||
"""
|
||||
|
||||
__schema_skip_defaults__ = True
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> FunctionTool:
|
||||
"""Wraps composio tool as Google Genai SDK compatible function calling object."""
|
||||
|
||||
input_parameters = t.cast(
|
||||
t.Dict[str, t.Any],
|
||||
tool.input_parameters
|
||||
or {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": [],
|
||||
},
|
||||
)
|
||||
aliases = alias_tool_input_schema(schema=input_parameters)
|
||||
properties = t.cast(
|
||||
t.Dict[str, t.Dict[str, t.Any]],
|
||||
aliases.schema.get("properties", {}),
|
||||
)
|
||||
docstring = tool.description or f"Execute {tool.slug}"
|
||||
docstring += "\nArgs:"
|
||||
for _param, _schema in properties.items():
|
||||
docstring += "\n "
|
||||
docstring += _param + ": " + _schema.get("description", _param.title())
|
||||
|
||||
docstring += "\nReturns:"
|
||||
docstring += "\n A dictionary containing response from the action"
|
||||
|
||||
def _execute(**kwargs: t.Any) -> t.Dict:
|
||||
kwargs = aliases.restore_arguments(kwargs)
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
return execute_tool(
|
||||
slug=tool.slug, arguments=normalize_tool_arguments(kwargs)
|
||||
)
|
||||
|
||||
function = types.FunctionType(
|
||||
code=_execute.__code__,
|
||||
name=tool.slug,
|
||||
globals=globals(),
|
||||
closure=_execute.__closure__,
|
||||
)
|
||||
parameters = function_signature_from_jsonschema(
|
||||
schema=aliases.schema,
|
||||
skip_default=self.skip_default,
|
||||
)
|
||||
setattr(function, "__signature__", Signature(parameters=parameters))
|
||||
setattr(
|
||||
function,
|
||||
"__annotations__",
|
||||
{p.name: p.annotation for p in parameters} | {"return": dict},
|
||||
)
|
||||
function.__doc__ = docstring
|
||||
return FunctionTool(function)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> list[FunctionTool]:
|
||||
"""Get composio tools wrapped as Google Genai SDK compatible function calling object."""
|
||||
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,62 @@
|
||||
from composio_google_adk import GoogleAdkProvider
|
||||
from google.adk.agents import Agent
|
||||
from google.adk.runners import Runner
|
||||
from google.adk.sessions import InMemorySessionService
|
||||
from google.genai import types
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# ADK config
|
||||
model = "gemini-2.0-flash"
|
||||
app_name = "weather_sentiment_agent"
|
||||
session_id = "1234"
|
||||
user_id = "user1234"
|
||||
|
||||
# Initialize composio tools
|
||||
composio = Composio(provider=GoogleAdkProvider())
|
||||
tools = composio.tools.get(user_id=user_id, toolkits=["GITHUB"])
|
||||
|
||||
# Agent
|
||||
weather_sentiment_agent = Agent(
|
||||
name=app_name,
|
||||
model=model,
|
||||
instruction="You are a github utility agent with ability to interact with github APIs",
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
# Session and Runner
|
||||
session_service = InMemorySessionService()
|
||||
session = session_service.create_session_sync(
|
||||
app_name=app_name,
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
)
|
||||
runner = Runner(
|
||||
agent=weather_sentiment_agent,
|
||||
app_name=app_name,
|
||||
session_service=session_service,
|
||||
)
|
||||
|
||||
|
||||
# Agent Interaction
|
||||
content = types.Content(
|
||||
role="user",
|
||||
parts=[
|
||||
types.Part(
|
||||
text="Star github repository composiohq/composio",
|
||||
)
|
||||
],
|
||||
)
|
||||
events = runner.run(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
new_message=content,
|
||||
)
|
||||
for event in events:
|
||||
if (
|
||||
event.is_final_response()
|
||||
and event.content is not None
|
||||
and event.content.parts
|
||||
and len(event.content.parts) > 0
|
||||
):
|
||||
print("Agent Response: ", event.content.parts[0].text)
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-google-adk"
|
||||
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-adk>=2.2.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_adk",
|
||||
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-adk>=2.2.0",
|
||||
"composio",
|
||||
],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
# composio-langchain
|
||||
|
||||
Adapts Composio tools to LangChain's `StructuredTool` format so a LangChain agent can call 1000+ apps through a single Composio session.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-langchain langchain langchain-openai
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (get one from [dashboard.composio.dev/settings](https://dashboard.composio.dev/settings)) and `OPENAI_API_KEY` in your environment:
|
||||
|
||||
```bash
|
||||
export COMPOSIO_API_KEY=xxxxxxxxx
|
||||
export OPENAI_API_KEY=xxxxxxxxx
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
Create a session for your user, fetch its tools, and hand them to `create_agent`. LangChain runs the tool loop; each tool executes itself through Composio.
|
||||
|
||||
```python
|
||||
from composio import Composio
|
||||
from composio_langchain import LangchainProvider
|
||||
from langchain.agents import create_agent
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
composio = Composio(provider=LangchainProvider())
|
||||
llm = ChatOpenAI(model="gpt-5.2")
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
agent = create_agent(tools=tools, model=llm)
|
||||
result = agent.invoke(
|
||||
{
|
||||
"messages": [
|
||||
(
|
||||
"user",
|
||||
"Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'",
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
print(result["messages"][-1].content)
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
Each wrapped tool builds its `args_schema` from the Composio tool's input schema. When argument validation fails, the tool does not raise; it returns a structured result:
|
||||
|
||||
```python
|
||||
{"successful": False, "error": "<validation message>", "data": None}
|
||||
```
|
||||
|
||||
Check `successful` in tool output instead of wrapping calls in `try`/`except`.
|
||||
|
||||
## Links
|
||||
|
||||
- [LangChain provider docs](https://docs.composio.dev/docs/providers/langchain)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from composio_langchain.provider import LangchainProvider
|
||||
|
||||
__all__ = ("LangchainProvider",)
|
||||
@@ -0,0 +1,96 @@
|
||||
"""ComposioLangChain class definition"""
|
||||
|
||||
import types
|
||||
import typing as t
|
||||
from inspect import Signature
|
||||
|
||||
import pydantic
|
||||
from langchain_core.tools import StructuredTool as BaseStructuredTool
|
||||
|
||||
from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
|
||||
from composio.types import Tool
|
||||
from composio.utils.pydantic import parse_pydantic_error
|
||||
from composio.utils.shared import (
|
||||
get_signature_format_from_schema_params,
|
||||
json_schema_to_model,
|
||||
normalize_tool_arguments,
|
||||
reinstate_reserved_python_keywords,
|
||||
substitute_reserved_python_keywords,
|
||||
)
|
||||
|
||||
|
||||
class StructuredTool(BaseStructuredTool): # type: ignore[misc]
|
||||
def run(self, *args, **kwargs):
|
||||
try:
|
||||
return super().run(*args, **kwargs)
|
||||
except pydantic.ValidationError as e:
|
||||
return {"successful": False, "error": parse_pydantic_error(e), "data": None}
|
||||
|
||||
|
||||
class LangchainProvider(
|
||||
AgenticProvider[StructuredTool, t.List[StructuredTool]],
|
||||
name="langchain",
|
||||
):
|
||||
"""
|
||||
Composio toolset for Langchain framework.
|
||||
"""
|
||||
|
||||
runtime = "langchain"
|
||||
|
||||
def wrap_tool(
|
||||
self, tool: Tool, execute_tool: AgenticProviderExecuteFn
|
||||
) -> StructuredTool:
|
||||
"""Wraps composio tool as Langchain StructuredTool object."""
|
||||
# Replace reserved python keywords
|
||||
schema_params, keywords = substitute_reserved_python_keywords(
|
||||
schema=tool.input_parameters
|
||||
)
|
||||
|
||||
def function(**kwargs: t.Any) -> t.Dict:
|
||||
"""Wrapper function for composio action."""
|
||||
kwargs = reinstate_reserved_python_keywords(
|
||||
request=kwargs,
|
||||
keywords=keywords,
|
||||
)
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
return execute_tool(tool.slug, normalize_tool_arguments(kwargs))
|
||||
|
||||
action_func = types.FunctionType(
|
||||
function.__code__,
|
||||
globals=globals(),
|
||||
name=tool.slug,
|
||||
closure=function.__closure__,
|
||||
)
|
||||
action_func.__signature__ = Signature( # type: ignore
|
||||
parameters=get_signature_format_from_schema_params(
|
||||
schema_params=schema_params,
|
||||
skip_default=self.skip_default,
|
||||
)
|
||||
)
|
||||
action_func.__doc__ = tool.description
|
||||
|
||||
return t.cast(
|
||||
StructuredTool,
|
||||
StructuredTool.from_function(
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
args_schema=json_schema_to_model(
|
||||
json_schema=schema_params,
|
||||
skip_default=self.skip_default,
|
||||
),
|
||||
return_schema=True,
|
||||
func=action_func,
|
||||
handle_tool_error=True,
|
||||
handle_validation_error=True,
|
||||
),
|
||||
)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> t.List[StructuredTool]:
|
||||
"""
|
||||
Get composio tools wrapped as Langchain StructuredTool objects.
|
||||
"""
|
||||
return [self.wrap_tool(tool=tool, execute_tool=execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Langchain demo.
|
||||
"""
|
||||
|
||||
from composio_langchain import LangchainProvider
|
||||
from langchain.agents import create_agent
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
from composio import Composio
|
||||
|
||||
openai_client = ChatOpenAI(model="gpt-5")
|
||||
|
||||
|
||||
def main():
|
||||
composio = Composio(provider=LangchainProvider())
|
||||
|
||||
# Get All the tools
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Define task
|
||||
task = "Star a repo composiohq/composio on GitHub"
|
||||
|
||||
# Define agent
|
||||
agent = create_agent(
|
||||
model=openai_client, tools=tools, system_prompt="intelligent composio agent"
|
||||
)
|
||||
|
||||
# Execute task
|
||||
result = agent.invoke({"messages": [{"role": "user", "content": task}]})
|
||||
|
||||
print(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-langchain"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your Langchain agent."
|
||||
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 = [
|
||||
"langchain>=1.3.9,<2.0.0",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Setup configuration for Composio OpenAI plugin.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_langchain",
|
||||
version="0.17.1",
|
||||
author="composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your Langchain agent.",
|
||||
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=["langchain>=1.3.9,<2.0.0", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
# composio-langgraph
|
||||
|
||||
Adapts Composio tools to LangChain's `StructuredTool` format for use in LangGraph agents and graph workflows, giving them access to 1000+ apps through a single Composio session.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-langgraph langgraph langchain langchain-openai
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (get one from [dashboard.composio.dev/settings](https://dashboard.composio.dev/settings)) and `OPENAI_API_KEY` in your environment:
|
||||
|
||||
```bash
|
||||
export COMPOSIO_API_KEY=xxxxxxxxx
|
||||
export OPENAI_API_KEY=xxxxxxxxx
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
Create a session for your user, fetch its tools, and hand them to your agent. The wrapped tools also work anywhere LangGraph accepts LangChain tools, such as a `ToolNode` in a custom graph.
|
||||
|
||||
```python
|
||||
from composio import Composio
|
||||
from composio_langgraph import LanggraphProvider
|
||||
from langchain.agents import create_agent
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
composio = Composio(provider=LanggraphProvider())
|
||||
llm = ChatOpenAI(model="gpt-5.2")
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
agent = create_agent(tools=tools, model=llm)
|
||||
result = agent.invoke(
|
||||
{
|
||||
"messages": [
|
||||
(
|
||||
"user",
|
||||
"Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'",
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
print(result["messages"][-1].content)
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
Each wrapped tool builds its `args_schema` from the Composio tool's input schema. When argument validation fails, the tool does not raise; it returns a structured result:
|
||||
|
||||
```python
|
||||
{"successful": False, "error": "<validation message>", "data": None}
|
||||
```
|
||||
|
||||
Check `successful` in tool output instead of wrapping calls in `try`/`except`.
|
||||
|
||||
## Links
|
||||
|
||||
- [LangChain provider docs](https://docs.composio.dev/docs/providers/langchain) (covers LangGraph)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from .provider import LanggraphProvider
|
||||
|
||||
__all__ = ("LanggraphProvider",)
|
||||
@@ -0,0 +1,106 @@
|
||||
"""ComposioLangChain class definition"""
|
||||
|
||||
import types
|
||||
import typing as t
|
||||
from inspect import Signature
|
||||
|
||||
import pydantic
|
||||
from langchain_core.tools import StructuredTool as BaseStructuredTool
|
||||
|
||||
from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
|
||||
from composio.types import Tool
|
||||
from composio.utils.pydantic import parse_pydantic_error
|
||||
from composio.utils.shared import (
|
||||
get_signature_format_from_schema_params,
|
||||
json_schema_to_model,
|
||||
normalize_tool_arguments,
|
||||
reinstate_reserved_python_keywords,
|
||||
substitute_reserved_python_keywords,
|
||||
)
|
||||
|
||||
|
||||
class StructuredTool(BaseStructuredTool): # type: ignore[misc]
|
||||
def run(self, *args, **kwargs):
|
||||
try:
|
||||
return super().run(*args, **kwargs)
|
||||
except pydantic.ValidationError as e:
|
||||
return {"successful": False, "error": parse_pydantic_error(e), "data": None}
|
||||
|
||||
|
||||
class LanggraphProvider(
|
||||
AgenticProvider[StructuredTool, t.List[StructuredTool]],
|
||||
name="langgraph",
|
||||
):
|
||||
"""
|
||||
Composio toolset for Langchain framework.
|
||||
"""
|
||||
|
||||
def _wrap_action(
|
||||
self,
|
||||
tool: str,
|
||||
description: str,
|
||||
schema_params: t.Dict,
|
||||
keywords: t.Dict,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
):
|
||||
def function(**kwargs: t.Any) -> t.Dict:
|
||||
"""Wrapper function for composio action."""
|
||||
kwargs = reinstate_reserved_python_keywords(
|
||||
request=kwargs, keywords=keywords
|
||||
)
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
return execute_tool(tool, normalize_tool_arguments(kwargs))
|
||||
|
||||
action_func = types.FunctionType(
|
||||
function.__code__,
|
||||
globals=globals(),
|
||||
name=tool,
|
||||
closure=function.__closure__,
|
||||
)
|
||||
action_func.__signature__ = Signature( # type: ignore
|
||||
parameters=get_signature_format_from_schema_params(
|
||||
schema_params=schema_params,
|
||||
skip_default=self.skip_default,
|
||||
)
|
||||
)
|
||||
action_func.__doc__ = description
|
||||
return action_func
|
||||
|
||||
def wrap_tool(
|
||||
self, tool: Tool, execute_tool: AgenticProviderExecuteFn
|
||||
) -> StructuredTool:
|
||||
"""Wraps composio tool as Langchain StructuredTool object."""
|
||||
schema_params, keywords = substitute_reserved_python_keywords(
|
||||
schema=tool.input_parameters
|
||||
)
|
||||
return t.cast(
|
||||
StructuredTool,
|
||||
StructuredTool.from_function(
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
args_schema=json_schema_to_model(
|
||||
json_schema=schema_params,
|
||||
skip_default=self.skip_default,
|
||||
),
|
||||
return_schema=True,
|
||||
func=self._wrap_action(
|
||||
tool=tool.slug,
|
||||
description=tool.description,
|
||||
schema_params=schema_params,
|
||||
keywords=keywords,
|
||||
execute_tool=execute_tool,
|
||||
),
|
||||
handle_tool_error=True,
|
||||
handle_validation_error=True,
|
||||
),
|
||||
)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> t.List[StructuredTool]:
|
||||
"""
|
||||
Get composio tools wrapped as Langchain StructuredTool objects.
|
||||
"""
|
||||
return [self.wrap_tool(tool=tool, execute_tool=execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,97 @@
|
||||
import json
|
||||
import operator
|
||||
from typing import Annotated, Sequence, TypedDict
|
||||
|
||||
from composio_langgraph import LanggraphProvider
|
||||
from langchain_core.messages import BaseMessage, FunctionMessage, HumanMessage
|
||||
from langchain_core.utils.function_calling import convert_to_openai_function
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, StateGraph
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio(provider=LanggraphProvider())
|
||||
tools = composio.tools.get(
|
||||
user_id="default",
|
||||
tools=[
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER",
|
||||
"GITHUB_GET_THE_AUTHENTICATED_USER",
|
||||
],
|
||||
)
|
||||
functions = [convert_to_openai_function(t) for t in tools]
|
||||
model = ChatOpenAI(temperature=0, streaming=True).bind_functions(functions)
|
||||
|
||||
|
||||
def function_1(state):
|
||||
messages = state["messages"]
|
||||
response = model.invoke(messages)
|
||||
return {"messages": [response]}
|
||||
|
||||
|
||||
def function_2(state):
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
|
||||
parsed_function_call = last_message.additional_kwargs["function_call"]
|
||||
|
||||
# Find the correct tool to use from the provided list of tools.
|
||||
tool_to_use = None
|
||||
for t in tools:
|
||||
if t.name == parsed_function_call["name"]:
|
||||
tool_to_use = t
|
||||
break
|
||||
|
||||
if tool_to_use is None:
|
||||
raise ValueError(f"Tool with name {parsed_function_call['name']} not found.")
|
||||
|
||||
response = tool_to_use.invoke(json.loads(parsed_function_call["arguments"]))
|
||||
|
||||
function_message = FunctionMessage(
|
||||
content=str(response), name=parsed_function_call["name"]
|
||||
)
|
||||
|
||||
return {"messages": [function_message]}
|
||||
|
||||
|
||||
def where_to_go(state):
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
|
||||
if "function_call" in last_message.additional_kwargs:
|
||||
return "continue"
|
||||
return "end"
|
||||
|
||||
|
||||
class AgentState(TypedDict):
|
||||
messages: Annotated[Sequence[BaseMessage], operator.add]
|
||||
|
||||
|
||||
workflow = StateGraph(AgentState)
|
||||
workflow.add_node("agent", function_1)
|
||||
workflow.add_node("tool", function_2)
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
where_to_go,
|
||||
{
|
||||
# If return is "continue" then we call the tool node.
|
||||
"continue": "tool",
|
||||
# Otherwise we finish. END is a special node marking
|
||||
# that the graph should finish.
|
||||
"end": END,
|
||||
},
|
||||
)
|
||||
workflow.add_edge("tool", "agent")
|
||||
workflow.set_entry_point("agent")
|
||||
|
||||
app = workflow.compile()
|
||||
inputs = {
|
||||
"messages": [
|
||||
HumanMessage(content="Star a repo composiohq/composio on GitHub"),
|
||||
]
|
||||
}
|
||||
for output in app.stream(inputs): # type: ignore
|
||||
for key, value in output.items():
|
||||
print(f"Output from node '{key}':")
|
||||
print("---")
|
||||
print(value)
|
||||
print("\n---\n")
|
||||
@@ -0,0 +1,62 @@
|
||||
from typing import Literal
|
||||
|
||||
from composio_langgraph import LanggraphProvider
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import MessagesState, StateGraph
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
from composio import Composio
|
||||
|
||||
composio = Composio(provider=LanggraphProvider())
|
||||
tools = composio.tools.get(
|
||||
user_id="default",
|
||||
tools=[
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER",
|
||||
"GITHUB_GET_THE_AUTHENTICATED_USER",
|
||||
],
|
||||
)
|
||||
tool_node = ToolNode(tools)
|
||||
model_with_tools = ChatOpenAI(temperature=0, streaming=True).bind_tools(tools)
|
||||
|
||||
|
||||
def should_continue(state: MessagesState) -> Literal["tools", "__end__"]:
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
if last_message.tool_calls: # type: ignore
|
||||
return "tools"
|
||||
return "__end__"
|
||||
|
||||
|
||||
def call_model(state: MessagesState):
|
||||
messages = state["messages"]
|
||||
response = model_with_tools.invoke(messages)
|
||||
return {"messages": [response]}
|
||||
|
||||
|
||||
workflow = StateGraph(MessagesState)
|
||||
|
||||
# Define the two nodes we will cycle between
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("tools", tool_node)
|
||||
|
||||
workflow.add_edge("__start__", "agent")
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
)
|
||||
workflow.add_edge("tools", "agent")
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
for chunk in app.stream(
|
||||
{
|
||||
"messages": [
|
||||
( # type: ignore
|
||||
"human",
|
||||
"Star the Github Repository composiohq/composio",
|
||||
)
|
||||
]
|
||||
},
|
||||
stream_mode="values",
|
||||
):
|
||||
chunk["messages"][-1].pretty_print()
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-langgraph"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get array of tools with LangGraph Agent Workflows."
|
||||
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 = [
|
||||
"langgraph>=1.2.5",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Setup configuration for Composio LangGraph plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_langgraph",
|
||||
version="0.17.1",
|
||||
author="composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get array of tools with LangGraph Agent Workflows",
|
||||
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=["langgraph>=1.2.5", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
# composio-llamaindex
|
||||
|
||||
Adapts Composio tools to LlamaIndex's `FunctionTool` format so a LlamaIndex agent can call 1000+ apps through a single Composio session.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-llamaindex llama-index llama-index-llms-openai
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (get one from [dashboard.composio.dev/settings](https://dashboard.composio.dev/settings)) and `OPENAI_API_KEY` in your environment:
|
||||
|
||||
```bash
|
||||
export COMPOSIO_API_KEY=xxxxxxxxx
|
||||
export OPENAI_API_KEY=xxxxxxxxx
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
Create a session for your user, fetch its tools, and hand them to a `FunctionAgent`. LlamaIndex drives the tool calls; each tool executes itself through Composio.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from composio import Composio
|
||||
from composio_llamaindex import LlamaIndexProvider
|
||||
from llama_index.core.agent.workflow import FunctionAgent
|
||||
from llama_index.llms.openai import OpenAI
|
||||
|
||||
composio = Composio(provider=LlamaIndexProvider())
|
||||
llm = OpenAI(model="gpt-5.2")
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
agent = FunctionAgent(tools=tools, llm=llm)
|
||||
|
||||
|
||||
async def main():
|
||||
result = await agent.run(
|
||||
user_msg="Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [LlamaIndex provider docs](https://docs.composio.dev/docs/providers/llamaindex)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from .provider import LlamaIndexProvider
|
||||
|
||||
__all__ = ("LlamaIndexProvider",)
|
||||
@@ -0,0 +1,76 @@
|
||||
"""ComposioLangChain class definition"""
|
||||
|
||||
import types
|
||||
import typing as t
|
||||
from inspect import Signature
|
||||
|
||||
from llama_index.core.tools import FunctionTool
|
||||
|
||||
from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
|
||||
from composio.types import Tool
|
||||
from composio.utils.shared import (
|
||||
get_signature_format_from_schema_params,
|
||||
normalize_tool_arguments,
|
||||
reinstate_reserved_python_keywords,
|
||||
substitute_reserved_python_keywords,
|
||||
)
|
||||
|
||||
|
||||
class LlamaIndexProvider(
|
||||
AgenticProvider[FunctionTool, t.List[FunctionTool]],
|
||||
name="llamaindex",
|
||||
):
|
||||
"""
|
||||
Composio toolset for LlamaIndex framework.
|
||||
"""
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> FunctionTool:
|
||||
"""
|
||||
Wrap a tool into a LlamaIndex FunctionTool object.
|
||||
"""
|
||||
schema_params, keywords = substitute_reserved_python_keywords(
|
||||
schema=tool.input_parameters
|
||||
)
|
||||
|
||||
def function(**kwargs: t.Any) -> t.Dict:
|
||||
"""Wrapper function for composio action."""
|
||||
kwargs = reinstate_reserved_python_keywords(
|
||||
request=kwargs, keywords=keywords
|
||||
)
|
||||
# Normalize defensively so a stringified payload is coerced to a dict (issue #2406).
|
||||
return execute_tool(
|
||||
slug=tool.slug, arguments=normalize_tool_arguments(kwargs)
|
||||
)
|
||||
|
||||
action_func = types.FunctionType(
|
||||
function.__code__,
|
||||
globals=globals(),
|
||||
name=tool.slug,
|
||||
closure=function.__closure__,
|
||||
)
|
||||
action_func.__signature__ = Signature( # type: ignore
|
||||
parameters=get_signature_format_from_schema_params(
|
||||
schema_params=schema_params,
|
||||
skip_default=self.skip_default,
|
||||
)
|
||||
)
|
||||
action_func.__doc__ = tool.description
|
||||
return FunctionTool.from_defaults(
|
||||
action_func,
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> t.List[FunctionTool]:
|
||||
"""
|
||||
Wrap tools into LlamaIndex FunctionTool objects.
|
||||
"""
|
||||
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,38 @@
|
||||
import asyncio
|
||||
|
||||
import dotenv
|
||||
from composio_llamaindex import LlamaIndexProvider
|
||||
from llama_index.core.agent.workflow import FunctionAgent
|
||||
from llama_index.llms.openai import OpenAI
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Load environment variables from .env
|
||||
dotenv.load_dotenv()
|
||||
|
||||
# Setup client
|
||||
llm = OpenAI(model="gpt-5")
|
||||
composio = Composio(provider=LlamaIndexProvider())
|
||||
|
||||
# Get All the tools
|
||||
tools = composio.tools.get(
|
||||
user_id="default",
|
||||
tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"],
|
||||
)
|
||||
|
||||
workflow = FunctionAgent(
|
||||
tools=tools,
|
||||
llm=llm,
|
||||
system_prompt="You are an agent that perform github actions.",
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
result = await workflow.run(
|
||||
user_msg="Hello! I would like to star a repo composiohq/composio on GitHub"
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-llamaindex"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get array of tools with LlamaIndex Agent Workflows."
|
||||
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 = [
|
||||
"llama_index>=0.14.22",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Setup configuration for Composio LangGraph plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_llamaindex",
|
||||
version="0.17.1",
|
||||
author="composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get array of tools with LlamaIndex Agent Workflows",
|
||||
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=["llama-index>=0.14.22", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
# composio-openai
|
||||
|
||||
Adapts Composio tools to OpenAI function calling, for both the Responses API and the Chat Completions API.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-openai openai
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (create one at https://dashboard.composio.dev/settings) and `OPENAI_API_KEY` in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
This package exports two providers: `OpenAIResponsesProvider` for the [Responses API](https://platform.openai.com/docs/api-reference/responses) and `OpenAIProvider` for Chat Completions. Both are non-agentic: the model returns tool calls, you execute them with `handle_tool_calls`, and you feed the results back.
|
||||
|
||||
```python
|
||||
import json
|
||||
from openai import OpenAI
|
||||
from composio import Composio
|
||||
from composio_openai import OpenAIResponsesProvider
|
||||
|
||||
composio = Composio(provider=OpenAIResponsesProvider())
|
||||
client = OpenAI()
|
||||
|
||||
# Create a session for your user
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
response = client.responses.create(
|
||||
model="gpt-5.2",
|
||||
tools=tools,
|
||||
input=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
# Agentic loop: keep executing tool calls until the model responds with text
|
||||
while True:
|
||||
tool_calls = [o for o in response.output if o.type == "function_call"]
|
||||
if not tool_calls:
|
||||
break
|
||||
results = composio.provider.handle_tool_calls(response=response, user_id="user_123")
|
||||
response = client.responses.create(
|
||||
model="gpt-5.2",
|
||||
tools=tools,
|
||||
previous_response_id=response.id,
|
||||
input=[
|
||||
{"type": "function_call_output", "call_id": tool_calls[i].call_id, "output": json.dumps(result)}
|
||||
for i, result in enumerate(results)
|
||||
]
|
||||
)
|
||||
|
||||
# Print final response
|
||||
for item in response.output:
|
||||
if item.type == "message":
|
||||
print(item.content[0].text)
|
||||
```
|
||||
|
||||
## Chat Completions
|
||||
|
||||
`OpenAIProvider` targets `client.chat.completions.create` and is the Composio SDK default, so `Composio()` with no provider uses it. The loop is the same shape: call `handle_tool_calls` on each response, append the results as `tool` messages, and call the API again. See the [docs page](https://docs.composio.dev/docs/providers/openai) for the full example.
|
||||
|
||||
## Links
|
||||
|
||||
- OpenAI provider docs: https://docs.composio.dev/docs/providers/openai
|
||||
- Composio docs: https://docs.composio.dev
|
||||
@@ -0,0 +1,3 @@
|
||||
from composio_openai.provider import OpenAIProvider, OpenAIResponsesProvider
|
||||
|
||||
__all__ = ("OpenAIProvider", "OpenAIResponsesProvider")
|
||||
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
OpenAI Provider for composio SDK.
|
||||
"""
|
||||
|
||||
from composio.core.provider._openai import OpenAIProvider
|
||||
from composio.core.provider._openai_responses import OpenAIResponsesProvider
|
||||
|
||||
__all__ = ["OpenAIProvider", "OpenAIResponsesProvider"]
|
||||
@@ -0,0 +1,64 @@
|
||||
from datetime import datetime
|
||||
|
||||
from composio_openai import OpenAIProvider
|
||||
from openai import OpenAI
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize tools.
|
||||
openai_client = OpenAI()
|
||||
composio = Composio(provider=OpenAIProvider())
|
||||
|
||||
# Retrieve actions
|
||||
actions = composio.tools.get(
|
||||
user_id="default",
|
||||
tools=["NOTION_ADD_PAGE_CONTENT"],
|
||||
)
|
||||
|
||||
# Setup openai assistant
|
||||
assistant_instruction = (
|
||||
"You are a super intelligent personal assistant."
|
||||
+ "You have been given a set of tools that you are supposed to choose from."
|
||||
+ "You decide the right tool and execute it."
|
||||
)
|
||||
# Prepare assistant
|
||||
assistant = openai_client.beta.assistants.create(
|
||||
name="Personal Assistant",
|
||||
instructions=assistant_instruction,
|
||||
model="gpt-5",
|
||||
tools=actions, # type: ignore
|
||||
)
|
||||
|
||||
# Give a task to execute via Openai Assistants
|
||||
my_task = (
|
||||
f"Can you copy all the events in coming week from google calendar to notion? "
|
||||
f"Today's date is {datetime.now()} and day is {datetime.now().strftime('%A')}"
|
||||
)
|
||||
|
||||
# create a thread
|
||||
thread = openai_client.beta.threads.create()
|
||||
print("Thread ID: ", thread.id)
|
||||
print("Assistant ID: ", assistant.id)
|
||||
|
||||
# start the asssitant with my task
|
||||
message = openai_client.beta.threads.messages.create(
|
||||
thread_id=thread.id,
|
||||
role="user",
|
||||
content=my_task,
|
||||
)
|
||||
|
||||
# Execute Agent with integrations
|
||||
run = openai_client.beta.threads.runs.create(
|
||||
thread_id=thread.id,
|
||||
assistant_id=assistant.id,
|
||||
)
|
||||
|
||||
# Execute function calls
|
||||
run_after_tool_calls = composio.provider.wait_and_handle_assistant_tool_calls(
|
||||
user_id="default",
|
||||
client=openai_client,
|
||||
run=run,
|
||||
thread=thread,
|
||||
)
|
||||
|
||||
print(run_after_tool_calls)
|
||||
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
OpenAI demo.
|
||||
"""
|
||||
|
||||
from composio_openai import OpenAIProvider
|
||||
from openai import OpenAI
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize tools.
|
||||
openai_client = OpenAI()
|
||||
composio = Composio(provider=OpenAIProvider())
|
||||
|
||||
# Define task.
|
||||
task = "Star a repo composiohq/composio on GitHub"
|
||||
|
||||
# Get GitHub tools that are pre-configured
|
||||
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
||||
|
||||
# Get response from the LLM
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
tools=tools,
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": task},
|
||||
],
|
||||
)
|
||||
print(response)
|
||||
|
||||
# Execute the function calls.
|
||||
result = composio.provider.handle_tool_calls(response=response, user_id="default")
|
||||
print(result)
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-openai"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get an array of tools with your OpenAI Function Call."
|
||||
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 = [
|
||||
"openai>=2.42.0",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Setup configuration for Composio OpenAI plugin.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="composio_openai",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get an array of tools with your OpenAI Function Call.",
|
||||
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=["openai>=2.42.0", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
Generated
+615
@@ -0,0 +1,615 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.10, <4"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.14.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "idna" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.6.17"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio"
|
||||
version = "0.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "composio-client" },
|
||||
{ name = "json-schema-to-pydantic" },
|
||||
{ name = "openai" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pysher" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/58/c6/5d1e9b111776bbc3685e5f6122032c26fa4f165cd5dd12dd315bf4411843/composio-0.15.0.tar.gz", hash = "sha256:b6deb3a12e021799826fb191cb74b9c3fdf9555ba908a832ec6b1a6b63d4da17", size = 209932, upload-time = "2026-06-18T08:35:54.892Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/75/ceca9c76c5df4bb34004b58b9f27f0ceb1f803efa261a8ee768ce4bfcbdb/composio-0.15.0-py3-none-any.whl", hash = "sha256:1c51dfb7a4e291a353accaea8d090e2b630361a9b7398502f604cddfb93a8b6a", size = 141009, upload-time = "2026-06-18T08:35:42.187Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio-client"
|
||||
version = "1.39.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "distro" },
|
||||
{ name = "httpx" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0d/78/efc71b2df0d580c4550ee60644193644ae3ddc06fc89f3fc92998db6393c/composio_client-1.39.0.tar.gz", hash = "sha256:2086493925117b956a3166da18414925cb50829172c8211591137fcdc3d60c7d", size = 248246, upload-time = "2026-05-12T14:28:13.071Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/59/f4ddca56b9f02c8fed3277584111a687c9be986d0d2e8b421c80a20caa03/composio_client-1.39.0-py3-none-any.whl", hash = "sha256:bf6050dc0d523bd1e9d52e39e162f5b23bd029aa441b5c4a18e1751aae58571d", size = 284163, upload-time = "2026-05-12T14:28:11.681Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio-openai"
|
||||
version = "0.16.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "composio" },
|
||||
{ name = "openai" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "composio" },
|
||||
{ name = "openai", specifier = ">=2.42.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distro"
|
||||
version = "1.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "certifi" },
|
||||
{ name = "httpcore" },
|
||||
{ name = "idna" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiter"
|
||||
version = "0.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/da/76a2c7e510ba15fe323d9509c223ab272da79ea59f54488f4a78da6426db/jiter-0.15.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:edebcf7d1f601199084bb6e844d7dc67e03e04f6ac786b0332d616635c4ff7a4", size = 310849, upload-time = "2026-05-19T10:06:51.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/8e/827be942883a4dc0862c48626ff41af3320b1902d136a0bf4b9041f2c567/jiter-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f924585cdacf631cd382b657966847bb537bf9ed0a6f9b991da5f05a631480f", size = 314991, upload-time = "2026-05-19T10:06:53.522Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/38/be2832be361ba1b9517c76f46d30b64e985be1dd43c974f4c3a4b1844436/jiter-0.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abbf258599526ad0326fe51e252e24f2bd6f24f1852681b4b78feda3808f1d18", size = 340843, upload-time = "2026-05-19T10:06:55.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/d8/90f01fb83c0c7ba509303ec93e32a308fbfa167d264860b01c0fd0dbbd06/jiter-0.15.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c468136b8bd6bb18c8786e4236a1fa27362f24cb23450ba0cb204ab379b8e6f", size = 365116, upload-time = "2026-05-19T10:06:56.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/38/94593d34f8c67a0b6f6cbc027f016ffa9780b3a858a7a86f6fd7a15bcc1e/jiter-0.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05906b93d72f03339e6bb7cf8dc10ebda64a0266126eed6beba79e20abcf5fd4", size = 457970, upload-time = "2026-05-19T10:06:58.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/04/d79962dd49d00c97e2a9b4cacea1947904d02135936960351f9a96d4c1a6/jiter-0.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30ce785d2adb8e32c3f7741442370a74834ec4c01f3c48f0750227a0b4ef27d6", size = 375744, upload-time = "2026-05-19T10:07:00.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/2e/5d37abe2be0e819c21e2338bebd410e481763ce526a9138c8c3652fa0123/jiter-0.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd73e3da91a0a722d67165e849ce2cdc10de0e0d48738c142be8c6c5f310f4c", size = 349609, upload-time = "2026-05-19T10:07:01.829Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/90/98768ad2ed90c1fda15d64157de2dfbf73c1c074d4b1bfaca915480bc7cf/jiter-0.15.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:ceb8fc27d38793f9c97149be8302720c5b22e5c195a37bf2c45dc36c4600a512", size = 354366, upload-time = "2026-05-19T10:07:03.587Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/c4/fbfb806209f1fe4b7dccdfb07bc62bb044300734a945b06fd64db446ef6a/jiter-0.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d726e3ceeb337191324b49de298142f27c3ad10886341555d1d5315b5f252c6a", size = 393519, upload-time = "2026-05-19T10:07:05.08Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/1c/b9c257cd70cb453b6d10f3ebf0402cdb11669ab455389096f09839670290/jiter-0.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c8aea7781d2a372227871de4e1a1332aa96f5a89fd76c5e835dafdbad102887", size = 519952, upload-time = "2026-05-19T10:07:06.589Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/1a/aa85027db7ab15829c12feebbc33b404f53fc399bd559d85fd0d6365ff0d/jiter-0.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf4bd113a69c0a740e27cb962ce10630c36d2b8f59d759a651b955ee9d18a823", size = 550770, upload-time = "2026-05-19T10:07:08.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/54/8c3f65c8a5687925e84708f19d63f7f37d28e2b86a48d951702ad94424d8/jiter-0.15.0-cp310-cp310-win32.whl", hash = "sha256:d92a5cd21fdb083931d546c207aa29633787c5dc5b02daab2d32b843f88a2c53", size = 209303, upload-time = "2026-05-19T10:07:10.006Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/72/0528a1eb9f42dd2d8228a0711458628f35924d131f623eaebc35fd23d3d4/jiter-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:e58585a58209d72691ce2d62a9147445f5a87beb0bde97fde284c96ae392a3d1", size = 200404, upload-time = "2026-05-19T10:07:11.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/13/daa722f5765c393576f466378f9dfd29d77c9bed939e0688f96afa3601ea/jiter-0.15.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0f862193b8696249d22ec433e85fd2ab0ad9596bc3e45e6c0bc55e8aeba97be2", size = 310899, upload-time = "2026-05-19T10:07:12.89Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/82/2d2551829b082f4b6d82b9f939b031fb808a10aab1ec0664f82e150bb9a2/jiter-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1303d4d68a9b051ea90502402063ecf3807da00ad2affa19ca1ae3b90b3c5f67", size = 314963, upload-time = "2026-05-19T10:07:14.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/0a/8b1a51466f7fe9f31dbe4bc7e0ca848674f9825e0f737b929b97e8c60aa7/jiter-0.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392b8ab019e5502d08aff85c6272209c24bc2cbe706ea82a56368f524236614a", size = 341730, upload-time = "2026-05-19T10:07:15.869Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/2a/e71dea19822e2e404e83992a08c1d6b9b617bb944f28c9c2fbd85d02c91e/jiter-0.15.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:773b6eb282ce11ee19f05f6b2d4404fa308e5bbd353b0b80a0262caad6db2cd7", size = 366214, upload-time = "2026-05-19T10:07:17.259Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/59/97e1fa539d124a509a00ab7f669289d1c1d236ecabf12948a18f16c91082/jiter-0.15.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2c0c44d569ce0f2850f5c926f8caeb5f245fbc84475aeb36efccc2103e6dbd", size = 459527, upload-time = "2026-05-19T10:07:18.741Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/7a/4a68d331aef8cf2e2393c14a3aacb635c62aa86071b0229899fb5baaa907/jiter-0.15.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:032396229564bca02440396bd327710719f724f5e7b7e9f7a8eb3faa4a2c2281", size = 375451, upload-time = "2026-05-19T10:07:20.208Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/7e/1c445c2b6f0e30a274dc8082e0c3c7825411cce80d726bccd697c98cc8d3/jiter-0.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d37768fce7f88dd2a8c6091f2325dea27d30d30d5c6e7a1c0f0af77723b708", size = 349428, upload-time = "2026-05-19T10:07:22.372Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/94/e20d38984fc17a636371bffd2ae0f698124fdc8e75ef969cd2da6ba7cea7/jiter-0.15.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2c9cb907439d20bd0c7d7565ca01ee52234203208433749bae5b516907526928", size = 355405, upload-time = "2026-05-19T10:07:23.916Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/fa/4d09f814779d0ea80a28ed8e4c6662ec9a4a8ecef0ac52190ebac6262d14/jiter-0.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9100ddbec09741cc66feb0fc6773f8bdbd0e3c345689368f260082ff85dcc0cd", size = 393688, upload-time = "2026-05-19T10:07:25.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/9d/8eb5d4fb8bf7e93a75964a5da71a75c67c864baf7fa3f98598187b3c7e57/jiter-0.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ae1b0d82ac2d987f9ea512b1c9adfcc71a28de3dea3a6039b54d76cffda9901e", size = 520853, upload-time = "2026-05-19T10:07:27.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/2c/5e07874e59e623a943a0acf1552a80d05b70f31b402287a8fc6d7ec634c7/jiter-0.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8020c99ec13a7db2b6f96cbe82ef4721c88b426a4892f27478044af0284615ef", size = 551016, upload-time = "2026-05-19T10:07:28.846Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/ed/d2d34422143474cadc15b60d482b1c35683dbc5c63c24346ddd0df09bcaf/jiter-0.15.0-cp311-cp311-win32.whl", hash = "sha256:42bfb257930800cf43e7c62c832402c704ab60797c992faf88d20e903eac8f32", size = 209518, upload-time = "2026-05-19T10:07:30.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/7d/52778b930e5cc3e52a37d950b1c10494244308b4329b25a0ff0d88303a81/jiter-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:860a74063284a2ae9bfedd694f299cc2c68e2696c5f3d440cc9d18bb81b9dd04", size = 200565, upload-time = "2026-05-19T10:07:32.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/4f/d9b4067feb69b3fa6eb0488e1b59e2ad5b463fe39f59e527eab2aca00bb0/jiter-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:37a10c377ce3a4a85f4a67f28b7afe093154cde77eaf248a72e856aa08b4d865", size = 195488, upload-time = "2026-05-19T10:07:33.846Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/43/1fc62172aa98b50a7de9a25554060db510f85c89cfbed0dfe13e1907a139/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:411fa4dfa5a7ae3d11491027ffb9beadec3996010a986862db70d91abba1c750", size = 305585, upload-time = "2026-05-19T10:09:35.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/c4/dd58fcd9e2df83666e5c1c1347bef58ce919cd8efc3ffa38aeea62ce493b/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:2b0074e2f56eb2dacca1689760fd2852a068f85a0547a157b82cb4cafeb6768b", size = 306936, upload-time = "2026-05-19T10:09:37.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/86/b695e16f1180c07f43ea98e73ecd21cf63fa2e1b0c1103739013784d11ae/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913d02d29c9606643418d9ccfc3b72492ab25a6bf7889934e09a3490f8d3438b", size = 342453, upload-time = "2026-05-19T10:09:39.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/56/55d76614af37fe3f22a3347d1e410d2a15da581997cb2da499a625000bb5/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15d3ec9b0449c40e85319bdb4caa8b77ab526e74f5532ed94bec15e2f66822c", size = 345606, upload-time = "2026-05-19T10:09:40.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json-schema-to-pydantic"
|
||||
version = "0.4.11"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/d8/423895b918706c80db1cee679c13fbe810200b9a9d9a9442c7a58d35c3f2/json_schema_to_pydantic-0.4.11.tar.gz", hash = "sha256:35448ed711a28dd33396b095c8492939b4925aa30eb31942e9b8e08d04279465", size = 56597, upload-time = "2026-03-09T20:53:55.692Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/64/7cfeb8c6d2a5e73e0f8d732032aa62be9a7724c04beb461d677de0b4beb3/json_schema_to_pydantic-0.4.11-py3-none-any.whl", hash = "sha256:da2ccc39d070ee03dbcf0517d16720e3e33f7aa8d61257ace09af8c51bd46348", size = 17842, upload-time = "2026-03-09T20:53:54.576Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "2.43.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "distro" },
|
||||
{ name = "httpx" },
|
||||
{ name = "jiter" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/fa/88d0c58a0c58df7e6758e66b99c5d028d5e0bb49f8812d7203940cd9dbf1/openai-2.43.0.tar.gz", hash = "sha256:e74d238200a26868977002190fb6631613480a93dfe0c9c982e77021ed60a017", size = 785369, upload-time = "2026-06-17T17:06:56.06Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/d2/ba767f4bbb30776c03d40906a2d3afad716a165ffa1771fc23b8992f7920/openai-2.43.0-py3-none-any.whl", hash = "sha256:65a670b54fadf2268c9e1330133373c963eb779ee969e5cbad419ec2c21dce97", size = 1355077, upload-time = "2026-06-17T17:06:53.614Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.13.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.46.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pysher"
|
||||
version = "1.0.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "requests" },
|
||||
{ name = "websocket-client" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/12/a0/d0638470df605ce266991fb04f74c69ab1bed3b90ac3838e9c3c8b69b66a/Pysher-1.0.8.tar.gz", hash = "sha256:7849c56032b208e49df67d7bd8d49029a69042ab0bb45b2ed59fa08f11ac5988", size = 9071, upload-time = "2022-10-10T13:41:09.936Z" }
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.34.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.68.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/87/d7/0535a28b1f5f24f6612fb3ff1e89fb1a8d160fee0f976e0aa6803862134b/tqdm-4.68.3.tar.gz", hash = "sha256:00dfa48452b6b6cfae3dd9885636c23d3422d1ec97c66d96818cbd5e0821d482", size = 170596, upload-time = "2026-06-17T07:36:52.105Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-inspection"
|
||||
version = "0.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
version = "1.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" },
|
||||
]
|
||||
@@ -0,0 +1,41 @@
|
||||
# composio-openai-agents
|
||||
|
||||
Adapts Composio tools to the [OpenAI Agents](https://github.com/openai/openai-agents-python) framework's native tool format, so your agents can take action across 1000+ apps.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install composio composio-openai-agents openai-agents
|
||||
```
|
||||
|
||||
Set `COMPOSIO_API_KEY` (from the [dashboard](https://dashboard.composio.dev/settings)) and `OPENAI_API_KEY` in your environment.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```python
|
||||
from composio import Composio
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
from agents import Agent, Runner
|
||||
|
||||
composio = Composio(provider=OpenAIAgentsProvider())
|
||||
|
||||
# Each session is scoped to one of your users
|
||||
session = composio.create(user_id="user_123")
|
||||
tools = session.tools()
|
||||
|
||||
agent = Agent(
|
||||
name="Personal Assistant",
|
||||
instructions="You are a helpful assistant. Use Composio tools to take action.",
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
result = Runner.run_sync(starting_agent=agent, input="Summarize my emails from today")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
By default a session gets meta tools that discover, authenticate, and execute app tools at runtime. For multi-turn use, store `session.session_id` and reuse it with `composio.use(session_id)` instead of calling `create()` again.
|
||||
|
||||
## Links
|
||||
|
||||
- [Quickstart](https://docs.composio.dev/docs/quickstart)
|
||||
- [Composio documentation](https://docs.composio.dev)
|
||||
@@ -0,0 +1,3 @@
|
||||
from composio_openai_agents.provider import OpenAIAgentsProvider
|
||||
|
||||
__all__ = ("OpenAIAgentsProvider",)
|
||||
@@ -0,0 +1,143 @@
|
||||
import asyncio
|
||||
import copy
|
||||
import json
|
||||
import typing as t
|
||||
|
||||
import pydantic
|
||||
from agents import FunctionTool
|
||||
|
||||
from composio.core.provider import AgenticProvider
|
||||
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
||||
from composio.types import Tool
|
||||
from composio.utils.pydantic import parse_pydantic_error
|
||||
from composio.utils.shared import normalize_tool_arguments
|
||||
|
||||
|
||||
# Recursively remove 'examples' keys from the schema properties
|
||||
def _remove_examples_from_schema(schema_obj: t.Dict[str, t.Any]) -> None:
|
||||
"""
|
||||
Remove 'examples', 'pattern', and 'default' keys from all properties in the
|
||||
schema, including nested ones. Also ensure that any 'items' object has a 'type' key.
|
||||
"""
|
||||
# Handle properties directly
|
||||
if "properties" in schema_obj and isinstance(schema_obj["properties"], dict):
|
||||
for _, prop_value in schema_obj["properties"].items():
|
||||
if isinstance(prop_value, dict):
|
||||
# Remove examples, pattern, and default from this property
|
||||
if "examples" in prop_value:
|
||||
del prop_value["examples"]
|
||||
if "pattern" in prop_value:
|
||||
del prop_value["pattern"]
|
||||
if "default" in prop_value:
|
||||
del prop_value["default"]
|
||||
|
||||
# Ensure 'items' has a 'type' key
|
||||
if "items" in prop_value and isinstance(prop_value["items"], dict):
|
||||
if "type" not in prop_value["items"]:
|
||||
# Default to string type for items if not specified
|
||||
prop_value["items"]["type"] = "string"
|
||||
|
||||
# Recursively process nested properties
|
||||
_remove_examples_from_schema(prop_value)
|
||||
|
||||
# Handle array items
|
||||
if "items" in schema_obj and isinstance(schema_obj["items"], dict):
|
||||
if "examples" in schema_obj["items"]:
|
||||
del schema_obj["items"]["examples"]
|
||||
if "pattern" in schema_obj["items"]:
|
||||
del schema_obj["items"]["pattern"]
|
||||
if "default" in schema_obj["items"]:
|
||||
del schema_obj["items"]["default"]
|
||||
# Ensure items has a type
|
||||
if "type" not in schema_obj["items"]:
|
||||
schema_obj["items"]["type"] = "string"
|
||||
_remove_examples_from_schema(schema_obj["items"])
|
||||
|
||||
# Handle any other nested object properties
|
||||
for _, value in schema_obj.items():
|
||||
if isinstance(value, dict):
|
||||
_remove_examples_from_schema(value)
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, dict):
|
||||
_remove_examples_from_schema(item)
|
||||
|
||||
|
||||
class OpenAIAgentsProvider(
|
||||
AgenticProvider[FunctionTool, list[FunctionTool]],
|
||||
name="openai_agents",
|
||||
):
|
||||
"""
|
||||
Composio toolset for OpenAI Agents framework.
|
||||
"""
|
||||
|
||||
def wrap_tool(
|
||||
self,
|
||||
tool: Tool,
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> FunctionTool:
|
||||
"""Wrap a tool as a FunctionTool."""
|
||||
|
||||
# Create a function that accepts explicit JSON string for parameters
|
||||
# This avoids the issue with **kwargs in schema validation
|
||||
async def execute_tool_wrapper(_ctx, payload):
|
||||
"""Execute Composio action with the given arguments."""
|
||||
try:
|
||||
return json.dumps(
|
||||
obj=(
|
||||
await asyncio.to_thread( # Running a thread since `execute_tool` is not async
|
||||
execute_tool,
|
||||
slug=tool.slug,
|
||||
# Models occasionally emit arguments as a JSON string (issue #2406).
|
||||
arguments=normalize_tool_arguments(payload),
|
||||
)
|
||||
)
|
||||
)
|
||||
except pydantic.ValidationError as e:
|
||||
return json.dumps(
|
||||
{
|
||||
"successful": False,
|
||||
"error": parse_pydantic_error(e),
|
||||
"data": None,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{
|
||||
"successful": False,
|
||||
"error": str(e),
|
||||
"data": None,
|
||||
}
|
||||
)
|
||||
|
||||
# Ensure the schema has additionalProperties set to false
|
||||
# this is required by OpenAI's function validation.
|
||||
# Deep-copy so the in-place edits below (additionalProperties + the
|
||||
# examples/pattern/default removal) never mutate the caller's
|
||||
# Tool.input_parameters, matching the deepcopy contract in
|
||||
# composio.utils.shared.
|
||||
modified_schema = copy.deepcopy(tool.input_parameters)
|
||||
modified_schema["additionalProperties"] = False
|
||||
|
||||
# Apply the example removal function. This is done to optimize the
|
||||
# schema as much as possible for Responses API
|
||||
_remove_examples_from_schema(modified_schema)
|
||||
|
||||
# Create a custom FunctionTool with the appropriate schema
|
||||
return FunctionTool(
|
||||
name=tool.slug,
|
||||
description=tool.description,
|
||||
params_json_schema=modified_schema,
|
||||
on_invoke_tool=execute_tool_wrapper,
|
||||
# To avoid schema errors due to required flags. Composio tools
|
||||
# already process to have optimal schemas.
|
||||
strict_json_schema=False,
|
||||
)
|
||||
|
||||
def wrap_tools(
|
||||
self,
|
||||
tools: t.Sequence[Tool],
|
||||
execute_tool: AgenticProviderExecuteFn,
|
||||
) -> list[FunctionTool]:
|
||||
"""Wrap a list of tools as a list of FunctionTools."""
|
||||
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
||||
@@ -0,0 +1,37 @@
|
||||
import asyncio
|
||||
|
||||
from agents import Agent, Runner
|
||||
from composio_openai_agents import OpenAIAgentsProvider
|
||||
|
||||
from composio import Composio
|
||||
|
||||
# Initialize Composio toolset
|
||||
composio = Composio(provider=OpenAIAgentsProvider())
|
||||
|
||||
# Get all the tools
|
||||
tools = composio.tools.get(
|
||||
user_id="default",
|
||||
tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"],
|
||||
)
|
||||
|
||||
# Create an agent with the tools
|
||||
agent = Agent(
|
||||
name="GitHub Agent",
|
||||
instructions="You are a helpful assistant that helps users with GitHub tasks.",
|
||||
tools=tools,
|
||||
)
|
||||
|
||||
|
||||
# Run the agent
|
||||
async def main():
|
||||
result = await Runner.run(
|
||||
starting_agent=agent,
|
||||
input=(
|
||||
"Star the repository composiohq/composio on GitHub. If done "
|
||||
"successfully, respond with 'Action executed successfully'"
|
||||
),
|
||||
)
|
||||
print(result.final_output)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "composio-openai-agents"
|
||||
version = "0.17.1"
|
||||
description = "Use Composio to get array of strongly typed tools for OpenAI Agents"
|
||||
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 = [
|
||||
"openai-agents>=0.17.5",
|
||||
"composio",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/ComposioHQ/composio"
|
||||
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
Setup configuration for Composio OpenAI Agents plugin
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name="composio_openai_agents",
|
||||
version="0.17.1",
|
||||
author="Composio",
|
||||
author_email="tech@composio.dev",
|
||||
description="Use Composio to get array of strongly typed tools for OpenAI Agents",
|
||||
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",
|
||||
packages=find_packages(),
|
||||
install_requires=["openai-agents>=0.17.5", "composio"],
|
||||
include_package_data=True,
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
"""Tests for the OpenAI Agents provider."""
|
||||
|
||||
import copy
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from composio_openai_agents.provider import OpenAIAgentsProvider
|
||||
|
||||
|
||||
def test_wrap_tool_does_not_mutate_input_parameters():
|
||||
"""wrap_tool must not strip default/examples/pattern from the caller's schema.
|
||||
|
||||
``wrap_tool`` removes ``examples``/``pattern``/``default`` from the schema it
|
||||
hands to the OpenAI Agents SDK. It must do so on a *copy*: a shallow
|
||||
``dict.copy()`` left the nested ``properties`` dicts shared with the original,
|
||||
so those in-place deletions leaked back into the caller's
|
||||
``Tool.input_parameters``.
|
||||
"""
|
||||
tool = MagicMock(
|
||||
slug="GITHUB_GET_REPO",
|
||||
name="Github Get Repo",
|
||||
description="Get a repository",
|
||||
input_parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"owner": {"type": "string", "default": "me", "examples": ["octocat"]},
|
||||
"repo": {"type": "string", "pattern": "^[a-z]+$"},
|
||||
},
|
||||
"required": ["owner"],
|
||||
},
|
||||
)
|
||||
snapshot = copy.deepcopy(tool.input_parameters)
|
||||
|
||||
wrapped_tool = OpenAIAgentsProvider().wrap_tool(tool, lambda **kwargs: {})
|
||||
|
||||
# The caller's schema must be untouched.
|
||||
assert tool.input_parameters == snapshot
|
||||
owner = tool.input_parameters["properties"]["owner"]
|
||||
assert owner["default"] == "me"
|
||||
assert owner["examples"] == ["octocat"]
|
||||
assert tool.input_parameters["properties"]["repo"]["pattern"] == "^[a-z]+$"
|
||||
|
||||
# The provider-local schema should still be normalized for OpenAI Agents.
|
||||
assert wrapped_tool.params_json_schema == {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"owner": {"type": "string"},
|
||||
"repo": {"type": "string"},
|
||||
},
|
||||
"required": ["owner"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
Reference in New Issue
Block a user