using AIStudio.Provider.Anthropic; using AIStudio.Provider.OpenAI; using AIStudio.Tools.ToolCallingSystem; namespace AIStudio.Provider; /// /// Converts a tool definition into the wire shape one provider API expects. /// /// /// The definitions state a tool once, in plain JSON Schema. What differs per API is not only the /// field names but how an optional argument is expressed, which is why the OpenAI shapes convert /// the schema while Anthropic takes it as written.

/// Strict mode is a promise of the host, not of the definition: only a host which binds the /// model's output to the schema keeps it. Everywhere else the model merely reads a schema that /// calls every argument required, and then either invents a value for an argument it meant to /// leave out, or the host rejects the call for lacking one. That is why the Chat Completions /// shape, which reaches every OpenAI-compatible host, only goes strict where the host says so. ///
public static class ProviderToolAdapters { /// /// Builds the nested function tool shape used by Chat Completions compatible APIs. /// /// The tool to describe. /// Whether the host binds the model's tool calls to a strict schema. Only then is the tool sent in strict mode. public static object ToChatCompletionTool(ToolDefinition definition, bool hostEnforcesStrict) { var isStrict = definition.Function.Strict && hostEnforcesStrict; return new { type = "function", function = new { name = definition.Function.Name, description = definition.Function.DescriptionForLLM, parameters = ToOpenAIParameters(definition, isStrict), strict = isStrict, } }; } /// /// Builds the flat function tool shape used by the OpenAI Responses API. /// /// /// Only OpenAI speaks this API, and it enforces strict mode, so the definition alone decides. /// public static ResponsesFunctionTool ToResponsesTool(ToolDefinition definition) => new() { Name = definition.Function.Name, Description = definition.Function.DescriptionForLLM, Parameters = ToOpenAIParameters(definition, definition.Function.Strict), Strict = definition.Function.Strict, }; /// /// Builds the tool shape used by the Anthropic messages API. /// /// /// Different field names — Anthropic calls the parameters an input schema and takes the /// description without nesting it under a function object — but the schema itself needs no /// conversion: Anthropic reads optionality the same way the definitions write it. /// public static AnthropicTool ToAnthropicTool(ToolDefinition definition) => new() { Name = definition.Function.Name, Description = definition.Function.DescriptionForLLM, InputSchema = definition.Function.Parameters, Strict = definition.Function.Strict, }; /// /// The parameter schema for the OpenAI APIs, converted only when strict mode asks for it. /// private static System.Text.Json.JsonElement ToOpenAIParameters(ToolDefinition definition, bool isStrict) => isStrict ? OpenAIStrictToolSchema.FromToolParameters(definition.Function.Parameters) : definition.Function.Parameters; }