composio-openai-agents 0.7.18__py3-none-any.whl → 1.0.0rc2__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.
@@ -1,14 +1,7 @@
1
- from composio_openai_agents.toolset import ComposioToolSet
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
- "Action",
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]
@@ -1,19 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: composio_openai_agents
3
- Version: 0.7.18
3
+ Version: 1.0.0rc2
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: Siddharth
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.0rc2.dist-info/METADATA,sha256=1y6wa6vUsf9wtiQ8pCHId4v6J1OJuiD4dSEgRtH5I4U,2104
4
+ composio_openai_agents-1.0.0rc2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ composio_openai_agents-1.0.0rc2.dist-info/top_level.txt,sha256=_YKpUJOoiq4jzAZX0LC2SfaYhsCOcyNcoK9lbK5uWQw,23
6
+ composio_openai_agents-1.0.0rc2.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.18.dist-info/METADATA,sha256=buAaAT7LlEZij2amYVT5Doj0cD5SiCbi-XZeBkZi9H8,2217
4
- composio_openai_agents-0.7.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
- composio_openai_agents-0.7.18.dist-info/top_level.txt,sha256=_YKpUJOoiq4jzAZX0LC2SfaYhsCOcyNcoK9lbK5uWQw,23
6
- composio_openai_agents-0.7.18.dist-info/RECORD,,