Skip to content

@effect/ai-anthropic: Use Anthropic's native Structured Outputs instead of tool-call emulation #6091

@davidgoli

Description

@davidgoli

What is the problem this feature would solve?

Currently, @effect/ai-anthropic's AnthropicLanguageModel emulates structured output (generateObject) by creating a forced tool call — it wraps the schema as a tool's input_schema and sets tool_choice: { type: "tool", name } (see packages/ai/anthropic/src/AnthropicLanguageModel.ts lines ~336-348).

Anthropic has since released native Structured Outputs (now GA), which provides grammar-constrained JSON generation via the output_format parameter. This native approach:

The current tool-call emulation is less reliable, doesn't benefit from constrained decoding, and diverges from how @effect/ai-openai handles structured output (which already uses OpenAI's native response_format: { type: "json_schema" }).

What is the feature you are proposing to solve the problem?

Update AnthropicLanguageModel to use Anthropic's native output_format parameter when responseFormat.type === "json":

  1. Map ProviderOptions.responseFormat to Anthropic's output_format: { type: "json_schema", json_schema: { name, schema } } instead of creating a forced tool call
  2. Update the generated API types (Generated.ts) if they don't yet include the output_format field (may require regenerating from updated Anthropic API specs)
  3. The LanguageModel.ProviderOptions.responseFormat already carries { type: "json", objectName, schema } — the mapping is straightforward
  4. Reference: @effect/ai-openai's prepareResponseFormat() function already does the equivalent for OpenAI

Anthropic API docs: https://docs.anthropic.com/en/docs/build-with-claude/structured-outputs

What alternatives have you considered?

  • Keep current tool-call emulation: Works but doesn't benefit from constrained decoding, more fragile with prompt ordering
  • User-side workaround: Could bypass generateObject and call the Anthropic API directly with output_format, but this defeats the purpose of the provider abstraction

Related issues:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions