Skip to content

feat(models): native response_format pass-through for OpenAI / OCIOpenAIModel#39

Merged
fede-kamel merged 1 commit into
mainfrom
feat/openai-structured-outputs-native
May 2, 2026
Merged

feat(models): native response_format pass-through for OpenAI / OCIOpenAIModel#39
fede-kamel merged 1 commit into
mainfrom
feat/openai-structured-outputs-native

Conversation

@fede-kamel
Copy link
Copy Markdown
Contributor

Closes #36.

Problem

When `Agent(output_schema=Pydantic)` is configured, the agent loop always falls back to prompted-JSON + post-hoc parsing — even on OpenAI / OCI-OpenAI-compat where `response_format={"type":"json_schema",...}` is supported natively.

The native pass-through path existed in the codebase but was only used in the post-loop repair flow at `agent.py:1743`, never in the main loop at `agent.py:1632`. `build_response_format()` (`src/locus/core/structured.py:257-288`) already returns the right shape; it just wasn't being called.

Fix

Add a `supports_structured_output` capability property on each provider model:

Provider Returns Reason
`OpenAIModel` `True` Native `response_format`
`OCIOpenAIModel` `True` (inherited) Same chat-completions shape on OCI
`AnthropicModel` `False` No `response_format`; prompted-JSON path
`OllamaModel` `False` Same
`OCIModel` (Cohere R-series via native SDK) `False` Different transport

In `agent.py`, the main-loop `complete()` call now gates on the capability:

```python
if self.config.output_schema is not None and getattr(
self._model, "supports_structured_output", False
):
response_format = build_response_format(
self.config.output_schema,
strict=self.config.output_schema_strict,
)
```

When set, `response_format=` is passed to `model.complete(...)` in the same kwargs path the existing repair flow already uses.

Outcome

  • OpenAI / OCI-OpenAI-compat: native pass-through. The provider returns a guaranteed-parseable Pydantic instance — no retry, lower token cost, stronger correctness guarantee.
  • Other providers: unchanged. Prompted-JSON fallback still active. Backward-compatible.

Tests

Six new tests in `tests/unit/test_native_structured_output.py`:

  • Each provider's `supports_structured_output` value verified.
  • `build_response_format(SamplePayload, strict=True)` returns the OpenAI shape.

All 3,210 pre-existing unit tests still pass; pre-commit clean; `mkdocs build --strict` clean.

Test plan

  • `hatch run test tests/unit/` — 3,210 pass + 1 skip (OCIOpenAIModel needs real config)
  • `pre-commit run --all-files` — clean
  • `hatch -e docs run mkdocs build --strict` — clean
  • DCO sign-off

…nAIModel

Closes #36.

When `Agent(output_schema=Pydantic)` is configured AND the provider
ships native OpenAI-style `response_format={"type":"json_schema",...}`,
the agent loop now passes the schema through directly. Anthropic /
Ollama / OCI native-SDK transport keep the prompted-JSON fallback —
their providers report `supports_structured_output = False`.

Before: every structured-output run paid prompted-JSON cost (schema
duplicated in the system prompt, post-hoc parse + retry on parse
failure) even on providers that support native pass-through. The
existing `build_response_format()` was only used in the post-loop
repair flow at `agent.py:1743`.

After: native providers (`OpenAIModel`, `OCIOpenAIModel` via
inheritance) get the schema in the request directly. The provider
returns a guaranteed-parseable instance. No retry, lower token cost,
stronger correctness guarantee.

Capability flag:
- `OpenAIModel.supports_structured_output → True`
- `OCIOpenAIModel` inherits from `OpenAIModel` → True
- `AnthropicModel.supports_structured_output → False` (explicit)
- `OllamaModel.supports_structured_output → False` (explicit)
- `OCIModel` (Cohere R-series via SDK transport) → False (explicit)

Six unit tests in `tests/unit/test_native_structured_output.py`
verify each provider's capability and assert
`build_response_format()` returns the OpenAI-native shape. All 3,210
pre-existing unit tests still pass.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label May 2, 2026
@fede-kamel fede-kamel merged commit caf1efc into main May 2, 2026
10 checks passed
@fede-kamel fede-kamel deleted the feat/openai-structured-outputs-native branch May 2, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Surface OpenAI structured outputs at provider layer

1 participant