Skip to content

feat(core): resilient structured output (key drift) + activate pipeline gate#117

Merged
Killian-Aidalinfo merged 2 commits into
devfrom
fix/structured-output-resilience
Jun 3, 2026
Merged

feat(core): resilient structured output (key drift) + activate pipeline gate#117
Killian-Aidalinfo merged 2 commits into
devfrom
fix/structured-output-resilience

Conversation

@Killian-Aidalinfo

Copy link
Copy Markdown
Collaborator

Problem

Structured-output agents on non-OpenAI providers (e.g. Scaleway) fail intermittently with AI_NoObjectGeneratedError when the model returns valid JSON but with drifted keys — casing/separator (documentTypedocument_type) or semantic/translated aliases (typedocument_type). These providers don't enforce responseFormat, so nothing constrains the model.

Fix (agnostic, on by default, backward-compatible)

Two resilience layers in the structured pipeline. A conformant object passes through unchanged with no extra LLM call:

  1. Deterministic key normalization — recursively remaps keys onto the schema property names, insensitive to case/separator. Fixes casing drift at zero cost.
  2. Error-driven repair retry — if required keys are still missing / enums violated, re-queries the model (default 2 attempts) reinjecting the issues + the exact expected keys. Generic net for semantic aliases.

Also fixes shouldUseStructuredPipeline, which gated on structuredOutput.type while the AI SDK's Output.object() identifies via .name === "object" — so the pipeline (and any resilience) was never selected for real usage. Now accepts type ?? name, consistent with getJsonSchemaFromStructuredOutput. This activates the structured pipeline for all non-OpenAI structured-output calls (OpenAI unchanged → native structured outputs).

New opt-out options (Agent config + per call): normalizeStructuredKeys, structuredOutputRepair.

Validation

  • tsconfig.build.json: 0 errors. Full suite: 149 passed, 7 skipped, no regressions.
  • Live (Scaleway gpt-oss-120b, real Agent.generate): drifted {type, summary} → zod-conformant {document_type, patient_name, short_summary} in one repair; with resilience disabled the drift is not corrected (reproduces the original bug).

Bumps @ai_kit/core to 1.9.0.

🤖 Generated with Claude Code

Killian-Aidalinfo and others added 2 commits June 3, 2026 20:48
…eFormat

Structured-output agents on non-OpenAI providers (e.g. Scaleway) fail
intermittently with AI_NoObjectGeneratedError when the model returns valid
JSON but with drifted keys (casing/separator like documentType↔document_type,
or semantic/translated aliases like type↔document_type). Nothing constrains
the model since these providers don't enforce responseFormat.

Adds two agnostic resilience layers to the structured pipeline, on by default
and backward-compatible (a conformant object passes through unchanged, no extra
LLM call):

1. Deterministic key normalization — recursively remaps keys onto the schema's
   property names, insensitive to case/separator. Fixes casing drift with zero
   LLM cost.
2. Error-driven repair retry — when the object still misses required keys or
   violates enums, re-queries the model (default 2 attempts) reinjecting the
   issues and the exact expected keys. Generic safety net for semantic aliases.

Also fixes shouldUseStructuredPipeline, which gated on structuredOutput.type
but the AI SDK's Output.object() identifies via .name === "object" — so the
pipeline (and thus any resilience) was never selected for real usage. Now
accepts type ?? name, consistent with getJsonSchemaFromStructuredOutput.

New opt-out options on Agent config and per call: normalizeStructuredKeys,
structuredOutputRepair. Validated live against Scaleway gpt-oss-120b: drifted
keys converge to a zod-conformant object in one repair; disabling resilience
reproduces the original failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Killian-Aidalinfo
Killian-Aidalinfo merged commit 2a814ac into dev Jun 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant