mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
95b7b7f649
## Problem `OpenAIAgentsProvider.wrap_tool` mutates the caller's `Tool.input_parameters`. It shallow-copies the schema and then strips keys from the nested property dicts **in place**: ```python modified_schema = tool.input_parameters.copy() # shallow copy modified_schema["additionalProperties"] = False _remove_examples_from_schema(modified_schema) # del prop["examples"/"pattern"/"default"] ``` `_remove_examples_from_schema` recurses into `schema["properties"][*]` and does `del prop_value["examples"]`, `del prop_value["pattern"]`, `del prop_value["default"]`. Because `dict.copy()` is shallow, `modified_schema["properties"]` (and every nested property dict) is the **same object** as the original, so those deletions strip `examples`/`pattern`/**`default`** out of the caller's `Tool.input_parameters`. Since essentially every Composio tool carries `default`/`examples` on its properties, wrapping a tool silently corrupts its schema. Wrapping the same `Tool` twice, reusing it with another provider, or inspecting it after wrapping then sees the defaults gone. This is the only wrap path using a shallow `.copy()` + nested mutation — the rest of the codebase deep-copies before mutating schemas, e.g. `composio/utils/shared.py` (`schema = copy.deepcopy(schema)`, with the docstring *"The schema is deep-copied before any mutation so the caller's original is never modified."*) and `Tools._get` (`tool.model_copy(deep=True)`). ## Fix Deep-copy the schema before mutating (`copy.deepcopy(tool.input_parameters)`), so `_remove_examples_from_schema` only edits the provider-local copy. One-line change + `import copy`. ## Test Added `python/providers/openai_agents/tests/test_provider.py::test_wrap_tool_does_not_mutate_input_parameters` — builds a tool whose properties carry `default`/`examples`/`pattern`, snapshots `input_parameters`, calls `wrap_tool`, and asserts both sides of the contract: - the caller's original `Tool.input_parameters` is unchanged - the returned `FunctionTool.params_json_schema` is still normalized for OpenAI Agents (`additionalProperties: false`, with `default`/`examples`/`pattern` stripped) - **Before:** the assertion fails — `owner` loses `default`/`examples`, `repo` loses `pattern`. - **After:** `Tool.input_parameters` is untouched while the wrapped schema remains sanitized. Verification: - `uv run pytest python/providers/openai_agents/tests/test_provider.py -q` → **1 passed** - `uv run ruff check python/providers/openai_agents/composio_openai_agents/provider.py python/providers/openai_agents/tests/test_provider.py` → clean - `uv run ruff format --check python/providers/openai_agents/composio_openai_agents/provider.py python/providers/openai_agents/tests/test_provider.py` → clean Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>