mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 20:27:21 +00:00
Generated the JSON contracts of providers, the runtime, and services at compile time (#1150)
This commit is contained in:
1 parent
e954881fda
commit
24e3c22db3
140 files changed
+1415
-292
No files matched your search
@@ -117,8 +117,8 @@ public sealed class AnthropicContentBlockBuilder
|
||||
case TYPE_TEXT:
|
||||
return this.BuildFromSeed(new()
|
||||
{
|
||||
["type"] = JsonSerializer.Serialize(TYPE_TEXT),
|
||||
["text"] = JsonSerializer.Serialize(this.text.ToString()),
|
||||
["type"] = JsonSerializer.Serialize(TYPE_TEXT, ProviderJsonOptions.OPTIONS),
|
||||
["text"] = JsonSerializer.Serialize(this.text.ToString(), ProviderJsonOptions.OPTIONS),
|
||||
});
|
||||
|
||||
case TYPE_THINKING:
|
||||
@@ -129,9 +129,9 @@ public sealed class AnthropicContentBlockBuilder
|
||||
//
|
||||
return this.BuildFromSeed(new()
|
||||
{
|
||||
["type"] = JsonSerializer.Serialize(TYPE_THINKING),
|
||||
["thinking"] = JsonSerializer.Serialize(this.thinking.ToString()),
|
||||
["signature"] = JsonSerializer.Serialize(this.signature),
|
||||
["type"] = JsonSerializer.Serialize(TYPE_THINKING, ProviderJsonOptions.OPTIONS),
|
||||
["thinking"] = JsonSerializer.Serialize(this.thinking.ToString(), ProviderJsonOptions.OPTIONS),
|
||||
["signature"] = JsonSerializer.Serialize(this.signature, ProviderJsonOptions.OPTIONS),
|
||||
});
|
||||
|
||||
case TYPE_TOOL_USE:
|
||||
|
||||
@@ -10,4 +10,12 @@ namespace AIStudio.Provider.Anthropic;
|
||||
/// thinking blocks, and the thinking blocks have to return exactly as they arrived — reading and
|
||||
/// rebuilding them would risk changing them.
|
||||
/// </remarks>
|
||||
public sealed record AnthropicMessage(IList<JsonElement> Content, string Role = "assistant") : IMessage<IList<JsonElement>>;
|
||||
public sealed record AnthropicMessage(IList<JsonElement> Content, string Role = "assistant") : IMessage<IList<JsonElement>>
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable, as the messages demand it. Keep them in
|
||||
// the order of the parameters, which is the order they get written in:
|
||||
//
|
||||
public IList<JsonElement> Content { get; set; } = Content;
|
||||
public string Role { get; set; } = Role;
|
||||
}
|
||||
@@ -152,9 +152,9 @@ public sealed class AnthropicMessageStreamAccumulator
|
||||
this.unparsableToolArguments[builder.ToolUseId] = builder.UnparsableToolArguments;
|
||||
}
|
||||
|
||||
private static JsonElement EmptyTextBlock() => JsonSerializer.SerializeToElement(new
|
||||
private static JsonElement EmptyTextBlock()
|
||||
{
|
||||
type = "text",
|
||||
text = string.Empty,
|
||||
});
|
||||
using var document = JsonDocument.Parse("""{"type":"text","text":""}""");
|
||||
return document.RootElement.Clone();
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,5 @@ public sealed record AnthropicStreamMessage
|
||||
/// <summary>
|
||||
/// What the request carried, where the stream states it.
|
||||
/// </summary>
|
||||
public AnthropicUsage? Usage { get; init; }
|
||||
public AnthropicUsage? Usage { get; set; }
|
||||
}
|
||||
@@ -11,11 +11,11 @@ namespace AIStudio.Provider.Anthropic;
|
||||
/// </remarks>
|
||||
public sealed record AnthropicTool
|
||||
{
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public bool Strict { get; init; }
|
||||
public bool Strict { get; set; }
|
||||
|
||||
public JsonElement InputSchema { get; init; }
|
||||
public JsonElement InputSchema { get; set; }
|
||||
}
|
||||
@@ -4,11 +4,11 @@ namespace AIStudio.Provider.Anthropic;
|
||||
|
||||
public sealed record AnthropicToolResultContent
|
||||
{
|
||||
public string Type { get; init; } = "tool_result";
|
||||
public string Type { get; set; } = "tool_result";
|
||||
|
||||
public string ToolUseId { get; init; } = string.Empty;
|
||||
public string ToolUseId { get; set; } = string.Empty;
|
||||
|
||||
public string Content { get; init; } = string.Empty;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the tool failed rather than returning a result.
|
||||
@@ -18,5 +18,5 @@ public sealed record AnthropicToolResultContent
|
||||
/// result stays byte-identical to what earlier versions sent.
|
||||
/// </remarks>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public bool IsError { get; init; }
|
||||
public bool IsError { get; set; }
|
||||
}
|
||||
@@ -7,4 +7,12 @@ namespace AIStudio.Provider.Anthropic;
|
||||
/// All results of one turn belong in a single message. Splitting them across several messages
|
||||
/// teaches the model to stop asking for more than one tool at a time.
|
||||
/// </remarks>
|
||||
public sealed record AnthropicToolResultMessage(IList<AnthropicToolResultContent> Content, string Role = "user") : IMessage<IList<AnthropicToolResultContent>>;
|
||||
public sealed record AnthropicToolResultMessage(IList<AnthropicToolResultContent> Content, string Role = "user") : IMessage<IList<AnthropicToolResultContent>>
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable, as the messages demand it. Keep them in
|
||||
// the order of the parameters, which is the order they get written in:
|
||||
//
|
||||
public IList<AnthropicToolResultContent> Content { get; set; } = Content;
|
||||
public string Role { get; set; } = Role;
|
||||
}
|
||||
@@ -23,17 +23,17 @@ public sealed record AnthropicUsage
|
||||
/// <summary>
|
||||
/// What the request carried after its last cache breakpoint, which is all of it without caching.
|
||||
/// </summary>
|
||||
public int? InputTokens { get; init; }
|
||||
public int? InputTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What the request wrote to the cache.
|
||||
/// </summary>
|
||||
public int? CacheCreationInputTokens { get; init; }
|
||||
public int? CacheCreationInputTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What the request read from the cache.
|
||||
/// </summary>
|
||||
public int? CacheReadInputTokens { get; init; }
|
||||
public int? CacheReadInputTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// States what this block reports, as far as it can be believed.
|
||||
|
||||
@@ -10,7 +10,11 @@ namespace AIStudio.Provider.Anthropic;
|
||||
/// <param name="MaxTokens">The maximum number of tokens to generate.</param>
|
||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
||||
/// <param name="System">The system prompt for the chat completion.</param>
|
||||
public readonly record struct ChatRequest(
|
||||
/// <remarks>
|
||||
/// Not readonly, so that its properties can be settable: the source generation of System.Text.Json refuses to
|
||||
/// write extension data which is init-only.
|
||||
/// </remarks>
|
||||
public record struct ChatRequest(
|
||||
string Model,
|
||||
IList<IMessageBase> Messages,
|
||||
int MaxTokens,
|
||||
@@ -26,9 +30,12 @@ public readonly record struct ChatRequest(
|
||||
/// tools at all, and the final round of a tool conversation has to offer none.
|
||||
/// </remarks>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public IList<AnthropicTool>? Tools { get; init; }
|
||||
public IList<AnthropicTool>? Tools { get; set; }
|
||||
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
//
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData], and neither is init: the source
|
||||
// generation of System.Text.Json refuses to write the request at all then.
|
||||
//
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; set; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -4,7 +4,7 @@ public record SubContentBase64Image : ISubContentImageSource
|
||||
{
|
||||
public SubContentImageType Type => SubContentImageType.BASE64;
|
||||
|
||||
public string MediaType { get; init; } = string.Empty;
|
||||
public string MediaType { get; set; } = string.Empty;
|
||||
|
||||
public string Data { get; init; } = string.Empty;
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -4,6 +4,15 @@ namespace AIStudio.Provider.Anthropic;
|
||||
|
||||
public record SubContentImage(SubContentType Type, ISubContentImageSource Source) : ISubContent
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public SubContentType Type { get; set; } = Type;
|
||||
public ISubContentImageSource Source { get; set; } = Source;
|
||||
|
||||
public SubContentImage() : this(SubContentType.IMAGE, new SubContentImageUrl())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ public record SubContentImageUrl : ISubContentImageSource
|
||||
{
|
||||
public SubContentImageType Type => SubContentImageType.URL;
|
||||
|
||||
public string Url { get; init; } = string.Empty;
|
||||
public string Url { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1377,7 +1377,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if (runnableTools.Count > 0)
|
||||
{
|
||||
var adapter = new ChatCompletionToolCallingAdapter<TRequest>(requestFactory, systemPrompt, apiParameters,
|
||||
runnableTools.Select(x => ProviderToolAdapters.ToChatCompletionTool(x.Definition, enforcesStrictToolSchemas)).ToList(), mayAskForSequentialToolCalls, runnableTools,
|
||||
runnableTools.Select(x => (object)ProviderToolAdapters.ToChatCompletionTool(x.Definition, enforcesStrictToolSchemas)).ToList(), mayAskForSequentialToolCalls, runnableTools,
|
||||
(requestDto, requestToken) => this.StreamChatCompletionRequest(requestDto, providerName, requestPath, requestedSecret, requestHeadersAction, requestToken),
|
||||
ChatCompletionSourceReader.Read<TDelta, TAnnotation>,
|
||||
this.logger);
|
||||
@@ -1662,13 +1662,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
modelName = "placeholder";
|
||||
|
||||
// Prepare the HTTP embedding request:
|
||||
var payload = new
|
||||
{
|
||||
model = modelName,
|
||||
input = texts,
|
||||
encoding_format = "float"
|
||||
};
|
||||
|
||||
var payload = new EmbeddingRequest(modelName, texts);
|
||||
var embeddingRequest = JsonSerializer.Serialize(payload, JSON_SERIALIZER_OPTIONS);
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, host.EmbeddingURL());
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// A request for embeddings, in the shape of the OpenAI API, which most providers take.
|
||||
/// </summary>
|
||||
/// <param name="Model">The name of the embedding model.</param>
|
||||
/// <param name="Input">The texts to embed.</param>
|
||||
public sealed record EmbeddingRequest(string Model, IList<string> Input)
|
||||
{
|
||||
/// <summary>
|
||||
/// Asks for the vectors as numbers, instead of as base64.
|
||||
/// </summary>
|
||||
public string EncodingFormat => "float";
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
public sealed record EmbeddingResponse
|
||||
{
|
||||
public string? Id { get; init; }
|
||||
public string? Id { get; set; }
|
||||
|
||||
public string? Object { get; init; }
|
||||
public string? Object { get; set; }
|
||||
|
||||
public List<EmbeddingData>? Data { get; init; }
|
||||
public List<EmbeddingData>? Data { get; set; }
|
||||
|
||||
public string? Model { get; init; }
|
||||
public string? Model { get; set; }
|
||||
|
||||
public EmbeddingUsage? Usage { get; init; }
|
||||
public EmbeddingUsage? Usage { get; set; }
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
public sealed record GoogleEmbedding
|
||||
{
|
||||
public List<float>? Values { get; init; }
|
||||
public List<float>? Values { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Provider.Google;
|
||||
|
||||
/// <summary>
|
||||
/// The content of a request for an embedding to the Gemini API.
|
||||
/// </summary>
|
||||
/// <param name="Parts">The texts, each of them as a part of its own.</param>
|
||||
public sealed record GoogleEmbeddingContent(IList<GoogleEmbeddingPart> Parts);
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Provider.Google;
|
||||
|
||||
/// <summary>
|
||||
/// One text in a request for an embedding to the Gemini API.
|
||||
/// </summary>
|
||||
/// <param name="Text">The text to embed.</param>
|
||||
public sealed record GoogleEmbeddingPart(string Text);
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AIStudio.Provider.Google;
|
||||
|
||||
/// <summary>
|
||||
/// A request for an embedding to the Gemini API.
|
||||
/// </summary>
|
||||
/// <param name="Content">The texts to embed.</param>
|
||||
public sealed record GoogleEmbeddingRequest(GoogleEmbeddingContent Content)
|
||||
{
|
||||
/// <summary>
|
||||
/// Asks for vectors which tell how similar texts are.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Written as task_type, like every other field of our requests. The API reads both its field names in
|
||||
/// snake case and in camel case.
|
||||
/// </remarks>
|
||||
public string TaskType => "SEMANTIC_SIMILARITY";
|
||||
|
||||
/// <summary>
|
||||
/// Creates the request for the given texts, each of them as a part of its own.
|
||||
/// </summary>
|
||||
public static GoogleEmbeddingRequest Of(IEnumerable<string> texts) => new(new GoogleEmbeddingContent([..texts.Select(text => new GoogleEmbeddingPart(text))]));
|
||||
}
|
||||
@@ -6,9 +6,13 @@ namespace AIStudio.Provider.Google;
|
||||
public sealed record GoogleEmbeddingResponse
|
||||
{
|
||||
[JsonConverter(typeof(GoogleEmbeddingListConverter))]
|
||||
public List<GoogleEmbedding>? Embedding { get; init; }
|
||||
public List<GoogleEmbedding>? Embedding { get; set; }
|
||||
|
||||
private sealed class GoogleEmbeddingListConverter : JsonConverter<List<GoogleEmbedding>>
|
||||
/// <remarks>
|
||||
/// Internal rather than private: the JSON context of the providers has to reach the converter, else it reads
|
||||
/// the embeddings without it.
|
||||
/// </remarks>
|
||||
internal sealed class GoogleEmbeddingListConverter : JsonConverter<List<GoogleEmbedding>>
|
||||
{
|
||||
public override List<GoogleEmbedding> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
|
||||
@@ -92,16 +92,7 @@ public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, new Uri("https
|
||||
}
|
||||
|
||||
// Prepare the Google Gemini embedding request:
|
||||
var payload = new
|
||||
{
|
||||
content = new
|
||||
{
|
||||
parts = texts.Select(text => new { text }).ToArray()
|
||||
},
|
||||
|
||||
taskType = "SEMANTIC_SIMILARITY"
|
||||
};
|
||||
|
||||
var payload = GoogleEmbeddingRequest.Of(texts);
|
||||
var embeddingRequest = JsonSerializer.Serialize(payload, JSON_SERIALIZER_OPTIONS);
|
||||
var embedUrl = $"https://generativelanguage.googleapis.com/v1beta/models/{modelName}:embedContent";
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, embedUrl);
|
||||
|
||||
@@ -11,5 +11,5 @@ public interface IMessage<T> : IMessageBase
|
||||
/// The content encapsulates the core information or data being transmitted,
|
||||
/// and its type can vary based on the specific implementation or use case.
|
||||
/// </summary>
|
||||
public T Content { get; init; }
|
||||
public T Content { get; set; }
|
||||
}
|
||||
@@ -10,5 +10,5 @@ public interface IMessageBase
|
||||
/// This property typically identifies whether the entity is acting
|
||||
/// as a user, assistant, or system in the context of the interaction.
|
||||
/// </summary>
|
||||
public string Role { get; init; }
|
||||
public string Role { get; set; }
|
||||
}
|
||||
@@ -5,12 +5,12 @@ namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
public sealed record AssistantToolCallMessage : IMessageBase
|
||||
{
|
||||
public string Role { get; init; } = "assistant";
|
||||
public string Role { get; set; } = "assistant";
|
||||
|
||||
public JsonElement? Content { get; init; }
|
||||
public JsonElement? Content { get; set; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? ReasoningContent { get; init; }
|
||||
public string? ReasoningContent { get; set; }
|
||||
|
||||
public IList<ChatCompletionToolCall> ToolCalls { get; init; } = [];
|
||||
public IList<ChatCompletionToolCall> ToolCalls { get; set; } = [];
|
||||
}
|
||||
@@ -14,15 +14,25 @@ public record ChatCompletionAPIRequest(
|
||||
bool Stream
|
||||
)
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public string Model { get; set; } = Model;
|
||||
public IList<IMessageBase> Messages { get; set; } = Messages;
|
||||
public bool Stream { get; set; } = Stream;
|
||||
|
||||
public ChatCompletionAPIRequest() : this(string.Empty, [], true)
|
||||
{
|
||||
}
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public IList<object>? Tools { get; init; }
|
||||
public IList<object>? Tools { get; set; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public bool? ParallelToolCalls { get; init; }
|
||||
public bool? ParallelToolCalls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Asks a streamed request to end with what it cost.
|
||||
@@ -35,7 +45,10 @@ public record ChatCompletionAPIRequest(
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public ChatCompletionStreamOptions? StreamOptions => this.Stream ? ChatCompletionStreamOptions.INCLUDE_USAGE : null;
|
||||
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
//
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData], and neither is init: the source
|
||||
// generation of System.Text.Json refuses to write the request at all then.
|
||||
//
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; set; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -7,6 +7,14 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// <param name="Delta">The delta text of the choice.</param>
|
||||
public record ChatCompletionChoice(int Index, ChatCompletionDelta Delta)
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters:
|
||||
//
|
||||
public int Index { get; set; } = Index;
|
||||
public ChatCompletionDelta Delta { get; set; } = Delta;
|
||||
|
||||
public ChatCompletionChoice() : this(0, new())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace AIStudio.Provider.OpenAI;
|
||||
public sealed record ChatCompletionDelta
|
||||
{
|
||||
[JsonPropertyName("content")]
|
||||
public JsonElement? RawContent { get; init; }
|
||||
public JsonElement? RawContent { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string Content => ChatCompletionContent.GetText(this.RawContent) ?? string.Empty;
|
||||
|
||||
@@ -11,6 +11,19 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// <param name="Choices">The choices made by the AI.</param>
|
||||
public record ChatCompletionDeltaStreamLine(string Id, string Object, uint Created, string Model, string SystemFingerprint, IList<ChatCompletionChoice> Choices) : IResponseStreamLine
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost, e.g., the empty list of choices of the line which carries the
|
||||
// usage. Keep them first and in the order of the parameters:
|
||||
//
|
||||
public string Id { get; set; } = Id;
|
||||
public string Object { get; set; } = Object;
|
||||
public uint Created { get; set; } = Created;
|
||||
public string Model { get; set; } = Model;
|
||||
public string SystemFingerprint { get; set; } = SystemFingerprint;
|
||||
public IList<ChatCompletionChoice> Choices { get; set; } = Choices;
|
||||
|
||||
public ChatCompletionDeltaStreamLine() : this(string.Empty, string.Empty, 0, string.Empty, string.Empty, [])
|
||||
{
|
||||
}
|
||||
@@ -25,7 +38,7 @@ public record ChatCompletionDeltaStreamLine(string Id, string Object, uint Creat
|
||||
/// line of its own which carries no choices -- which is why the usage is read apart from the
|
||||
/// content rather than next to it.
|
||||
/// </remarks>
|
||||
public ChatCompletionUsage? Usage { get; init; }
|
||||
public ChatCompletionUsage? Usage { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsContent() => this.Choices.Count > 0;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// The function a tool of a Chat Completions compatible API offers to the model.
|
||||
/// </summary>
|
||||
public sealed record ChatCompletionFunctionDeclaration
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public JsonElement Parameters { get; set; }
|
||||
|
||||
public bool Strict { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// The function tool definition shape expected by Chat Completions compatible APIs, with the function nested.
|
||||
/// </summary>
|
||||
public sealed record ChatCompletionFunctionTool
|
||||
{
|
||||
public string Type { get; set; } = "function";
|
||||
|
||||
public ChatCompletionFunctionDeclaration Function { get; set; } = new();
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public sealed record ChatCompletionStreamDelta
|
||||
/// The content as it arrived: a string for most providers, a list of parts for some.
|
||||
/// </summary>
|
||||
[JsonPropertyName("content")]
|
||||
public JsonElement? RawContent { get; init; }
|
||||
public JsonElement? RawContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text of this fragment, whichever shape it arrived in.
|
||||
@@ -27,10 +27,10 @@ public sealed record ChatCompletionStreamDelta
|
||||
/// <summary>
|
||||
/// The reasoning text some providers stream next to the answer.
|
||||
/// </summary>
|
||||
public string? ReasoningContent { get; init; }
|
||||
public string? ReasoningContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The fragments of the tool calls the model is asking for.
|
||||
/// </summary>
|
||||
public IList<ChatCompletionToolCallDelta?>? ToolCalls { get; init; }
|
||||
public IList<ChatCompletionToolCallDelta?>? ToolCalls { get; set; }
|
||||
}
|
||||
@@ -5,12 +5,12 @@ namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
public sealed record ChatCompletionToolCall
|
||||
{
|
||||
public string? Id { get; init; }
|
||||
public string? Id { get; set; }
|
||||
|
||||
public string? Type { get; init; } = "function";
|
||||
public string? Type { get; set; } = "function";
|
||||
|
||||
public ChatCompletionToolFunction? Function { get; init; }
|
||||
public ChatCompletionToolFunction? Function { get; set; }
|
||||
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JsonElement> AdditionalMetadata { get; init; } = new Dictionary<string, JsonElement>();
|
||||
public IDictionary<string, JsonElement> AdditionalMetadata { get; set; } = new Dictionary<string, JsonElement>();
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public sealed class ChatCompletionToolCallAccumulator(Func<ServerSentEvent, ILis
|
||||
// No text means no content field, the way a round which only calls a tool arrives
|
||||
// when it is not streamed. Some providers reject an empty string in its place.
|
||||
//
|
||||
RawContent = answer.Length is 0 ? null : JsonSerializer.SerializeToElement(answer),
|
||||
RawContent = answer.Length is 0 ? null : JsonSerializer.SerializeToElement(answer, ProviderJsonOptions.OPTIONS),
|
||||
ReasoningContent = this.reasoning.Length is 0 ? null : this.reasoning.ToString(),
|
||||
ToolCalls = this.toolCalls.Count is 0
|
||||
? null
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
public sealed record ChatCompletionToolFunction
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Arguments { get; init; }
|
||||
public string? Arguments { get; set; }
|
||||
}
|
||||
@@ -21,5 +21,5 @@ public sealed record ChatCompletionToolStreamLine(string? Id, IList<ChatCompleti
|
||||
/// ChatCompletionAPIRequest asks for it, the requests of the tool rounds included. Not a
|
||||
/// positional parameter, because nobody but the serializer ever builds this line.
|
||||
/// </remarks>
|
||||
public ChatCompletionUsage? Usage { get; init; }
|
||||
public ChatCompletionUsage? Usage { get; set; }
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public sealed record ChatCompletionUsage
|
||||
/// <summary>
|
||||
/// What everything sent to the model cost.
|
||||
/// </summary>
|
||||
public int? PromptTokens { get; init; }
|
||||
public int? PromptTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// States what this block reports, as far as it can be believed.
|
||||
|
||||
@@ -8,5 +8,5 @@ public interface ISubContent
|
||||
/// <summary>
|
||||
/// The type of the sub-content.
|
||||
/// </summary>
|
||||
public SubContentType Type { get; init; }
|
||||
public SubContentType Type { get; set; }
|
||||
}
|
||||
@@ -15,5 +15,5 @@ public interface ISubContentImageUrl
|
||||
/// <summary>
|
||||
/// The URL or base64-encoded data URI of the image.
|
||||
/// </summary>
|
||||
public string Url { get; init; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
@@ -7,6 +7,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// <param name="Role">The role of the message.</param>
|
||||
public record MultimodalMessage(List<ISubContent> Content, string Role) : IMessage<List<ISubContent>>
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public List<ISubContent> Content { get; set; } = Content;
|
||||
public string Role { get; set; } = Role;
|
||||
|
||||
public MultimodalMessage() : this([], string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public static class OpenAIStrictToolSchema
|
||||
// schema stays stable across requests, which prompt caching depends on.
|
||||
//
|
||||
schema["required"] = new JsonArray([..properties.Select(property => JsonValue.Create(property.Key))]);
|
||||
return JsonSerializer.Deserialize<JsonElement>(schema.ToJsonString());
|
||||
return JsonSerializer.Deserialize<JsonElement>(schema.ToJsonString(), ProviderJsonOptions.OPTIONS);
|
||||
}
|
||||
|
||||
private static void AllowNullType(JsonObject property)
|
||||
|
||||
@@ -17,11 +17,26 @@ public record ResponsesAPIRequest(
|
||||
bool Store,
|
||||
IList<object> Tools)
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public string Model { get; set; } = Model;
|
||||
public IList<object> Input { get; set; } = Input;
|
||||
public bool Stream { get; set; } = Stream;
|
||||
public bool Store { get; set; } = Store;
|
||||
public IList<object> Tools { get; set; } = Tools;
|
||||
|
||||
public ResponsesAPIRequest() : this(string.Empty, [], true, false, [])
|
||||
{
|
||||
}
|
||||
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
|
||||
//
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData], and neither is init: the source
|
||||
// generation of System.Text.Json refuses to write the request at all then.
|
||||
//
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; set; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -5,9 +5,9 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </summary>
|
||||
public sealed record ResponsesFunctionCallOutputItem
|
||||
{
|
||||
public string Type { get; init; } = "function_call_output";
|
||||
public string Type { get; set; } = "function_call_output";
|
||||
|
||||
public string CallId { get; init; } = string.Empty;
|
||||
public string CallId { get; set; } = string.Empty;
|
||||
|
||||
public string Output { get; init; } = string.Empty;
|
||||
public string Output { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -7,13 +7,13 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </summary>
|
||||
public sealed record ResponsesFunctionTool
|
||||
{
|
||||
public string Type { get; init; } = "function";
|
||||
public string Type { get; set; } = "function";
|
||||
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public JsonElement Parameters { get; init; }
|
||||
public JsonElement Parameters { get; set; }
|
||||
|
||||
public bool Strict { get; init; }
|
||||
public bool Strict { get; set; }
|
||||
}
|
||||
@@ -17,18 +17,18 @@ public sealed record ResponsesResponse
|
||||
/// </remarks>
|
||||
private static readonly HashSet<string> OUTPUT_ITEMS_LEAVING_THE_INPUT_ALONE = ["message", "reasoning", "function_call"];
|
||||
|
||||
public string Id { get; init; } = string.Empty;
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
public string Model { get; init; } = string.Empty;
|
||||
public string Model { get; set; } = string.Empty;
|
||||
|
||||
public string? OutputText { get; init; }
|
||||
public string? OutputText { get; set; }
|
||||
|
||||
public IList<JsonElement> Output { get; init; } = [];
|
||||
public IList<JsonElement> Output { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// What OpenAI says the response cost. Only the completed response carries it.
|
||||
/// </summary>
|
||||
public ResponsesUsage? Usage { get; init; }
|
||||
public ResponsesUsage? Usage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// States what the request of this response carried, as far as it can be believed.
|
||||
|
||||
@@ -17,7 +17,7 @@ public sealed record ResponsesUsage
|
||||
/// <summary>
|
||||
/// What everything sent to the model cost, the cached part included.
|
||||
/// </summary>
|
||||
public int? InputTokens { get; init; }
|
||||
public int? InputTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// States what this block reports, as far as it can be believed.
|
||||
|
||||
@@ -5,6 +5,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </summary>
|
||||
public record SubContentImageUrl(SubContentType Type, string ImageUrl) : ISubContent
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public SubContentType Type { get; set; } = Type;
|
||||
public string ImageUrl { get; set; } = ImageUrl;
|
||||
|
||||
public SubContentImageUrl() : this(SubContentType.IMAGE_URL, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ namespace AIStudio.Provider.OpenAI;
|
||||
public record SubContentImageUrlData : ISubContentImageUrl
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Url { get; init; } = string.Empty;
|
||||
public string Url { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -12,6 +12,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </remarks>
|
||||
public record SubContentImageUrlNested(SubContentType Type, ISubContentImageUrl ImageUrl) : ISubContent
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public SubContentType Type { get; set; } = Type;
|
||||
public ISubContentImageUrl ImageUrl { get; set; } = ImageUrl;
|
||||
|
||||
public SubContentImageUrlNested() : this(SubContentType.IMAGE_URL, new SubContentImageUrlData())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -8,6 +8,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </remarks>
|
||||
public record SubContentInputImage(SubContentType Type, string ImageUrl) : ISubContent
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public SubContentType Type { get; set; } = Type;
|
||||
public string ImageUrl { get; set; } = ImageUrl;
|
||||
|
||||
public SubContentInputImage() : this(SubContentType.INPUT_IMAGE, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -8,6 +8,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </remarks>
|
||||
public record SubContentInputText(SubContentType Type, string Text) : ISubContent
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public SubContentType Type { get; set; } = Type;
|
||||
public string Text { get; set; } = Text;
|
||||
|
||||
public SubContentInputText() : this(SubContentType.INPUT_TEXT, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -5,6 +5,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// </summary>
|
||||
public record SubContentText(SubContentType Type, string Text) : ISubContent
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public SubContentType Type { get; set; } = Type;
|
||||
public string Text { get; set; } = Text;
|
||||
|
||||
public SubContentText() : this(SubContentType.TEXT, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// <param name="Role">The role of the message.</param>
|
||||
public record TextMessage(string Content, string Role) : IMessage<string>
|
||||
{
|
||||
//
|
||||
// The properties of the parameters, declared again to be settable. The source generation of System.Text.Json
|
||||
// sets every init-only property while it creates the object, also one missing in the JSON, so its value from
|
||||
// the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is
|
||||
// the order they get written in:
|
||||
//
|
||||
public string Content { get; set; } = Content;
|
||||
public string Role { get; set; } = Role;
|
||||
|
||||
public TextMessage() : this(string.Empty, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
public sealed record ToolResultMessage : IMessage<string>
|
||||
{
|
||||
public string Role { get; init; } = "tool";
|
||||
public string Role { get; set; } = "tool";
|
||||
|
||||
public string Content { get; init; } = string.Empty;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
public string ToolCallId { get; init; } = string.Empty;
|
||||
public string ToolCallId { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AIStudio.Provider.Anthropic;
|
||||
using AIStudio.Provider.Google;
|
||||
using AIStudio.Provider.Groq;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Provider.OpenRouter;
|
||||
using AIStudio.Provider.Requesty;
|
||||
|
||||
using AnthropicDelta = AIStudio.Provider.Anthropic.Delta;
|
||||
using AnthropicImageUrl = AIStudio.Provider.Anthropic.SubContentImageUrl;
|
||||
using AnthropicResponseStreamLine = AIStudio.Provider.Anthropic.ResponseStreamLine;
|
||||
using FireworksChoice = AIStudio.Provider.Fireworks.Choice;
|
||||
using FireworksDelta = AIStudio.Provider.Fireworks.Delta;
|
||||
using FireworksResponseStreamLine = AIStudio.Provider.Fireworks.ResponseStreamLine;
|
||||
using HuggingFaceModelsResponse = AIStudio.Provider.HuggingFace.ModelsResponse;
|
||||
using MistralModel = AIStudio.Provider.Mistral.Model;
|
||||
using MistralModelsResponse = AIStudio.Provider.Mistral.ModelsResponse;
|
||||
using OpenAIImageUrl = AIStudio.Provider.OpenAI.SubContentImageUrl;
|
||||
using OpenAIModelsResponse = AIStudio.Provider.OpenAI.ModelsResponse;
|
||||
using PerplexityChoice = AIStudio.Provider.Perplexity.Choice;
|
||||
using PerplexityDelta = AIStudio.Provider.Perplexity.Delta;
|
||||
using PerplexityResponseStreamLine = AIStudio.Provider.Perplexity.ResponseStreamLine;
|
||||
using SelfHostedModel = AIStudio.Provider.SelfHosted.Model;
|
||||
using SelfHostedModelsResponse = AIStudio.Provider.SelfHosted.ModelsResponse;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// The contracts with which System.Text.Json reads and writes the requests and answers of LLM providers, built
|
||||
/// at compile time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some types System.Text.Json meets only while it runs, since no property names them: the messages and their
|
||||
/// parts, which converters write as the type they are of; the annotations, which a converter reads as the type
|
||||
/// their JSON tells; and whatever a request holds as an object, i.e., the values of the additional API
|
||||
/// parameters, the tools, and the items of an earlier answer. A test fails for such a type which is missing
|
||||
/// here.<br/><br/>
|
||||
///
|
||||
/// Two types with the same name need a name of their own for their contract, else the generator builds only
|
||||
/// the first of them and warns with SYSLIB1031.
|
||||
/// </remarks>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
|
||||
// The requests:
|
||||
[JsonSerializable(typeof(ChatCompletionAPIRequest))]
|
||||
[JsonSerializable(typeof(ResponsesAPIRequest))]
|
||||
[JsonSerializable(typeof(ChatRequest))]
|
||||
[JsonSerializable(typeof(EmbeddingRequest))]
|
||||
[JsonSerializable(typeof(GoogleEmbeddingRequest))]
|
||||
|
||||
// The messages and their parts:
|
||||
[JsonSerializable(typeof(TextMessage))]
|
||||
[JsonSerializable(typeof(MultimodalMessage))]
|
||||
[JsonSerializable(typeof(AssistantToolCallMessage))]
|
||||
[JsonSerializable(typeof(ToolResultMessage))]
|
||||
[JsonSerializable(typeof(AnthropicMessage))]
|
||||
[JsonSerializable(typeof(AnthropicToolResultMessage))]
|
||||
[JsonSerializable(typeof(SubContentText))]
|
||||
[JsonSerializable(typeof(OpenAIImageUrl), TypeInfoPropertyName = "OpenAISubContentImageUrl")]
|
||||
[JsonSerializable(typeof(SubContentImageUrlNested))]
|
||||
[JsonSerializable(typeof(SubContentImageUrlData))]
|
||||
[JsonSerializable(typeof(SubContentInputText))]
|
||||
[JsonSerializable(typeof(SubContentInputImage))]
|
||||
[JsonSerializable(typeof(SubContentImage))]
|
||||
[JsonSerializable(typeof(SubContentBase64Image))]
|
||||
[JsonSerializable(typeof(AnthropicImageUrl), TypeInfoPropertyName = "AnthropicSubContentImageUrl")]
|
||||
|
||||
// What a request holds as an object:
|
||||
[JsonSerializable(typeof(string))]
|
||||
[JsonSerializable(typeof(int))]
|
||||
[JsonSerializable(typeof(long))]
|
||||
[JsonSerializable(typeof(double))]
|
||||
[JsonSerializable(typeof(decimal))]
|
||||
[JsonSerializable(typeof(bool))]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>))]
|
||||
[JsonSerializable(typeof(List<object>))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
[JsonSerializable(typeof(ChatCompletionFunctionTool))]
|
||||
[JsonSerializable(typeof(ResponsesFunctionTool))]
|
||||
[JsonSerializable(typeof(ResponsesFunctionCallOutputItem))]
|
||||
|
||||
// The lists of models:
|
||||
[JsonSerializable(typeof(OpenAIModelsResponse), TypeInfoPropertyName = "OpenAIModelsResponse")]
|
||||
[JsonSerializable(typeof(MistralModelsResponse), TypeInfoPropertyName = "MistralModelsResponse")]
|
||||
[JsonSerializable(typeof(SelfHostedModelsResponse), TypeInfoPropertyName = "SelfHostedModelsResponse")]
|
||||
[JsonSerializable(typeof(HuggingFaceModelsResponse), TypeInfoPropertyName = "HuggingFaceModelsResponse")]
|
||||
[JsonSerializable(typeof(IList<HubModel>))]
|
||||
[JsonSerializable(typeof(RequestyModelsResponse))]
|
||||
[JsonSerializable(typeof(OpenRouterModelsResponse))]
|
||||
[JsonSerializable(typeof(GroqModelsResponse))]
|
||||
|
||||
// The lines of streamed answers, with the annotations:
|
||||
[JsonSerializable(typeof(ChatCompletionDeltaStreamLine))]
|
||||
[JsonSerializable(typeof(ChatCompletionAnnotationStreamLine))]
|
||||
[JsonSerializable(typeof(ChatCompletionToolStreamLine))]
|
||||
[JsonSerializable(typeof(ResponsesDeltaStreamLine))]
|
||||
[JsonSerializable(typeof(ResponsesAnnotationStreamLine))]
|
||||
[JsonSerializable(typeof(ResponsesCompletedStreamLine))]
|
||||
[JsonSerializable(typeof(AnthropicResponseStreamLine), TypeInfoPropertyName = "AnthropicResponseStreamLine")]
|
||||
[JsonSerializable(typeof(AnthropicStreamLine))]
|
||||
[JsonSerializable(typeof(FireworksResponseStreamLine), TypeInfoPropertyName = "FireworksResponseStreamLine")]
|
||||
[JsonSerializable(typeof(PerplexityResponseStreamLine), TypeInfoPropertyName = "PerplexityResponseStreamLine")]
|
||||
[JsonSerializable(typeof(AnnotatingUnknown))]
|
||||
[JsonSerializable(typeof(ChatCompletionAnnotatingURL))]
|
||||
[JsonSerializable(typeof(ResponsesAnnotatingUrlCitationData))]
|
||||
|
||||
// Transcriptions and embeddings:
|
||||
[JsonSerializable(typeof(TranscriptionResponse))]
|
||||
[JsonSerializable(typeof(EmbeddingResponse))]
|
||||
[JsonSerializable(typeof(GoogleEmbeddingResponse))]
|
||||
[JsonSerializable(typeof(GoogleEmbedding))]
|
||||
[JsonSerializable(typeof(List<GoogleEmbedding>))]
|
||||
|
||||
// The types inside the answers which share their names with others:
|
||||
[JsonSerializable(typeof(MistralModel), TypeInfoPropertyName = "MistralModel")]
|
||||
[JsonSerializable(typeof(MistralModel[]), TypeInfoPropertyName = "MistralModelArray")]
|
||||
[JsonSerializable(typeof(SelfHostedModel), TypeInfoPropertyName = "SelfHostedModel")]
|
||||
[JsonSerializable(typeof(SelfHostedModel[]), TypeInfoPropertyName = "SelfHostedModelArray")]
|
||||
[JsonSerializable(typeof(FireworksChoice), TypeInfoPropertyName = "FireworksChoice")]
|
||||
[JsonSerializable(typeof(IList<FireworksChoice>), TypeInfoPropertyName = "IListFireworksChoice")]
|
||||
[JsonSerializable(typeof(PerplexityChoice), TypeInfoPropertyName = "PerplexityChoice")]
|
||||
[JsonSerializable(typeof(IList<PerplexityChoice>), TypeInfoPropertyName = "IListPerplexityChoice")]
|
||||
[JsonSerializable(typeof(AnthropicDelta), TypeInfoPropertyName = "AnthropicDelta")]
|
||||
[JsonSerializable(typeof(FireworksDelta), TypeInfoPropertyName = "FireworksDelta")]
|
||||
[JsonSerializable(typeof(PerplexityDelta), TypeInfoPropertyName = "PerplexityDelta")]
|
||||
internal sealed partial class ProviderJsonContext : JsonSerializerContext;
|
||||
@@ -21,7 +21,7 @@ public static class ProviderJsonOptions
|
||||
/// <summary>
|
||||
/// The shared options.
|
||||
/// </summary>
|
||||
public static readonly JsonSerializerOptions OPTIONS = new()
|
||||
public static readonly JsonSerializerOptions OPTIONS = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
Converters =
|
||||
@@ -33,6 +33,7 @@ public static class ProviderJsonOptions
|
||||
new SubContentImageSourceConverter(),
|
||||
new SubContentImageUrlConverter(),
|
||||
},
|
||||
AllowTrailingCommas = false
|
||||
};
|
||||
AllowTrailingCommas = false,
|
||||
TypeInfoResolver = ProviderJsonContext.Default,
|
||||
}.AsReadOnly();
|
||||
}
|
||||
@@ -24,19 +24,18 @@ public static class ProviderToolAdapters
|
||||
/// </summary>
|
||||
/// <param name="definition">The tool to describe.</param>
|
||||
/// <param name="hostEnforcesStrict">Whether the host binds the model's tool calls to a strict schema. Only then is the tool sent in strict mode.</param>
|
||||
public static object ToChatCompletionTool(ToolDefinition definition, bool hostEnforcesStrict)
|
||||
public static ChatCompletionFunctionTool ToChatCompletionTool(ToolDefinition definition, bool hostEnforcesStrict)
|
||||
{
|
||||
var isStrict = definition.Function.Strict && hostEnforcesStrict;
|
||||
return new
|
||||
return new()
|
||||
{
|
||||
type = "function",
|
||||
function = new
|
||||
Function = new()
|
||||
{
|
||||
name = definition.Function.Name,
|
||||
description = definition.Function.DescriptionForLLM,
|
||||
parameters = ToOpenAIParameters(definition, isStrict),
|
||||
strict = isStrict,
|
||||
}
|
||||
Name = definition.Function.Name,
|
||||
Description = definition.Function.DescriptionForLLM,
|
||||
Parameters = ToOpenAIParameters(definition, isStrict),
|
||||
Strict = isStrict,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user