Skip to content

Commit

Permalink
Skeleton implementation of OpenAI serialization methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis committed Dec 4, 2024
1 parent 5656a0d commit 21a45ce
Show file tree
Hide file tree
Showing 4 changed files with 538 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
<NoWarn>$(NoWarn);CA1063;CA1508;CA2227;SA1316;S1121;S3358;EA0002;OPENAI002</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DisableNETStandardCompatErrors>true</DisableNETStandardCompatErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup>
<InjectSharedEmptyCollections>true</InjectSharedEmptyCollections>
<InjectStringHashOnLegacy>true</InjectStringHashOnLegacy>
<InjectSharedServerSentEvents>true</InjectSharedServerSentEvents>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,97 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using OpenAI.Chat;

namespace Microsoft.Extensions.AI;

internal static partial class OpenAIModelMappers
{
internal static IEnumerable<ChatMessage> FromOpenAIChatMessages(IEnumerable<OpenAI.Chat.ChatMessage> inputs, JsonSerializerOptions options)
{
// Maps all of the OpenAI types to the corresponding M.E.AI types.
// Unrecognized or non-processable content is ignored.

foreach (OpenAI.Chat.ChatMessage input in inputs)
{
switch (input)
{
case SystemChatMessage systemMessage:
yield return new ChatMessage
{
Role = ChatRole.System,
AuthorName = systemMessage.ParticipantName,
Contents = FromOpenAIChatContent(systemMessage.Content),
};
break;

case UserChatMessage userMessage:
yield return new ChatMessage
{
Role = ChatRole.User,
AuthorName = userMessage.ParticipantName,
Contents = FromOpenAIChatContent(userMessage.Content),
};
break;

case ToolChatMessage toolMessage:
string textContent = string.Join(string.Empty, toolMessage.Content.Where(part => part.Kind is ChatMessageContentPartKind.Text).Select(part => part.Text));
object? result = textContent;
if (!string.IsNullOrEmpty(textContent))
{
#pragma warning disable CA1031 // Do not catch general exception types
try
{
result = JsonSerializer.Deserialize(textContent, options.GetTypeInfo(typeof(object)));
}
catch
{
// If the content can't be deserialized, leave it as a string.
}
#pragma warning restore CA1031 // Do not catch general exception types
}

yield return new ChatMessage
{
Role = ChatRole.Tool,
Contents = new AIContent[] { new FunctionResultContent(toolMessage.ToolCallId, name: string.Empty, result) },
};
break;

case AssistantChatMessage assistantMessage:

ChatMessage message = new()
{
Role = ChatRole.Assistant,
AuthorName = assistantMessage.ParticipantName,
Contents = FromOpenAIChatContent(assistantMessage.Content),
};

foreach (ChatToolCall toolCall in assistantMessage.ToolCalls)
{
if (!string.IsNullOrWhiteSpace(toolCall.FunctionName))
{
var callContent = ParseCallContentFromBinaryData(toolCall.FunctionArguments, toolCall.Id, toolCall.FunctionName);
callContent.RawRepresentation = toolCall;

message.Contents.Add(callContent);
}
}

if (assistantMessage.Refusal is not null)
{
message.AdditionalProperties ??= new();
message.AdditionalProperties.Add(nameof(assistantMessage.Refusal), assistantMessage.Refusal);
}

yield return message;
break;
}
}
}

/// <summary>Converts an Extensions chat message enumerable to an OpenAI chat message enumerable.</summary>
internal static IEnumerable<OpenAI.Chat.ChatMessage> ToOpenAIChatMessages(IEnumerable<ChatMessage> inputs, JsonSerializerOptions options)
{
Expand Down Expand Up @@ -60,7 +144,7 @@ internal static partial class OpenAIModelMappers

foreach (var content in input.Contents)
{
if (content is FunctionCallContent { CallId: not null } callRequest)
if (content is FunctionCallContent callRequest)
{
message.ToolCalls.Add(
ChatToolCall.CreateFunctionToolCall(
Expand All @@ -82,6 +166,31 @@ internal static partial class OpenAIModelMappers
}
}

private static List<AIContent> FromOpenAIChatContent(IList<ChatMessageContentPart> openAiMessageContentParts)
{
List<AIContent> contents = new();
foreach (var openAiContentPart in openAiMessageContentParts)
{
switch (openAiContentPart.Kind)
{
case ChatMessageContentPartKind.Text:
contents.Add(new TextContent(openAiContentPart.Text));
break;

case ChatMessageContentPartKind.Image when (openAiContentPart.ImageBytes is { } bytes):
contents.Add(new ImageContent(bytes.ToArray(), openAiContentPart.ImageBytesMediaType));
break;

case ChatMessageContentPartKind.Image:
contents.Add(new ImageContent(openAiContentPart.ImageUri?.ToString() ?? string.Empty));
break;

}
}

return contents;
}

/// <summary>Converts a list of <see cref="AIContent"/> to a list of <see cref="ChatMessageContentPart"/>.</summary>
private static List<ChatMessageContentPart> ToOpenAIChatContent(IList<AIContent> contents)
{
Expand Down
Loading

0 comments on commit 21a45ce

Please sign in to comment.