Skip to content

Conversation

@aadamsx
Copy link

@aadamsx aadamsx commented Dec 23, 2025

Summary

Implements Anthropic's native structured outputs (November 2025 feature) for supported Claude models using constrained decoding.

Note: This is a resubmission of #2528 which was closed as "resolved in #2515". However, #2515 addressed memory fixes, provider typing, and cost calculation - not structured outputs. The current Anthropic provider still uses the outdated prompt-based approach. This PR adds the missing native structured outputs support.

Problem

The current Anthropic provider uses prompt-based structured output, which is unreliable:

  • Adds JSON schema instructions to the system prompt
  • Claude is "asked nicely" but not constrained
  • Often produces extra text before/after JSON, wrong fields, etc.

Solution

Use Anthropic's native structured outputs with constrained decoding for Claude 3.5+ and 4.x models:

  • Beta header: structured-outputs-2025-11-13
  • Parameter: output_format with type: "json_schema"
  • Uses grammar-based constrained decoding - guaranteed valid JSON

Changes

  • Add STRUCTURED_OUTPUT_MODEL_PREFIXES list with prefix matching to avoid false positives
  • Add supportsNativeStructuredOutput() with proper prefix-based detection
  • Add ensureAdditionalPropertiesFalse() with full schema recursion including:
    • oneOf, anyOf, allOf keywords
    • definitions and $defs for reusable schema components
  • Add createMessage() and createStreamingMessage() helpers using beta API
  • Use module-level logger (not passed as parameter)
  • Proper TypeScript type assertions with explanatory comments
  • Automatic fallback to prompt-based approach for older models (Claude 3.x)

Supported Models

Native structured outputs work with:

  • claude-3-5-haiku-*
  • claude-sonnet-4-*
  • claude-opus-4-*

Code Review Feedback Addressed

All issues from the previous review have been fixed:

  1. Model matching logic: Changed to prefix-only matching to avoid false positives
  2. Schema recursion: Added support for oneOf, anyOf, allOf, definitions, $defs
  3. Logger anti-pattern: Removed logger parameter, using module-level logger
  4. TypeScript safety: Added documented type assertions with explanatory comments

References

Implements Anthropic's native structured outputs (November 2025 feature)
for supported Claude models using constrained decoding.

Changes:
- Add STRUCTURED_OUTPUT_MODELS list for Claude 4.x models that support
  native structured outputs (haiku-4-5, sonnet-4-5, opus-4-1, opus-4-5)
- Add supportsNativeStructuredOutput() to detect supported models
- Add ensureAdditionalPropertiesFalse() to prepare schemas
- Add createMessage() and createStreamingMessage() helpers that use
  anthropic.beta.messages.create() with the structured-outputs-2025-11-13
  beta header when native structured outputs are enabled
- Automatically detect model capability and use native structured outputs
  when available, falling back to prompt-based approach for older models

Native structured outputs use grammar-based constrained decoding,
guaranteeing valid JSON output matching the specified schema.
Addresses all issues from PR review:

1. Model matching logic (false positives):
   - Changed from includes/startsWith to prefix-only matching
   - Renamed to STRUCTURED_OUTPUT_MODEL_PREFIXES for clarity
   - Added claude-3-5-haiku which supports structured outputs
   - Now correctly matches "claude-sonnet-4-*" but not "claude-3-5-sonnet"

2. Incomplete schema recursion:
   - Added support for oneOf, anyOf, allOf keywords
   - Added support for definitions/$defs (reusable schema components)
   - Now properly processes all nested object types

3. Logger parameter anti-pattern:
   - Removed logger parameter from createMessage and createStreamingMessage
   - Now uses module-level logger directly
   - Cleaner function signatures

4. TypeScript type safety:
   - Added documented type assertions with explanatory comments
   - Created explicit interface for beta.messages.create
   - Documented why cast is needed (SDK types lag behind beta features)
@vercel
Copy link

vercel bot commented Dec 23, 2025

@aadamsx is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 23, 2025

Greptile Summary

  • Adds native structured outputs support for Anthropic Claude models using November2025beta API with constrained decoding to replace unreliable prompt-based approach
  • Implements prefix-based model detection for supported models (claude-3-5-haiku-*, claude-sonnet-4-*, claude-opus-4-*) and recursive JSON schema processing with additionalProperties: false
  • Creates dual API approach with beta endpoint helpers for newer models while maintaining backward compatibility fallback for older Claude models

Important Files Changed

Filename Overview
apps/sim/providers/anthropic/index.ts Major refactor adding native structured outputs with beta API support, model capability detection, recursive schema processing, and message creation helpers

Confidence score: 2/5

  • This PR introduces significant complexity and uses beta API features which could break if Anthropic changes their API structure or removes beta support
  • Score reflects concerns about beta API stability, complex recursive schema processing logic that could have edge cases, and the module-level type assertions bypassing TypeScript safety
  • Pay close attention to the recursive ensureAdditionalPropertiesFalse function and beta API type assertions which could fail silently

Sequence Diagram

sequenceDiagram
    participant User
    participant anthropicProvider
    participant Anthropic as "Anthropic API"
    participant Tool as "Tool System"

    User->>anthropicProvider: "executeRequest(request)"
    anthropicProvider->>anthropicProvider: "Transform messages to Anthropic format"
    anthropicProvider->>anthropicProvider: "Check if model supports native structured outputs"
    
    alt Native structured outputs supported
        anthropicProvider->>anthropicProvider: "ensureAdditionalPropertiesFalse(schema)"
        anthropicProvider->>anthropicProvider: "Set useNativeStructuredOutput = true"
    else Fallback to prompt-based
        anthropicProvider->>anthropicProvider: "Add JSON schema instructions to system prompt"
    end
    
    anthropicProvider->>anthropicProvider: "Prepare tools with usage control"
    anthropicProvider->>anthropicProvider: "Build request payload"
    
    alt Streaming with no tools
        anthropicProvider->>Anthropic: "createStreamingMessage()"
        Anthropic-->>anthropicProvider: "Stream response"
        anthropicProvider-->>User: "StreamingExecution with readable stream"
    else Non-streaming or tools present
        anthropicProvider->>Anthropic: "createMessage()"
        Anthropic-->>anthropicProvider: "Response with content/tool calls"
        
        loop Tool execution (max iterations)
            alt Tool calls present
                anthropicProvider->>Tool: "executeTool(toolName, params)"
                Tool-->>anthropicProvider: "Tool result"
                anthropicProvider->>anthropicProvider: "Add tool result to messages"
                anthropicProvider->>Anthropic: "createMessage() with updated messages"
                Anthropic-->>anthropicProvider: "Next response"
            end
        end
        
        alt Final streaming requested
            anthropicProvider->>Anthropic: "createStreamingMessage() for final response"
            Anthropic-->>anthropicProvider: "Final stream"
            anthropicProvider-->>User: "StreamingExecution with all tool data"
        else Standard response
            anthropicProvider-->>User: "ProviderResponse with content and tool results"
        end
    end
Loading

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 23, 2025

Greptile's behavior is changing!

From now on, if a review finishes with no comments, we will not post an additional "statistics" comment to confirm that our review found nothing to comment on. However, you can confirm that we reviewed your changes in the status check section.

This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR".

@waleedlatif1
Copy link
Collaborator

not true, we use the new structured output from anthropic for the models that support it.

@aadamsx
Copy link
Author

aadamsx commented Dec 23, 2025

I've checked the current main branch. The Anthropic provider at apps/sim/providers/anthropic/index.ts does NOT use native structured outputs.

Current main branch (line ~157):

// If response format is specified, add strict formatting instructions
// Build a system prompt for structured output based on the JSON schema

This is the prompt-based approach - adding JSON instructions to the system prompt. Claude is "asked nicely" but not constrained.

Native structured outputs (November 2025) uses:

anthropic.beta.messages.create({
  betas: ['structured-outputs-2025-11-13'],
  output_format: { type: 'json_schema', schema: ... }
})

A search for beta, output_format, betas, or structured-outputs in main returns zero matches (except the comment about "system prompt").

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

@aadamsx
Copy link
Author

aadamsx commented Dec 23, 2025

@waleedlatif1 ^

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.

2 participants