Skip to content

Conversation

@TJaenichen
Copy link
Contributor

Summary

This PR adds support for structured JSON output when using the IChatClient interface from Microsoft.Extensions.AI/Microsoft Agent Framework. It enables the ChatOptions.ResponseFormat property to work correctly with Anthropic's native structured output API.

Changes

  • New OutputFormat class (Anthropic.SDK/Messaging/OutputFormat.cs)

    • Model class for Anthropic's output_format API parameter
    • Supports json_schema type with schema definition
  • Updated MessageParameters

    • Added OutputFormat property for structured JSON output configuration
  • Updated Function class

    • Added Strict property to enable strict mode for tool definitions
    • Updated CopyFrom method to copy the Strict property
  • Updated ChatClientHelper

    • Maps ChatResponseFormatJson to Anthropic's output_format parameter
    • Automatically processes JSON schemas to add additionalProperties: false on all object types (required by Anthropic's API)
    • Conditionally enables strict mode for tools when structured output is requested
  • Updated MessagesEndpoint

    • Automatically adds the structured-outputs-2025-11-13 beta header when structured output or strict tools are used
  • New extension methods (ChatOptionsExtensions.cs)

    • WithStrictTools() - Enable strict mode for tool definitions
    • GetStrictToolsEnabled() - Check if strict tools are enabled

Usage Example

using Microsoft.Extensions.AI;
using Anthropic.SDK;

var client = new AnthropicClient("your-api-key");
var chatClient = client.Messages.AsIChatClient(AnthropicModels.Claude35Sonnet);

var options = new ChatOptions
{
    ResponseFormat = ChatResponseFormat.ForJsonSchema(
        JsonSerializer.SerializeToElement(new
        {
            type = "object",
            properties = new
            {
                name = new { type = "string" },
                age = new { type = "integer" }
            },
            required = new[] { "name", "age" }
        })
    )
};

var response = await chatClient.GetResponseAsync("Extract: John is 30 years old", options);
// Response will be valid JSON matching the schema

Microsoft Agent Framework Compatibility

This enables full compatibility with ChatClientAgent from the Microsoft Agent Framework:

var agent = new ChatClientAgent(
    chatClient,
    "Assistant",
    new ChatClientAgentOptions
    {
        ChatOptions = new ChatOptions
        {
            ResponseFormat = ChatResponseFormat.ForJsonSchema(schema)
        }
    }
);

Technical Notes

  • Beta Header: The structured-outputs-2025-11-13 beta header is automatically added when structured output or strict tools are detected
  • Schema Processing: The implementation automatically adds additionalProperties: false to all object types in schemas, as required by Anthropic's structured output API
  • Backwards Compatibility: Schema processing for tools only occurs when strict mode is explicitly enabled (via ResponseFormat or WithStrictTools()), ensuring existing code continues to work unchanged

Tests

Added comprehensive unit tests in StructuredOutputTests.cs covering:

  • Extension methods functionality
  • ResponseFormat mapping to OutputFormat
  • Strict tools configuration
  • Schema serialization
  • Beta header handling

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

…ork)

Implements ChatOptions.ResponseFormat support for the IChatClient interface,
enabling seamless integration with Microsoft Agent Framework's structured
output capabilities.

Features:
- Map ChatResponseFormat.ForJsonSchema() to Anthropic's output_format parameter
- Automatic additionalProperties: false injection for JSON schemas (required by Anthropic API)
- Strict tool use support with Function.Strict property
- Auto-add structured-outputs-2025-11-13 beta header when needed
- New WithStrictTools() extension method for explicit strict mode control

Files changed:
- OutputFormat.cs: New model class for output_format API parameter
- MessageParameters.cs: Added OutputFormat property
- Function.cs: Added Strict property for strict tool use
- ChatClientHelper.cs: ResponseFormat mapping and schema processing
- MessagesEndpoint.cs: Beta header handling for structured outputs
- ChatOptionsExtensions.cs: WithStrictTools() extension method
- StructuredOutputTests.cs: 16 unit tests for new functionality

Backwards compatible: No changes to direct API usage or existing IChatClient
behavior without ResponseFormat.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@TJaenichen
Copy link
Contributor Author

@tghamm Please let me know if there are any changes required for this to be merged

@tghamm
Copy link
Owner

tghamm commented Jan 22, 2026

@TJaenichen awesome addition, thank you very much!

@tghamm tghamm merged commit a4dadd8 into tghamm:main Jan 22, 2026
1 check passed
@TJaenichen
Copy link
Contributor Author

Fantastic, thank you!

@fwaris
Copy link

fwaris commented Jan 25, 2026

@TJaenichen thanks for updating the SDK!

I am testing the update on my usecase.

It seems the following way of setting the response format does not work:

 opts.ResponseFormat <- ChatResponseFormat.ForJsonSchema(typeof<Completion>)

I get an error like the following:

    System.AggregateException: One or more errors occurred. (The node must be of type 'JsonValue'.)
       ---> System.InvalidOperationException: The node must be of type 'JsonValue'.
2026-01-25 14:46:05.538 CuaSample[92021:1825320]          at System.Text.Json.Nodes.JsonNode.GetValue[String]()
         at Anthropic.SDK.Messaging.ChatClientHelper.ProcessSchemaNode(JsonNode node)
         at Anthropic.SDK.Messaging.ChatClientHelper.ProcessSchemaNode(JsonNode node)
2026-01-25 14:46:05.538 CuaSample[92021:1825320]          at Anthropic.SDK.Messaging.ChatClientHelper.EnsureAdditionalPropertiesFalse(JsonElement schema)
         at Anthropic.SDK.Messaging.ChatClientHelper.CreateMessageParameters(IChatClient client, IEnumerable`1 messages, ChatOptions options)
2026-01-25 14:46:05.538 CuaSample[92021:1825320]          at Anthropic.SDK.Messaging.MessagesEndpoint.Microsoft.Extensions.AI.IChatClient.GetResponseAsync(IEnumerable`1 messages, ChatOptions options, CancellationToken cancellationToken)
2026-01-25 14:46:05.538 CuaSample[92021:1825320]          at Microsoft.Extensions.AI.ChatClientStructuredOutputExtensions.<GetResponseAsync>d__5`1[[FsAICore.Completion, FsAICore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()

Note the above approach works with MS.Ext.AI when targeting OpenAI.

It would be appreciated if we can code the same way across both backends for structured output.

Thanks

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.

3 participants