2 Commits

Author SHA1 Message Date
Anas Khan 3b28dc756e fix(py): handle list-valued JSON Schema type in openapi signatures (#3822)
`_type_to_parameter` in `python/composio/utils/openapi.py` assumed a
property's
`type` was a scalar string. JSON Schema Draft 2020-12 and OpenAPI 3.1
can express
a nullable field as a list of types instead, for example
`{"type": ["string", "null"]}`. Passing that list to the scalar
membership check
raised `TypeError: unhashable type: 'list'`, which could break tool
signature
generation in providers such as `google_adk`.

## Changes

- Handle list-valued `type` definitions as unions.
- Re-run every member through `_type_to_parameter` with a copy of the
full
property schema and only `type` replaced. This preserves sibling fields
such
as an array's `items`, so `{"type": ["array", "null"], "items": ...}`
resolves
  to `Optional[List[...]]`.
- Preserve scalar error behavior for invalid member types: an unknown
type in a
  list raises `InvalidSchemaError` instead of silently becoming `Any`.
- Keep the existing empty-list fallback to `Any`.
- Add regression coverage for nullable arrays and scalar/list
unknown-type
  parity alongside the existing list-valued type cases.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

## Verification

From `python/`:

- `uv run pytest tests/test_openapi.py -q` — 18 passed.
- `make chk` — Ruff and mypy passed across the Python SDK, providers,
tests,
  examples, and scripts.

## Checklist

- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [ ] I updated documentation as needed (not applicable: internal
helper)
- [x] I added regression tests
- [ ] I added a changeset (not applicable: Python-only change)

---------

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
Signed-off-by: jkomyno <alberto@composio.dev>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-15 23:34:26 +04:00
Anas Khan c660007c71 fix(py): handle typeless and empty combiner schemas in openapi signatures (#3710)
### Summary
`function_signature_from_jsonschema` in
`python/composio/utils/openapi.py` builds the `__signature__` for tools
wrapped by the `google_adk` provider
(`providers/google_adk/composio_google_adk/provider.py:66`). It already
falls back to `Any` for a top-level property with no `type`, but the
nested combiner helpers do not, so real combiner shapes crash at wrap
time:

- a `oneOf`/`anyOf` option with no `type` key -> `KeyError: 'type'`
- an empty `oneOf`/`anyOf` list -> `TypeError: Cannot take a Union of no
types`
- an empty `allOf` list -> `KeyError: 'type'` (merges to `{}`)

This extends the existing "default to Any" intent to nested options.
Follow-up to #3708, which fixed the same bug family in `shared.py` and
noted this `openapi.py` case for a separate PR.

Fixes #

### Changes
- `_type_to_parameter`: read `schema.get("type")` instead of
`schema["type"]`; when the type is missing, return `t.Any` (mirrors the
top-level fallback). An unknown *string* type still raises
`InvalidSchemaError`.
- `_handle_composite_type`: return `t.Any` for an empty option list
instead of building an empty `Union`. Empty `allOf` merges to `{}` and
resolves to `Any` via the above.
- New `python/tests/test_openapi.py` (11 tests): this builder had none.
Asserts the five former crashes now resolve to `Any`, and that existing
shapes are unchanged.

### Type of change
- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

### How Has This Been Tested?
From `python/`:

```
uv run pytest tests/test_openapi.py -q   # 11 passed
make chk                                 # ruff + mypy -> clean
```

To confirm the tests guard the fix, the five former crashes (typeless
`anyOf`/`oneOf` option, empty `anyOf`/`oneOf`/`allOf`) raise on the
unmodified file and now resolve to `Any`; regressions cover
`anyOf:[str,null]` -> `Optional[str]`, a 4-member union, enum, array,
object, and a typeless property.

### Checklist
- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [ ] I updated documentation as needed (internal util)
- [x] I added tests (new test_openapi.py)
- [ ] I added a changeset (Python-only, not needed)

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-07-02 11:40:35 +04:00