composio-openai-agents 0.7.17__py3-none-any.whl → 1.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- composio_openai_agents/__init__.py +2 -9
- composio_openai_agents/provider.py +141 -0
- {composio_openai_agents-0.7.17.dist-info → composio_openai_agents-1.0.0rc1.dist-info}/METADATA +2 -5
- composio_openai_agents-1.0.0rc1.dist-info/RECORD +6 -0
- composio_openai_agents/toolset.py +0 -267
- composio_openai_agents-0.7.17.dist-info/RECORD +0 -6
- {composio_openai_agents-0.7.17.dist-info → composio_openai_agents-1.0.0rc1.dist-info}/WHEEL +0 -0
- {composio_openai_agents-0.7.17.dist-info → composio_openai_agents-1.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,7 @@
|
|
1
|
-
from composio_openai_agents.
|
1
|
+
from composio_openai_agents.provider import OpenAIAgentsProvider
|
2
2
|
|
3
|
-
from composio import Action, App, Tag, Trigger, WorkspaceType, action
|
4
3
|
|
5
4
|
|
6
5
|
__all__ = (
|
7
|
-
"
|
8
|
-
"App",
|
9
|
-
"Tag",
|
10
|
-
"Trigger",
|
11
|
-
"WorkspaceType",
|
12
|
-
"action",
|
13
|
-
"ComposioToolSet",
|
6
|
+
"OpenAIAgentsProvider",
|
14
7
|
)
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import json
|
2
|
+
import asyncio
|
3
|
+
import typing as t
|
4
|
+
from inspect import Signature
|
5
|
+
from typing import List, cast
|
6
|
+
|
7
|
+
import pydantic
|
8
|
+
import pydantic.error_wrappers
|
9
|
+
from agents import FunctionTool, Tool
|
10
|
+
|
11
|
+
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
12
|
+
from composio.types import Tool
|
13
|
+
from composio.core.provider import AgenticProvider
|
14
|
+
from composio.utils.pydantic import parse_pydantic_error
|
15
|
+
|
16
|
+
|
17
|
+
# Recursively remove 'examples' keys from the schema properties
|
18
|
+
def _remove_examples_from_schema(schema_obj: t.Dict[str, t.Any]) -> None:
|
19
|
+
"""
|
20
|
+
Remove 'examples', 'pattern', and 'default' keys from all properties in the
|
21
|
+
schema, including nested ones. Also ensure that any 'items' object has a 'type' key.
|
22
|
+
"""
|
23
|
+
# Handle properties directly
|
24
|
+
if "properties" in schema_obj and isinstance(
|
25
|
+
schema_obj["properties"], dict
|
26
|
+
):
|
27
|
+
for _, prop_value in schema_obj["properties"].items():
|
28
|
+
if isinstance(prop_value, dict):
|
29
|
+
# Remove examples, pattern, and default from this property
|
30
|
+
if "examples" in prop_value:
|
31
|
+
del prop_value["examples"]
|
32
|
+
if "pattern" in prop_value:
|
33
|
+
del prop_value["pattern"]
|
34
|
+
if "default" in prop_value:
|
35
|
+
del prop_value["default"]
|
36
|
+
|
37
|
+
# Ensure 'items' has a 'type' key
|
38
|
+
if "items" in prop_value and isinstance(
|
39
|
+
prop_value["items"], dict
|
40
|
+
):
|
41
|
+
if "type" not in prop_value["items"]:
|
42
|
+
# Default to string type for items if not specified
|
43
|
+
prop_value["items"]["type"] = "string"
|
44
|
+
|
45
|
+
# Recursively process nested properties
|
46
|
+
_remove_examples_from_schema(prop_value)
|
47
|
+
|
48
|
+
# Handle array items
|
49
|
+
if "items" in schema_obj and isinstance(schema_obj["items"], dict):
|
50
|
+
if "examples" in schema_obj["items"]:
|
51
|
+
del schema_obj["items"]["examples"]
|
52
|
+
if "pattern" in schema_obj["items"]:
|
53
|
+
del schema_obj["items"]["pattern"]
|
54
|
+
if "default" in schema_obj["items"]:
|
55
|
+
del schema_obj["items"]["default"]
|
56
|
+
# Ensure items has a type
|
57
|
+
if "type" not in schema_obj["items"]:
|
58
|
+
schema_obj["items"]["type"] = "string"
|
59
|
+
_remove_examples_from_schema(schema_obj["items"])
|
60
|
+
|
61
|
+
# Handle any other nested object properties
|
62
|
+
for _, value in schema_obj.items():
|
63
|
+
if isinstance(value, dict):
|
64
|
+
_remove_examples_from_schema(value)
|
65
|
+
elif isinstance(value, list):
|
66
|
+
for item in value:
|
67
|
+
if isinstance(item, dict):
|
68
|
+
_remove_examples_from_schema(item)
|
69
|
+
|
70
|
+
class OpenAIAgentsProvider(
|
71
|
+
AgenticProvider[FunctionTool, list[FunctionTool]],
|
72
|
+
name="openai_agents",
|
73
|
+
):
|
74
|
+
"""
|
75
|
+
Composio toolset for OpenAI Agents framework.
|
76
|
+
"""
|
77
|
+
|
78
|
+
def wrap_tool(
|
79
|
+
self,
|
80
|
+
tool: Tool,
|
81
|
+
execute_tool: AgenticProviderExecuteFn,
|
82
|
+
) -> FunctionTool:
|
83
|
+
"""Wrap a tool as a FunctionTool."""
|
84
|
+
# Create a function that accepts explicit JSON string for parameters
|
85
|
+
# This avoids the issue with **kwargs in schema validation
|
86
|
+
async def execute_tool_wrapper(_ctx, payload):
|
87
|
+
"""Execute Composio action with the given arguments."""
|
88
|
+
try:
|
89
|
+
return json.dumps(
|
90
|
+
obj=(
|
91
|
+
await asyncio.to_thread( # Running a thread since `execute_tool` is not async
|
92
|
+
execute_tool,
|
93
|
+
slug=tool.slug,
|
94
|
+
arguments=json.loads(payload) if payload else {},
|
95
|
+
)
|
96
|
+
)
|
97
|
+
)
|
98
|
+
except pydantic.ValidationError as e:
|
99
|
+
return json.dumps(
|
100
|
+
{
|
101
|
+
"successful": False,
|
102
|
+
"error": parse_pydantic_error(e),
|
103
|
+
"data": None,
|
104
|
+
}
|
105
|
+
)
|
106
|
+
except Exception as e:
|
107
|
+
return json.dumps(
|
108
|
+
{
|
109
|
+
"successful": False,
|
110
|
+
"error": str(e),
|
111
|
+
"data": None,
|
112
|
+
}
|
113
|
+
)
|
114
|
+
|
115
|
+
# Ensure the schema has additionalProperties set to false
|
116
|
+
# this is required by OpenAI's function validation
|
117
|
+
modified_schema = tool.input_parameters.copy()
|
118
|
+
modified_schema["additionalProperties"] = False
|
119
|
+
|
120
|
+
# Apply the example removal function. This is done to optimize the
|
121
|
+
# schema as much as possible for Responses API
|
122
|
+
_remove_examples_from_schema(modified_schema)
|
123
|
+
|
124
|
+
# Create a custom FunctionTool with the appropriate schema
|
125
|
+
return FunctionTool(
|
126
|
+
name=tool.slug,
|
127
|
+
description=tool.description,
|
128
|
+
params_json_schema=modified_schema,
|
129
|
+
on_invoke_tool=execute_tool_wrapper,
|
130
|
+
# To avoid schema errors due to required flags. Composio tools
|
131
|
+
# already process to have optimal schemas.
|
132
|
+
strict_json_schema=False,
|
133
|
+
)
|
134
|
+
|
135
|
+
def wrap_tools(
|
136
|
+
self,
|
137
|
+
tools: t.Sequence[Tool],
|
138
|
+
execute_tool: AgenticProviderExecuteFn,
|
139
|
+
) -> list[FunctionTool]:
|
140
|
+
"""Wrap a list of tools as a list of FunctionTools."""
|
141
|
+
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
{composio_openai_agents-0.7.17.dist-info → composio_openai_agents-1.0.0rc1.dist-info}/METADATA
RENAMED
@@ -1,19 +1,16 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: composio_openai_agents
|
3
|
-
Version: 0.
|
3
|
+
Version: 1.0.0rc1
|
4
4
|
Summary: Use Composio to get array of strongly typed tools for OpenAI Agents
|
5
5
|
Home-page: https://github.com/ComposioHQ/composio
|
6
|
-
Author:
|
6
|
+
Author: Composio
|
7
7
|
Author-email: tech@composio.dev
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: License :: OSI Approved :: Apache Software License
|
10
10
|
Classifier: Operating System :: OS Independent
|
11
11
|
Requires-Python: >=3.9,<4
|
12
12
|
Description-Content-Type: text/markdown
|
13
|
-
Requires-Dist: composio_core<0.8.0,>=0.7.0
|
14
13
|
Requires-Dist: openai-agents>=0.0.3
|
15
|
-
Requires-Dist: pydantic>=2.0.0
|
16
|
-
Requires-Dist: typing-extensions>=4.0.0
|
17
14
|
Dynamic: author
|
18
15
|
Dynamic: author-email
|
19
16
|
Dynamic: classifier
|
@@ -0,0 +1,6 @@
|
|
1
|
+
composio_openai_agents/__init__.py,sha256=9AhhdbI4L3skTl1i9t8_enhpmh2OYitDnfAxP0R9il4,110
|
2
|
+
composio_openai_agents/provider.py,sha256=ylijMtq-Q5iK4_7FVNUZ0yteqTr-X7VC0nJGi3XguSc,5412
|
3
|
+
composio_openai_agents-1.0.0rc1.dist-info/METADATA,sha256=dgVtCwRuePx8qBP6Vzl6OyCSwsgFrUrmcloOHbsK6_o,2104
|
4
|
+
composio_openai_agents-1.0.0rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
+
composio_openai_agents-1.0.0rc1.dist-info/top_level.txt,sha256=_YKpUJOoiq4jzAZX0LC2SfaYhsCOcyNcoK9lbK5uWQw,23
|
6
|
+
composio_openai_agents-1.0.0rc1.dist-info/RECORD,,
|
@@ -1,267 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import types
|
3
|
-
import typing as t
|
4
|
-
from inspect import Signature
|
5
|
-
from typing import List, cast
|
6
|
-
|
7
|
-
import pydantic
|
8
|
-
import pydantic.error_wrappers
|
9
|
-
from agents import FunctionTool, Tool
|
10
|
-
|
11
|
-
from composio import ActionType, AppType, TagType
|
12
|
-
from composio.tools import ComposioToolSet as BaseComposioToolSet
|
13
|
-
from composio.tools.toolset import ProcessorsType
|
14
|
-
from composio.utils.pydantic import parse_pydantic_error
|
15
|
-
from composio.utils.shared import get_signature_format_from_schema_params
|
16
|
-
|
17
|
-
|
18
|
-
class ComposioToolSet(
|
19
|
-
BaseComposioToolSet,
|
20
|
-
runtime="openai_agents",
|
21
|
-
description_char_limit=1024,
|
22
|
-
action_name_char_limit=64,
|
23
|
-
):
|
24
|
-
"""
|
25
|
-
Composio toolset for OpenAI Agents framework.
|
26
|
-
|
27
|
-
Example:
|
28
|
-
```python
|
29
|
-
import os
|
30
|
-
import asyncio
|
31
|
-
import dotenv
|
32
|
-
from agents import Agent, Runner
|
33
|
-
|
34
|
-
from composio_openai_agents import App, ComposioToolSet
|
35
|
-
|
36
|
-
# Load environment variables from .env
|
37
|
-
dotenv.load_dotenv()
|
38
|
-
|
39
|
-
# Initialize Composio toolset
|
40
|
-
composio_toolset = ComposioToolSet()
|
41
|
-
|
42
|
-
# Get all the tools
|
43
|
-
tools = composio_toolset.get_tools(apps=[App.GITHUB])
|
44
|
-
|
45
|
-
# Create an agent with the tools
|
46
|
-
agent = Agent(
|
47
|
-
name="GitHub Agent",
|
48
|
-
instructions="You are a helpful assistant that helps users with GitHub tasks.",
|
49
|
-
tools=tools,
|
50
|
-
)
|
51
|
-
|
52
|
-
# Run the agent
|
53
|
-
async def main():
|
54
|
-
result = await Runner.run(agent, "Star the repository composiohq/composio on GitHub")
|
55
|
-
print(result.final_output)
|
56
|
-
|
57
|
-
asyncio.run(main())
|
58
|
-
```
|
59
|
-
"""
|
60
|
-
|
61
|
-
def _wrap_action(
|
62
|
-
self,
|
63
|
-
action: str,
|
64
|
-
description: str,
|
65
|
-
schema_params: t.Dict,
|
66
|
-
entity_id: t.Optional[str] = None,
|
67
|
-
):
|
68
|
-
def function(**kwargs: t.Any) -> t.Dict:
|
69
|
-
"""Wrapper function for composio action."""
|
70
|
-
self.logger.debug(f"Executing action: {action} with params: {kwargs}")
|
71
|
-
return self.execute_action(
|
72
|
-
action=action,
|
73
|
-
params=kwargs,
|
74
|
-
entity_id=entity_id or self.entity_id,
|
75
|
-
_check_requested_actions=True,
|
76
|
-
)
|
77
|
-
|
78
|
-
action_func = types.FunctionType(
|
79
|
-
function.__code__,
|
80
|
-
globals=globals(),
|
81
|
-
name=action,
|
82
|
-
closure=function.__closure__,
|
83
|
-
)
|
84
|
-
# Using setattr to avoid type checking errors
|
85
|
-
setattr(
|
86
|
-
action_func,
|
87
|
-
"__signature__",
|
88
|
-
Signature(
|
89
|
-
parameters=get_signature_format_from_schema_params(
|
90
|
-
schema_params=schema_params
|
91
|
-
)
|
92
|
-
),
|
93
|
-
)
|
94
|
-
|
95
|
-
# Using setattr to avoid type checking errors
|
96
|
-
setattr(action_func, "__doc__", description)
|
97
|
-
|
98
|
-
return action_func
|
99
|
-
|
100
|
-
def _wrap_tool(
|
101
|
-
self,
|
102
|
-
schema: t.Dict[str, t.Any],
|
103
|
-
entity_id: t.Optional[str] = None,
|
104
|
-
) -> FunctionTool:
|
105
|
-
"""Wraps composio tool as OpenAI Agents FunctionTool object."""
|
106
|
-
action = schema["name"]
|
107
|
-
description = schema["description"]
|
108
|
-
schema_params = schema["parameters"]
|
109
|
-
|
110
|
-
# Create a function that accepts explicit JSON string for parameters
|
111
|
-
# This avoids the issue with **kwargs in schema validation
|
112
|
-
async def execute_action_wrapper(_ctx, args_json):
|
113
|
-
"""Execute Composio action with the given arguments."""
|
114
|
-
try:
|
115
|
-
kwargs = json.loads(args_json) if args_json else {}
|
116
|
-
|
117
|
-
result = self.execute_action(
|
118
|
-
action=action,
|
119
|
-
params=kwargs,
|
120
|
-
entity_id=entity_id or self.entity_id,
|
121
|
-
_check_requested_actions=True,
|
122
|
-
)
|
123
|
-
|
124
|
-
# Serialize the result to JSON string
|
125
|
-
# The OpenAI API expects strings for tool outputs
|
126
|
-
if not isinstance(result, dict):
|
127
|
-
result_dict = {"result": result}
|
128
|
-
else:
|
129
|
-
result_dict = result
|
130
|
-
|
131
|
-
# Convert to JSON string
|
132
|
-
return json.dumps(result_dict)
|
133
|
-
|
134
|
-
except pydantic.ValidationError as e:
|
135
|
-
error_msg = parse_pydantic_error(e)
|
136
|
-
return json.dumps(
|
137
|
-
{
|
138
|
-
"successful": False,
|
139
|
-
"error": error_msg,
|
140
|
-
"data": None,
|
141
|
-
}
|
142
|
-
)
|
143
|
-
except Exception as e:
|
144
|
-
return json.dumps(
|
145
|
-
{
|
146
|
-
"successful": False,
|
147
|
-
"error": str(e),
|
148
|
-
"data": None,
|
149
|
-
}
|
150
|
-
)
|
151
|
-
|
152
|
-
# Ensure the schema has additionalProperties set to false
|
153
|
-
# This is required by OpenAI's function validation
|
154
|
-
modified_schema = schema_params.copy()
|
155
|
-
modified_schema["additionalProperties"] = False
|
156
|
-
|
157
|
-
# Recursively remove 'examples' keys from the schema properties
|
158
|
-
def remove_examples_from_schema(schema_obj: t.Dict[str, t.Any]) -> None:
|
159
|
-
"""Remove 'examples', 'pattern', and 'default' keys from all properties in the schema, including nested ones.
|
160
|
-
Also ensure that any 'items' object has a 'type' key."""
|
161
|
-
# Handle properties directly
|
162
|
-
if "properties" in schema_obj and isinstance(
|
163
|
-
schema_obj["properties"], dict
|
164
|
-
):
|
165
|
-
for _, prop_value in schema_obj["properties"].items():
|
166
|
-
if isinstance(prop_value, dict):
|
167
|
-
# Remove examples, pattern, and default from this property
|
168
|
-
if "examples" in prop_value:
|
169
|
-
del prop_value["examples"]
|
170
|
-
if "pattern" in prop_value:
|
171
|
-
del prop_value["pattern"]
|
172
|
-
if "default" in prop_value:
|
173
|
-
del prop_value["default"]
|
174
|
-
|
175
|
-
# Ensure 'items' has a 'type' key
|
176
|
-
if "items" in prop_value and isinstance(
|
177
|
-
prop_value["items"], dict
|
178
|
-
):
|
179
|
-
if "type" not in prop_value["items"]:
|
180
|
-
# Default to string type for items if not specified
|
181
|
-
prop_value["items"]["type"] = "string"
|
182
|
-
|
183
|
-
# Recursively process nested properties
|
184
|
-
remove_examples_from_schema(prop_value)
|
185
|
-
|
186
|
-
# Handle array items
|
187
|
-
if "items" in schema_obj and isinstance(schema_obj["items"], dict):
|
188
|
-
if "examples" in schema_obj["items"]:
|
189
|
-
del schema_obj["items"]["examples"]
|
190
|
-
if "pattern" in schema_obj["items"]:
|
191
|
-
del schema_obj["items"]["pattern"]
|
192
|
-
if "default" in schema_obj["items"]:
|
193
|
-
del schema_obj["items"]["default"]
|
194
|
-
# Ensure items has a type
|
195
|
-
if "type" not in schema_obj["items"]:
|
196
|
-
schema_obj["items"]["type"] = "string"
|
197
|
-
remove_examples_from_schema(schema_obj["items"])
|
198
|
-
|
199
|
-
# Handle any other nested object properties
|
200
|
-
for _, value in schema_obj.items():
|
201
|
-
if isinstance(value, dict):
|
202
|
-
remove_examples_from_schema(value)
|
203
|
-
elif isinstance(value, list):
|
204
|
-
for item in value:
|
205
|
-
if isinstance(item, dict):
|
206
|
-
remove_examples_from_schema(item)
|
207
|
-
|
208
|
-
# Apply the example removal function. This is done to optimize the schema as much as possible for Responses API
|
209
|
-
remove_examples_from_schema(modified_schema)
|
210
|
-
|
211
|
-
# Create a custom FunctionTool with the appropriate schema
|
212
|
-
tool = FunctionTool(
|
213
|
-
name=action,
|
214
|
-
description=description,
|
215
|
-
params_json_schema=modified_schema,
|
216
|
-
on_invoke_tool=execute_action_wrapper,
|
217
|
-
strict_json_schema=False, # To avoid schema errors due to required flags. Composio tools already process to have optimal schemas.
|
218
|
-
)
|
219
|
-
|
220
|
-
return tool
|
221
|
-
|
222
|
-
def get_tools(
|
223
|
-
self,
|
224
|
-
actions: t.Optional[t.Sequence[ActionType]] = None,
|
225
|
-
apps: t.Optional[t.Sequence[AppType]] = None,
|
226
|
-
tags: t.Optional[t.List[TagType]] = None,
|
227
|
-
entity_id: t.Optional[str] = None,
|
228
|
-
*,
|
229
|
-
processors: t.Optional[ProcessorsType] = None,
|
230
|
-
check_connected_accounts: bool = True,
|
231
|
-
) -> List[Tool]:
|
232
|
-
"""
|
233
|
-
Get composio tools wrapped as OpenAI Agents FunctionTool objects.
|
234
|
-
|
235
|
-
:param actions: List of actions to wrap
|
236
|
-
:param apps: List of apps to wrap
|
237
|
-
:param tags: Filter the apps by given tags
|
238
|
-
:param entity_id: Entity ID for the function wrapper
|
239
|
-
:param processors: Optional request/response processors
|
240
|
-
:param check_connected_accounts: Whether to check connected accounts
|
241
|
-
|
242
|
-
:return: Composio tools wrapped as `FunctionTool` objects
|
243
|
-
"""
|
244
|
-
self.validate_tools(apps=apps, actions=actions, tags=tags)
|
245
|
-
if processors is not None:
|
246
|
-
self._processor_helpers.merge_processors(processors)
|
247
|
-
|
248
|
-
# Create the tools as FunctionTools
|
249
|
-
function_tools = [
|
250
|
-
self._wrap_tool(
|
251
|
-
schema=tool.model_dump(
|
252
|
-
exclude_none=True,
|
253
|
-
),
|
254
|
-
entity_id=entity_id or self.entity_id,
|
255
|
-
)
|
256
|
-
for tool in self.get_action_schemas(
|
257
|
-
actions=actions,
|
258
|
-
apps=apps,
|
259
|
-
tags=tags,
|
260
|
-
check_connected_accounts=check_connected_accounts,
|
261
|
-
_populate_requested=True,
|
262
|
-
)
|
263
|
-
]
|
264
|
-
|
265
|
-
# Cast the list to List[Tool] to satisfy type checking
|
266
|
-
# Since FunctionTool is a subclass of Tool, this is type-safe
|
267
|
-
return cast(List[Tool], function_tools)
|
@@ -1,6 +0,0 @@
|
|
1
|
-
composio_openai_agents/__init__.py,sha256=E_-3XZ6yN4oM-yHfu3rJ0gHuLnfdDujYxT2XuZAcq1U,255
|
2
|
-
composio_openai_agents/toolset.py,sha256=Jz9f1JvHO0dFT26HqPpqfeRr6Q9GG7rNj8UUNzUgVxg,9852
|
3
|
-
composio_openai_agents-0.7.17.dist-info/METADATA,sha256=5KfPseCMRdIjQEHVbOHJwiPugUmbmpwiV_Q1Z2KNRvM,2217
|
4
|
-
composio_openai_agents-0.7.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
-
composio_openai_agents-0.7.17.dist-info/top_level.txt,sha256=_YKpUJOoiq4jzAZX0LC2SfaYhsCOcyNcoK9lbK5uWQw,23
|
6
|
-
composio_openai_agents-0.7.17.dist-info/RECORD,,
|
File without changes
|
{composio_openai_agents-0.7.17.dist-info → composio_openai_agents-1.0.0rc1.dist-info}/top_level.txt
RENAMED
File without changes
|