mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 12:27:21 +00:00
Replaced the anonymous request shapes of the providers with records
This commit is contained in:
15 files changed
+111
-32
No files matched your search
@@ -152,9 +152,9 @@ public sealed class AnthropicMessageStreamAccumulator
|
|||||||
this.unparsableToolArguments[builder.ToolUseId] = builder.UnparsableToolArguments;
|
this.unparsableToolArguments[builder.ToolUseId] = builder.UnparsableToolArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonElement EmptyTextBlock() => JsonSerializer.SerializeToElement(new
|
private static JsonElement EmptyTextBlock()
|
||||||
{
|
{
|
||||||
type = "text",
|
using var document = JsonDocument.Parse("""{"type":"text","text":""}""");
|
||||||
text = string.Empty,
|
return document.RootElement.Clone();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
@@ -1377,7 +1377,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
if (runnableTools.Count > 0)
|
if (runnableTools.Count > 0)
|
||||||
{
|
{
|
||||||
var adapter = new ChatCompletionToolCallingAdapter<TRequest>(requestFactory, systemPrompt, apiParameters,
|
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),
|
(requestDto, requestToken) => this.StreamChatCompletionRequest(requestDto, providerName, requestPath, requestedSecret, requestHeadersAction, requestToken),
|
||||||
ChatCompletionSourceReader.Read<TDelta, TAnnotation>,
|
ChatCompletionSourceReader.Read<TDelta, TAnnotation>,
|
||||||
this.logger);
|
this.logger);
|
||||||
@@ -1662,13 +1662,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
modelName = "placeholder";
|
modelName = "placeholder";
|
||||||
|
|
||||||
// Prepare the HTTP embedding request:
|
// Prepare the HTTP embedding request:
|
||||||
var payload = new
|
var payload = new EmbeddingRequest(modelName, texts);
|
||||||
{
|
|
||||||
model = modelName,
|
|
||||||
input = texts,
|
|
||||||
encoding_format = "float"
|
|
||||||
};
|
|
||||||
|
|
||||||
var embeddingRequest = JsonSerializer.Serialize(payload, JSON_SERIALIZER_OPTIONS);
|
var embeddingRequest = JsonSerializer.Serialize(payload, JSON_SERIALIZER_OPTIONS);
|
||||||
using var request = new HttpRequestMessage(HttpMethod.Post, host.EmbeddingURL());
|
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";
|
||||||
|
}
|
||||||
@@ -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))]));
|
||||||
|
}
|
||||||
@@ -92,16 +92,7 @@ public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, new Uri("https
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the Google Gemini embedding request:
|
// Prepare the Google Gemini embedding request:
|
||||||
var payload = new
|
var payload = GoogleEmbeddingRequest.Of(texts);
|
||||||
{
|
|
||||||
content = new
|
|
||||||
{
|
|
||||||
parts = texts.Select(text => new { text }).ToArray()
|
|
||||||
},
|
|
||||||
|
|
||||||
taskType = "SEMANTIC_SIMILARITY"
|
|
||||||
};
|
|
||||||
|
|
||||||
var embeddingRequest = JsonSerializer.Serialize(payload, JSON_SERIALIZER_OPTIONS);
|
var embeddingRequest = JsonSerializer.Serialize(payload, JSON_SERIALIZER_OPTIONS);
|
||||||
var embedUrl = $"https://generativelanguage.googleapis.com/v1beta/models/{modelName}:embedContent";
|
var embedUrl = $"https://generativelanguage.googleapis.com/v1beta/models/{modelName}:embedContent";
|
||||||
using var request = new HttpRequestMessage(HttpMethod.Post, embedUrl);
|
using var request = new HttpRequestMessage(HttpMethod.Post, embedUrl);
|
||||||
|
|||||||
@@ -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; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Description { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public JsonElement Parameters { get; init; }
|
||||||
|
|
||||||
|
public bool Strict { get; init; }
|
||||||
|
}
|
||||||
@@ -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; init; } = "function";
|
||||||
|
|
||||||
|
public ChatCompletionFunctionDeclaration Function { get; init; } = new();
|
||||||
|
}
|
||||||
@@ -24,19 +24,18 @@ public static class ProviderToolAdapters
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="definition">The tool to describe.</param>
|
/// <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>
|
/// <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;
|
var isStrict = definition.Function.Strict && hostEnforcesStrict;
|
||||||
return new
|
return new()
|
||||||
{
|
{
|
||||||
type = "function",
|
Function = new()
|
||||||
function = new
|
|
||||||
{
|
{
|
||||||
name = definition.Function.Name,
|
Name = definition.Function.Name,
|
||||||
description = definition.Function.DescriptionForLLM,
|
Description = definition.Function.DescriptionForLLM,
|
||||||
parameters = ToOpenAIParameters(definition, isStrict),
|
Parameters = ToOpenAIParameters(definition, isStrict),
|
||||||
strict = isStrict,
|
Strict = isStrict,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"model":"embedding-model","input":["The first text.","The second text."],"encoding_format":"float"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"content":{"parts":[{"text":"The first text."},{"text":"The second text."}]},"task_type":"SEMANTIC_SIMILARITY"}
|
||||||
@@ -22,6 +22,8 @@ public sealed class JsonGoldenRequestTests
|
|||||||
yield return new("chat-completion-request.json", JsonSampleRequests.ChatCompletion, ProviderJsonOptions.OPTIONS);
|
yield return new("chat-completion-request.json", JsonSampleRequests.ChatCompletion, ProviderJsonOptions.OPTIONS);
|
||||||
yield return new("responses-request.json", JsonSampleRequests.Responses, ProviderJsonOptions.OPTIONS);
|
yield return new("responses-request.json", JsonSampleRequests.Responses, ProviderJsonOptions.OPTIONS);
|
||||||
yield return new("anthropic-messages-request.json", () => JsonSampleRequests.Anthropic(), ProviderJsonOptions.OPTIONS);
|
yield return new("anthropic-messages-request.json", () => JsonSampleRequests.Anthropic(), ProviderJsonOptions.OPTIONS);
|
||||||
|
yield return new("embedding-request.json", JsonSampleRequests.Embedding, ProviderJsonOptions.OPTIONS);
|
||||||
|
yield return new("google-embedding-request.json", JsonSampleRequests.GoogleEmbedding, ProviderJsonOptions.OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCaseSource(nameof(GoldenRequests))]
|
[TestCaseSource(nameof(GoldenRequests))]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Text.Json;
|
|||||||
|
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Provider.Anthropic;
|
using AIStudio.Provider.Anthropic;
|
||||||
|
using AIStudio.Provider.Google;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
using AIStudio.Tools.ToolCallingSystem;
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
@@ -91,6 +92,16 @@ public static class JsonSampleRequests
|
|||||||
AdditionalApiParameters = ApiParameters(),
|
AdditionalApiParameters = ApiParameters(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A request for embeddings in the shape of the OpenAI API.
|
||||||
|
/// </summary>
|
||||||
|
public static EmbeddingRequest Embedding() => new("embedding-model", ["The first text.", "The second text."]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A request for an embedding to the Gemini API.
|
||||||
|
/// </summary>
|
||||||
|
public static GoogleEmbeddingRequest GoogleEmbedding() => GoogleEmbeddingRequest.Of(["The first text.", "The second text."]);
|
||||||
|
|
||||||
private static List<IMessageBase> Messages() =>
|
private static List<IMessageBase> Messages() =>
|
||||||
[
|
[
|
||||||
new TextMessage("You answer briefly.", "system"),
|
new TextMessage("You answer briefly.", "system"),
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ public static partial class JsonTestRoots
|
|||||||
yield return CaseWithNamespace(typeof(ChatCompletionAPIRequest), options);
|
yield return CaseWithNamespace(typeof(ChatCompletionAPIRequest), options);
|
||||||
yield return CaseWithNamespace(typeof(ResponsesAPIRequest), options);
|
yield return CaseWithNamespace(typeof(ResponsesAPIRequest), options);
|
||||||
yield return CaseWithNamespace(typeof(ChatRequest), options);
|
yield return CaseWithNamespace(typeof(ChatRequest), options);
|
||||||
|
yield return CaseWithNamespace(typeof(EmbeddingRequest), options);
|
||||||
|
yield return CaseWithNamespace(typeof(GoogleEmbeddingRequest), options);
|
||||||
|
|
||||||
//
|
//
|
||||||
// The messages and their parts, which converters write as the type they are of:
|
// The messages and their parts, which converters write as the type they are of:
|
||||||
@@ -43,7 +45,7 @@ public static partial class JsonTestRoots
|
|||||||
foreach (var type in new[] { typeof(string), typeof(int), typeof(long), typeof(double), typeof(decimal), typeof(bool), typeof(Dictionary<string, object>), typeof(List<object>), typeof(JsonElement) })
|
foreach (var type in new[] { typeof(string), typeof(int), typeof(long), typeof(double), typeof(decimal), typeof(bool), typeof(Dictionary<string, object>), typeof(List<object>), typeof(JsonElement) })
|
||||||
yield return Case(new(type, options), "value of a request");
|
yield return Case(new(type, options), "value of a request");
|
||||||
|
|
||||||
yield return Case(new(ProviderToolAdapters.ToChatCompletionTool(JsonSampleRequests.Tool(), hostEnforcesStrict: true).GetType(), options), "tool of a Chat Completions API");
|
yield return CaseWithNamespace(typeof(ChatCompletionFunctionTool), options);
|
||||||
yield return CaseWithNamespace(typeof(ResponsesFunctionTool), options);
|
yield return CaseWithNamespace(typeof(ResponsesFunctionTool), options);
|
||||||
yield return CaseWithNamespace(typeof(ResponsesFunctionCallOutputItem), options);
|
yield return CaseWithNamespace(typeof(ResponsesFunctionCallOutputItem), options);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user