Skip to content

Emit OpenAI strict-mode function schemas (strict: true + additionalProperties: false) from the export adapter #405

@dgenio

Description

@dgenio

Summary

Add an opt-in strict=True path to chainweaver/export/openai.py so exported function payloads satisfy OpenAI's structured-outputs requirements: strict: true on the function plus additionalProperties: false on every object schema (recursively), with all properties marked required as the spec demands.

Why this matters

OpenAI's structured-outputs / strict function calling guarantees the model returns arguments matching the schema, which is exactly the determinism story ChainWeaver sells. Today _build_payload emits a plain function schema (no strict, no additionalProperties: false), so users who paste the export into a strict tool definition get a runtime rejection. Closing this makes the export adapter a drop-in for the most reliability-conscious OpenAI users.

Current evidence

  • chainweaver/export/openai.py _build_payload produces {"type": "function", "function": {name, description, parameters}} and only schema.pop("title", None) — no strict flag and no additionalProperties: false injection.
  • chainweaver/export/_schema.py (model_input_schema_json) is the shared schema emitter where recursive transformation would live.
  • examples/export_openai_anthropic.py and docs/ (## Export adapters in README) demonstrate the adapter, so a strict variant has an obvious documentation home.

External context

OpenAI's structured-outputs guide requires additionalProperties: false on every object and all keys listed in required when strict: true; passing a schema that violates this is rejected at request time.

Proposed implementation

  1. Add strict: bool = False to flow_to_openai_function/tool_to_openai_function; thread into _build_payload.
  2. Implement a pure recursive schema transformer (in _schema.py) that, on every object node, sets additionalProperties: false and promotes all properties to required, handling $defs/$ref, anyOf/oneOf, and arrays.
  3. Document the constraint that optional fields must use nullable-with-default patterns under strict mode (OpenAI requirement) and surface a clear ExportError when a schema cannot be made strict-compatible.
  4. Mirror any reusable transform for the Anthropic adapter if applicable (note relation, do not over-scope).

AI-agent execution notes

Inspect chainweaver/export/openai.py, chainweaver/export/_schema.py, chainweaver/export/anthropic.py, existing export tests under tests/. Keep the transform pure and deterministic (no network). Edge cases: nested models via $ref, lists of models, enums, fields with existing additionalProperties. Do not change the default (non-strict) output shape.

Acceptance criteria

  • flow_to_openai_function(flow, strict=True) produces strict: true and additionalProperties: false on every object including nested $defs.
  • A round-trip test validates the payload against OpenAI's documented strict constraints (structural assertions, offline).
  • Default behavior is byte-for-byte unchanged.

Test plan

Unit tests for flat/nested/array/$ref/enum schemas under strict mode; assertion that every object node has additionalProperties: false; regression test for the non-strict default; example script update run in CI.

Documentation plan

README ## Export adapters section, examples/export_openai_anthropic.py, a cookbook note, CHANGELOG.

Migration and compatibility notes

Not expected to require migration (opt-in flag; default unchanged).

Risks and tradeoffs

Strict mode forbids genuinely-optional fields, which can surprise users with optional tool inputs — document the nullable-with-default pattern and fail loudly rather than silently producing an invalid schema.

Suggested labels

integrations, ecosystem, product

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions