From 4948d3d31150413f52ab425748c28e79be2b1c50 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 14 Jan 2025 11:44:30 +0000 Subject: [PATCH 1/2] Ensure Ollama streaming updates specify a CompletionId. --- src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs | 1 + .../OllamaChatClientTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs index 408fa36d876..608573e05f6 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Ollama/OllamaChatClient.cs @@ -142,6 +142,7 @@ public async IAsyncEnumerable CompleteStreamingAs StreamingChatCompletionUpdate update = new() { + CompletionId = chunk.CreatedAt, Role = chunk.Message?.Role is not null ? new ChatRole(chunk.Message.Role) : null, CreatedAt = DateTimeOffset.TryParse(chunk.CreatedAt, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTimeOffset createdAt) ? createdAt : null, FinishReason = ToFinishReason(chunk), diff --git a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientTests.cs b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientTests.cs index 6c2fcebb154..507666e52fc 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientTests.cs @@ -185,6 +185,7 @@ public async Task BasicRequestResponse_Streaming() for (int i = 0; i < updates.Count; i++) { + Assert.NotNull(updates[i].CompletionId); Assert.Equal(i < updates.Count - 1 ? 1 : 2, updates[i].Contents.Count); Assert.Equal(ChatRole.Assistant, updates[i].Role); Assert.Equal("llama3.1", updates[i].ModelId); From db37095d97d3e3f245d92c4fec6138a5a7b5798a Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 14 Jan 2025 16:25:34 +0000 Subject: [PATCH 2/2] Fill out completion id in serialization layer. --- .../OpenAIModelMapper.ChatCompletion.cs | 5 ++++- .../OpenAIModelMappers.StreamingChatCompletion.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs index 9f35727cf80..6ef871fcbbe 100644 --- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs +++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMapper.ChatCompletion.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -54,7 +55,7 @@ public static OpenAI.Chat.ChatCompletion ToOpenAIChatCompletion(ChatCompletion c } return OpenAIChatModelFactory.ChatCompletion( - id: chatCompletion.CompletionId, + id: chatCompletion.CompletionId ?? CreateCompletionId(), model: chatCompletion.ModelId, createdAt: chatCompletion.CreatedAt ?? default, role: ToOpenAIChatRole(chatCompletion.Message.Role).Value, @@ -584,6 +585,8 @@ private static FunctionCallContent ParseCallContentFromBinaryData(BinaryData ut8 private static T? GetValueOrDefault(this AdditionalPropertiesDictionary? dict, string key) => dict?.TryGetValue(key, out T? value) is true ? value : default; + private static string CreateCompletionId() => $"chatcmpl-{Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)}"; + /// Used to create the JSON payload for an OpenAI chat tool description. public sealed class OpenAIChatToolJson { diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMappers.StreamingChatCompletion.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMappers.StreamingChatCompletion.cs index 9fe6fa3fada..a4d8551461f 100644 --- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMappers.StreamingChatCompletion.cs +++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIModelMappers.StreamingChatCompletion.cs @@ -46,7 +46,7 @@ internal static partial class OpenAIModelMappers } yield return OpenAIChatModelFactory.StreamingChatCompletionUpdate( - completionId: chatCompletionUpdate.CompletionId, + completionId: chatCompletionUpdate.CompletionId ?? CreateCompletionId(), model: chatCompletionUpdate.ModelId, createdAt: chatCompletionUpdate.CreatedAt ?? default, role: ToOpenAIChatRole(chatCompletionUpdate.Role),