Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c9a0740
Rename ModelId -> DefaultModelId
SteveSandersonMS Mar 19, 2025
2c952f8
Add ChatModelMetadata and EmbeddingModelMetadata
SteveSandersonMS Mar 19, 2025
3f5b26b
In GetResponse<T>, use model metadata to determine whether native sch…
SteveSandersonMS Mar 19, 2025
b7dcae7
Ollama always supports native JSON schema
SteveSandersonMS Mar 19, 2025
2e2eca5
OpenAI sometimes supports native JSON schema
SteveSandersonMS Mar 19, 2025
07917f0
Same for Azure AI Inference
SteveSandersonMS Mar 19, 2025
f4f6ca5
Deduplicate tests
SteveSandersonMS Mar 19, 2025
ae8eb98
Cache task
SteveSandersonMS Mar 20, 2025
7ac97f9
Remove the word "native" from JSON schema APIs
SteveSandersonMS Mar 20, 2025
fcf9eb3
Use ValueTask
SteveSandersonMS Mar 20, 2025
c069c56
Improve chat model metadata caching
SteveSandersonMS Mar 20, 2025
18ac7c6
Don't hardcode model-specific metadata
SteveSandersonMS Mar 20, 2025
a922c31
Remove per-model metadata from base libraries
SteveSandersonMS Mar 21, 2025
ca412c1
Remove per-model metadata from providers and flip default on useJsonS…
SteveSandersonMS Mar 21, 2025
2079845
Fix native structured output on OpenAI
SteveSandersonMS Mar 21, 2025
fb7a149
Fix integration test for structured output on AzureAIInference
SteveSandersonMS Mar 21, 2025
3832a27
Work around llama3.1 unreliability with structured output
SteveSandersonMS Mar 21, 2025
4296a4e
Update src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ChatClien…
SteveSandersonMS Mar 21, 2025
b13225b
Update test
SteveSandersonMS Mar 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ public class ChatClientMetadata
/// appropriate name defined in the OpenTelemetry Semantic Conventions for Generative AI systems.
/// </param>
/// <param name="providerUri">The URL for accessing the chat provider, if applicable.</param>
/// <param name="modelId">The ID of the chat model used, if applicable.</param>
public ChatClientMetadata(string? providerName = null, Uri? providerUri = null, string? modelId = null)
/// <param name="defaultModelId">The ID of the chat model used by default, if applicable.</param>
public ChatClientMetadata(string? providerName = null, Uri? providerUri = null, string? defaultModelId = null)
{
ModelId = modelId;
DefaultModelId = defaultModelId;
ProviderName = providerName;
ProviderUri = providerUri;
}
Expand All @@ -32,10 +32,10 @@ public ChatClientMetadata(string? providerName = null, Uri? providerUri = null,
/// <summary>Gets the URL for accessing the chat provider.</summary>
public Uri? ProviderUri { get; }

/// <summary>Gets the ID of the model used by this chat provider.</summary>
/// <summary>Gets the ID of the default model used by this chat client.</summary>
/// <remarks>
/// This value can be null if either the name is unknown or there are multiple possible models associated with this instance.
/// This value can be null if no default model is set on the corresponding <see cref="IChatClient"/>.
/// An individual request may override this value via <see cref="ChatOptions.ModelId"/>.
/// </remarks>
public string? ModelId { get; }
public string? DefaultModelId { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@ namespace Microsoft.Extensions.AI;
public class EmbeddingGeneratorMetadata
{
/// <summary>Initializes a new instance of the <see cref="EmbeddingGeneratorMetadata"/> class.</summary>

/// <param name="providerName">
/// The name of the embedding generation provider, if applicable. Where possible, this should map to the
/// appropriate name defined in the OpenTelemetry Semantic Conventions for Generative AI systems.
/// </param>
/// <param name="providerUri">The URL for accessing the embedding generation provider, if applicable.</param>
/// <param name="modelId">The ID of the embedding generation model used, if applicable.</param>
/// <param name="dimensions">The number of dimensions in vectors produced by this generator, if applicable.</param>
public EmbeddingGeneratorMetadata(string? providerName = null, Uri? providerUri = null, string? modelId = null, int? dimensions = null)
/// <param name="defaultModelId">The ID of the default embedding generation model used, if applicable.</param>
/// <param name="defaultModelDimensions">The number of dimensions in vectors produced by the default model, if applicable.</param>
public EmbeddingGeneratorMetadata(string? providerName = null, Uri? providerUri = null, string? defaultModelId = null, int? defaultModelDimensions = null)
{
ModelId = modelId;
DefaultModelId = defaultModelId;
ProviderName = providerName;
ProviderUri = providerUri;
Dimensions = dimensions;
DefaultModelDimensions = defaultModelDimensions;
}

/// <summary>Gets the name of the embedding generation provider.</summary>
Expand All @@ -35,17 +34,17 @@ public EmbeddingGeneratorMetadata(string? providerName = null, Uri? providerUri
/// <summary>Gets the URL for accessing the embedding generation provider.</summary>
public Uri? ProviderUri { get; }

/// <summary>Gets the ID of the model used by this embedding generation provider.</summary>
/// <summary>Gets the ID of the default model used by this embedding generator.</summary>
/// <remarks>
/// This value can be null if either the name is unknown or there are multiple possible models associated with this instance.
/// This value can be null if no default model is set on the corresponding embedding generator.
/// An individual request may override this value via <see cref="EmbeddingGenerationOptions.ModelId"/>.
/// </remarks>
public string? ModelId { get; }
public string? DefaultModelId { get; }

/// <summary>Gets the number of dimensions in the embeddings produced by this instance.</summary>
/// <summary>Gets the number of dimensions in the embeddings produced by the default model.</summary>
/// <remarks>
/// This value can be null if either the number of dimensions is unknown or there are multiple possible lengths associated with this instance.
/// This value can be null if either the number of dimensions is unknown or there are multiple possible lengths associated with this model.
/// An individual request may override this value via <see cref="EmbeddingGenerationOptions.Dimensions"/>.
/// </remarks>
public int? Dimensions { get; }
public int? DefaultModelDimensions { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public AzureAIInferenceChatClient(ChatCompletionsClient chatCompletionsClient, s
var providerUrl = typeof(ChatCompletionsClient).GetField("_endpoint", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(chatCompletionsClient) as Uri;

_metadata = new("az.ai.inference", providerUrl, modelId);
_metadata = new ChatClientMetadata("az.ai.inference", providerUrl, modelId);
}

/// <summary>Gets or sets <see cref="JsonSerializerOptions"/> to use for any serialization activities related to tool call arguments and results.</summary>
Expand Down Expand Up @@ -288,7 +288,7 @@ private ChatCompletionsOptions ToAzureAIOptions(IEnumerable<ChatMessage> chatCon
{
ChatCompletionsOptions result = new(ToAzureAIInferenceChatMessages(chatContents))
{
Model = options?.ModelId ?? _metadata.ModelId ?? throw new InvalidOperationException("No model id was provided when either constructing the client or in the chat options.")
Model = options?.ModelId ?? _metadata.DefaultModelId ?? throw new InvalidOperationException("No model id was provided when either constructing the client or in the chat options.")
};

if (options is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public AzureAIInferenceEmbeddingGenerator(
var providerUrl = typeof(EmbeddingsClient).GetField("_endpoint", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(embeddingsClient) as Uri;

_metadata = new("az.ai.inference", providerUrl, modelId, dimensions);
_metadata = new EmbeddingGeneratorMetadata("az.ai.inference", providerUrl, modelId, dimensions);
}

/// <inheritdoc />
Expand Down Expand Up @@ -167,7 +167,7 @@ private EmbeddingsOptions ToAzureAIOptions(IEnumerable<string> inputs, Embedding
EmbeddingsOptions result = new(inputs)
{
Dimensions = options?.Dimensions ?? _dimensions,
Model = options?.ModelId ?? _metadata.ModelId,
Model = options?.ModelId ?? _metadata.DefaultModelId,
EncodingFormat = format,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ private static IEnumerable<string> GetCachingKeysForChatClient(IChatClient chatC
yield return providerUri.AbsoluteUri;
}

string? modelId = metadata?.ModelId;
string? modelId = metadata?.DefaultModelId;
if (!string.IsNullOrWhiteSpace(modelId))
{
yield return modelId!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public OllamaChatClient(Uri endpoint, string? modelId = null, HttpClient? httpCl
_apiChatEndpoint = new Uri(endpoint, "api/chat");
_httpClient = httpClient ?? OllamaUtilities.SharedClient;

_metadata = new("ollama", endpoint, modelId);
_metadata = new ChatClientMetadata("ollama", endpoint, modelId);
}

/// <summary>Gets or sets <see cref="JsonSerializerOptions"/> to use for any serialization activities related to tool call arguments and results.</summary>
Expand Down Expand Up @@ -111,7 +111,7 @@ public async Task<ChatResponse> GetResponseAsync(
{
CreatedAt = DateTimeOffset.TryParse(response.CreatedAt, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTimeOffset createdAt) ? createdAt : null,
FinishReason = ToFinishReason(response),
ModelId = response.Model ?? options?.ModelId ?? _metadata.ModelId,
ModelId = response.Model ?? options?.ModelId ?? _metadata.DefaultModelId,
ResponseId = responseId,
Usage = ParseOllamaChatResponseUsage(response),
};
Expand Down Expand Up @@ -158,7 +158,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
continue;
}

string? modelId = chunk.Model ?? _metadata.ModelId;
string? modelId = chunk.Model ?? _metadata.DefaultModelId;

ChatResponseUpdate update = new()
{
Expand Down Expand Up @@ -306,7 +306,7 @@ private OllamaChatRequest ToOllamaChatRequest(IEnumerable<ChatMessage> messages,
{
Format = ToOllamaChatResponseFormat(options?.ResponseFormat),
Messages = messages.SelectMany(ToOllamaChatRequestMessages).ToArray(),
Model = options?.ModelId ?? _metadata.ModelId ?? string.Empty,
Model = options?.ModelId ?? _metadata.DefaultModelId ?? string.Empty,
Stream = stream,
Tools = options?.ToolMode is not NoneChatToolMode && options?.Tools is { Count: > 0 } tools ? tools.OfType<AIFunction>().Select(ToOllamaTool) : null,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(

// Create request.
string[] inputs = values.ToArray();
string? requestModel = options?.ModelId ?? _metadata.ModelId;
string? requestModel = options?.ModelId ?? _metadata.DefaultModelId;
var request = new OllamaEmbeddingRequest
{
Model = requestModel ?? string.Empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public OpenAIChatClient(OpenAIClient openAIClient, string modelId)
Uri providerUrl = typeof(OpenAIClient).GetField("_endpoint", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(openAIClient) as Uri ?? DefaultOpenAIEndpoint;

_metadata = new("openai", providerUrl, modelId);
_metadata = new ChatClientMetadata("openai", providerUrl, modelId);
}

/// <summary>Initializes a new instance of the <see cref="OpenAIChatClient"/> class for the specified <see cref="ChatClient"/>.</summary>
Expand Down
Loading
Loading