diff --git a/samples/cs/Directory.Packages.props b/samples/cs/Directory.Packages.props index e5ba306b..428cbbf8 100644 --- a/samples/cs/Directory.Packages.props +++ b/samples/cs/Directory.Packages.props @@ -7,7 +7,6 @@ - diff --git a/samples/cs/model-management-example/Program.cs b/samples/cs/model-management-example/Program.cs index a34d2737..f06d2f8f 100644 --- a/samples/cs/model-management-example/Program.cs +++ b/samples/cs/model-management-example/Program.cs @@ -1,5 +1,5 @@ using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Microsoft.AI.Foundry.Local.OpenAI; using System.Diagnostics; CancellationToken ct = new CancellationToken(); diff --git a/samples/cs/native-chat-completions/Program.cs b/samples/cs/native-chat-completions/Program.cs index d1527503..31fb9dab 100644 --- a/samples/cs/native-chat-completions/Program.cs +++ b/samples/cs/native-chat-completions/Program.cs @@ -1,7 +1,7 @@ // // using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Microsoft.AI.Foundry.Local.OpenAI; // // diff --git a/samples/cs/tool-calling-foundry-local-sdk/Program.cs b/samples/cs/tool-calling-foundry-local-sdk/Program.cs index 8ac96369..dd7e8c0c 100644 --- a/samples/cs/tool-calling-foundry-local-sdk/Program.cs +++ b/samples/cs/tool-calling-foundry-local-sdk/Program.cs @@ -1,9 +1,7 @@ // // using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; -using Betalgo.Ranul.OpenAI.ObjectModels.SharedModels; +using Microsoft.AI.Foundry.Local.OpenAI; using System.Text.Json; // diff --git a/samples/cs/tutorial-chat-assistant/Program.cs b/samples/cs/tutorial-chat-assistant/Program.cs index 10e9a63b..52eb3e3b 100644 --- a/samples/cs/tutorial-chat-assistant/Program.cs +++ b/samples/cs/tutorial-chat-assistant/Program.cs @@ -1,7 +1,7 @@ // // using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Microsoft.AI.Foundry.Local.OpenAI; using Microsoft.Extensions.Logging; // diff --git a/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj b/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj index a3533047..e48c209d 100644 --- a/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj +++ b/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj @@ -42,7 +42,6 @@ - diff --git a/samples/cs/tutorial-document-summarizer/Program.cs b/samples/cs/tutorial-document-summarizer/Program.cs index bc5546f6..11972827 100644 --- a/samples/cs/tutorial-document-summarizer/Program.cs +++ b/samples/cs/tutorial-document-summarizer/Program.cs @@ -1,7 +1,7 @@ // // using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Microsoft.AI.Foundry.Local.OpenAI; using Microsoft.Extensions.Logging; // diff --git a/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj b/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj index a3533047..e48c209d 100644 --- a/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj +++ b/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj @@ -42,7 +42,6 @@ - diff --git a/samples/cs/tutorial-tool-calling/Program.cs b/samples/cs/tutorial-tool-calling/Program.cs index 74f137db..c6b33425 100644 --- a/samples/cs/tutorial-tool-calling/Program.cs +++ b/samples/cs/tutorial-tool-calling/Program.cs @@ -2,9 +2,7 @@ // using System.Text.Json; using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; -using Betalgo.Ranul.OpenAI.ObjectModels.SharedModels; +using Microsoft.AI.Foundry.Local.OpenAI; using Microsoft.Extensions.Logging; // diff --git a/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj b/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj index a3533047..e48c209d 100644 --- a/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj +++ b/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj @@ -42,7 +42,6 @@ - diff --git a/samples/cs/tutorial-voice-to-text/Program.cs b/samples/cs/tutorial-voice-to-text/Program.cs index 976b44e4..4ed5a2ea 100644 --- a/samples/cs/tutorial-voice-to-text/Program.cs +++ b/samples/cs/tutorial-voice-to-text/Program.cs @@ -1,7 +1,7 @@ // // using Microsoft.AI.Foundry.Local; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Microsoft.AI.Foundry.Local.OpenAI; using Microsoft.Extensions.Logging; using System.Text; // diff --git a/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj b/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj index a3533047..e48c209d 100644 --- a/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj +++ b/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj @@ -42,7 +42,6 @@ - diff --git a/sdk/cs/README.md b/sdk/cs/README.md index 26287217..74f7d132 100644 --- a/sdk/cs/README.md +++ b/sdk/cs/README.md @@ -109,7 +109,7 @@ Catalog access no longer blocks on EP downloads. Call `DownloadAndRegisterEpsAsy using Microsoft.AI.Foundry.Local; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Microsoft.AI.Foundry.Local.OpenAI; // 1. Initialize the singleton manager await FoundryLocalManager.CreateAsync( @@ -276,7 +276,7 @@ audioClient.Settings.Temperature = 0.0f; For real-time microphone-to-text transcription, use `CreateLiveTranscriptionSession()`. Audio is pushed as raw PCM chunks and transcription results stream back as an `IAsyncEnumerable`. -The streaming result type (`LiveAudioTranscriptionResponse`) extends `ConversationItem` from the Betalgo OpenAI SDK's Realtime models, so it's compatible with the OpenAI Realtime API pattern. Access transcribed text via `result.Content[0].Text` or `result.Content[0].Transcript`. +The streaming result type (`LiveAudioTranscriptionResponse`) extends `ConversationItem`, which is compatible with the OpenAI Realtime API pattern. Access transcribed text via `result.Content[0].Text` or `result.Content[0].Transcript`. ```csharp var audioClient = await model.GetAudioClientAsync(); diff --git a/sdk/cs/docs/api/microsoft.ai.foundry.local.openaiaudioclient.md b/sdk/cs/docs/api/microsoft.ai.foundry.local.openaiaudioclient.md index b1b60bd8..75efdcdf 100644 --- a/sdk/cs/docs/api/microsoft.ai.foundry.local.openaiaudioclient.md +++ b/sdk/cs/docs/api/microsoft.ai.foundry.local.openaiaudioclient.md @@ -3,7 +3,6 @@ Namespace: Microsoft.AI.Foundry.Local Audio Client that uses the OpenAI API. - Implemented using Betalgo.Ranul.OpenAI SDK types. ```csharp public class OpenAIAudioClient diff --git a/sdk/cs/docs/api/microsoft.ai.foundry.local.openaichatclient.md b/sdk/cs/docs/api/microsoft.ai.foundry.local.openaichatclient.md index 43e00f6d..e637fd7e 100644 --- a/sdk/cs/docs/api/microsoft.ai.foundry.local.openaichatclient.md +++ b/sdk/cs/docs/api/microsoft.ai.foundry.local.openaichatclient.md @@ -3,7 +3,6 @@ Namespace: Microsoft.AI.Foundry.Local Chat Client that uses the OpenAI API. - Implemented using Betalgo.Ranul.OpenAI SDK types. ```csharp public class OpenAIChatClient diff --git a/sdk/cs/src/Detail/JsonSerializationContext.cs b/sdk/cs/src/Detail/JsonSerializationContext.cs index 37cc81ac..c9b3901e 100644 --- a/sdk/cs/src/Detail/JsonSerializationContext.cs +++ b/sdk/cs/src/Detail/JsonSerializationContext.cs @@ -10,16 +10,12 @@ namespace Microsoft.AI.Foundry.Local.Detail; using System.Text.Json; using System.Text.Json.Serialization; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; -using Betalgo.Ranul.OpenAI.ObjectModels.SharedModels; - using Microsoft.AI.Foundry.Local.OpenAI; [JsonSerializable(typeof(ModelInfo))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(CoreInteropRequest))] -[JsonSerializable(typeof(ChatCompletionCreateRequestExtended))] +[JsonSerializable(typeof(ChatCompletionCreateRequest))] [JsonSerializable(typeof(ChatCompletionCreateResponse))] [JsonSerializable(typeof(AudioCreateTranscriptionRequest))] [JsonSerializable(typeof(AudioCreateTranscriptionResponse))] @@ -27,7 +23,7 @@ namespace Microsoft.AI.Foundry.Local.Detail; [JsonSerializable(typeof(EpInfo[]))] [JsonSerializable(typeof(EpDownloadResult))] [JsonSerializable(typeof(JsonElement))] -[JsonSerializable(typeof(ResponseFormatExtended))] +[JsonSerializable(typeof(ResponseFormat))] [JsonSerializable(typeof(ToolChoice))] [JsonSerializable(typeof(ToolDefinition))] [JsonSerializable(typeof(IList))] @@ -35,8 +31,7 @@ namespace Microsoft.AI.Foundry.Local.Detail; [JsonSerializable(typeof(IList))] [JsonSerializable(typeof(PropertyDefinition))] [JsonSerializable(typeof(IList))] -// --- Audio streaming types (LiveAudioTranscriptionResponse inherits ConversationItem -// which has AOT-incompatible JsonConverters, so we only register the raw deserialization type) --- +// --- Audio streaming types (we only register the raw deserialization type used by LiveAudioTranscriptionResponse.FromJson) --- [JsonSerializable(typeof(LiveAudioTranscriptionRaw))] [JsonSerializable(typeof(CoreErrorResponse))] [JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, diff --git a/sdk/cs/src/Microsoft.AI.Foundry.Local.csproj b/sdk/cs/src/Microsoft.AI.Foundry.Local.csproj index e8a7b755..4dc678e0 100644 --- a/sdk/cs/src/Microsoft.AI.Foundry.Local.csproj +++ b/sdk/cs/src/Microsoft.AI.Foundry.Local.csproj @@ -121,7 +121,6 @@ - diff --git a/sdk/cs/src/OpenAI/AudioClient.cs b/sdk/cs/src/OpenAI/AudioClient.cs index a8cbc1d7..c9f8f595 100644 --- a/sdk/cs/src/OpenAI/AudioClient.cs +++ b/sdk/cs/src/OpenAI/AudioClient.cs @@ -8,8 +8,6 @@ namespace Microsoft.AI.Foundry.Local; using System.Runtime.CompilerServices; using System.Threading.Channels; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; using Microsoft.AI.Foundry.Local.Detail; using Microsoft.AI.Foundry.Local.OpenAI; @@ -17,7 +15,6 @@ namespace Microsoft.AI.Foundry.Local; /// /// Audio Client that uses the OpenAI API. -/// Implemented using Betalgo.Ranul.OpenAI SDK types. /// public class OpenAIAudioClient { @@ -97,8 +94,7 @@ public LiveAudioTranscriptionSession CreateLiveTranscriptionSession() private async Task TranscribeAudioImplAsync(string audioFilePath, CancellationToken? ct) { - var openaiRequest = AudioTranscriptionCreateRequestExtended.FromUserInput(_modelId, audioFilePath, Settings); - + var openaiRequest = AudioTranscriptionRequestResponseExtensions.CreateAudioRequest(_modelId, audioFilePath, Settings); var request = new CoreInteropRequest { @@ -120,7 +116,7 @@ private async Task TranscribeAudioImplAsync(st private async IAsyncEnumerable TranscribeAudioStreamingImplAsync( string audioFilePath, [EnumeratorCancellation] CancellationToken ct) { - var openaiRequest = AudioTranscriptionCreateRequestExtended.FromUserInput(_modelId, audioFilePath, Settings); + var openaiRequest = AudioTranscriptionRequestResponseExtensions.CreateAudioRequest(_modelId, audioFilePath, Settings); var request = new CoreInteropRequest { diff --git a/sdk/cs/src/OpenAI/AudioTranscriptionRequestResponseTypes.cs b/sdk/cs/src/OpenAI/AudioTranscriptionRequestResponseTypes.cs index 4ba28336..ed7114e9 100644 --- a/sdk/cs/src/OpenAI/AudioTranscriptionRequestResponseTypes.cs +++ b/sdk/cs/src/OpenAI/AudioTranscriptionRequestResponseTypes.cs @@ -8,34 +8,21 @@ namespace Microsoft.AI.Foundry.Local.OpenAI; using System.Globalization; using System.Text.Json; -using System.Text.Json.Serialization; - -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; using Microsoft.AI.Foundry.Local; using Microsoft.AI.Foundry.Local.Detail; - using Microsoft.Extensions.Logging; -internal record AudioTranscriptionCreateRequestExtended : AudioCreateTranscriptionRequest +internal static class AudioTranscriptionRequestResponseExtensions { - // Valid entries: - // int language - // int temperature - [JsonPropertyName("metadata")] - public Dictionary? Metadata { get; set; } - - internal static AudioTranscriptionCreateRequestExtended FromUserInput(string modelId, - string audioFilePath, - OpenAIAudioClient.AudioSettings settings) + internal static AudioCreateTranscriptionRequest CreateAudioRequest(string modelId, + string audioFilePath, + OpenAIAudioClient.AudioSettings settings) { - var request = new AudioTranscriptionCreateRequestExtended + var request = new AudioCreateTranscriptionRequest { Model = modelId, FileName = audioFilePath, - - // apply our specific settings Language = settings.Language, Temperature = settings.Temperature }; @@ -57,16 +44,14 @@ internal static AudioTranscriptionCreateRequestExtended FromUserInput(string mod request.Metadata = metadata; } - return request; } -} -internal static class AudioTranscriptionRequestResponseExtensions -{ + internal static string ToJson(this AudioCreateTranscriptionRequest request) { return JsonSerializer.Serialize(request, JsonSerializationContext.Default.AudioCreateTranscriptionRequest); } + internal static AudioCreateTranscriptionResponse ToAudioTranscription(this ICoreInterop.Response response, ILogger logger) { diff --git a/sdk/cs/src/OpenAI/ChatClient.cs b/sdk/cs/src/OpenAI/ChatClient.cs index b9f889f2..34daad4e 100644 --- a/sdk/cs/src/OpenAI/ChatClient.cs +++ b/sdk/cs/src/OpenAI/ChatClient.cs @@ -10,16 +10,12 @@ namespace Microsoft.AI.Foundry.Local; using System.Runtime.CompilerServices; using System.Threading.Channels; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; - using Microsoft.AI.Foundry.Local.Detail; using Microsoft.AI.Foundry.Local.OpenAI; using Microsoft.Extensions.Logging; /// /// Chat Client that uses the OpenAI API. -/// Implemented using Betalgo.Ranul.OpenAI SDK types. /// public class OpenAIChatClient { @@ -48,7 +44,7 @@ public record ChatSettings public int? TopK { get; set; } public float? TopP { get; set; } // Settings for tool calling and structured outputs - public ResponseFormatExtended? ResponseFormat { get; set; } + public ResponseFormat? ResponseFormat { get; set; } public ToolChoice? ToolChoice { get; set; } } @@ -132,7 +128,7 @@ private async Task CompleteChatImplAsync(IEnumerab { Settings.Stream = false; - var chatRequest = ChatCompletionCreateRequestExtended.FromUserInput(_modelId, messages, tools, Settings); + var chatRequest = ChatCompletionsRequestResponseExtensions.CreateChatRequest(_modelId, messages, tools, Settings); var chatRequestJson = chatRequest.ToJson(); var request = new CoreInteropRequest { Params = new() { { "OpenAICreateRequest", chatRequestJson } } }; @@ -150,7 +146,7 @@ private async IAsyncEnumerable ChatStreamingImplAs { Settings.Stream = true; - var chatRequest = ChatCompletionCreateRequestExtended.FromUserInput(_modelId, messages, tools, Settings); + var chatRequest = ChatCompletionsRequestResponseExtensions.CreateChatRequest(_modelId, messages, tools, Settings); var chatRequestJson = chatRequest.ToJson(); var request = new CoreInteropRequest { Params = new() { { "OpenAICreateRequest", chatRequestJson } } }; diff --git a/sdk/cs/src/OpenAI/ChatCompletionRequestResponseTypes.cs b/sdk/cs/src/OpenAI/ChatCompletionRequestResponseTypes.cs index cfd3e08c..879ff6fc 100644 --- a/sdk/cs/src/OpenAI/ChatCompletionRequestResponseTypes.cs +++ b/sdk/cs/src/OpenAI/ChatCompletionRequestResponseTypes.cs @@ -8,40 +8,23 @@ namespace Microsoft.AI.Foundry.Local.OpenAI; using System.Globalization; using System.Text.Json; -using System.Text.Json.Serialization; - -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; using Microsoft.AI.Foundry.Local; using Microsoft.AI.Foundry.Local.Detail; using Microsoft.Extensions.Logging; -// https://platform.openai.com/docs/api-reference/chat/create -// Using the Betalgo ChatCompletionCreateRequest and extending with the `metadata` field for additional parameters -// which is part of the OpenAI spec but for some reason not part of the Betalgo request object. -internal class ChatCompletionCreateRequestExtended : ChatCompletionCreateRequest +internal static class ChatCompletionsRequestResponseExtensions { - // Valid entries: - // int top_k - // int random_seed - [JsonPropertyName("metadata")] - public Dictionary? Metadata { get; set; } - - [JsonPropertyName("response_format")] - public new ResponseFormatExtended? ResponseFormat { get; set; } - - internal static ChatCompletionCreateRequestExtended FromUserInput(string modelId, - IEnumerable messages, - IEnumerable? tools, - OpenAIChatClient.ChatSettings settings) + internal static ChatCompletionCreateRequest CreateChatRequest(string modelId, + IEnumerable messages, + IEnumerable? tools, + OpenAIChatClient.ChatSettings settings) { - var request = new ChatCompletionCreateRequestExtended + var request = new ChatCompletionCreateRequest { Model = modelId, Messages = messages.ToList(), Tools = tools?.ToList(), - // Apply our specific settings FrequencyPenalty = settings.FrequencyPenalty, MaxTokens = settings.MaxTokens, N = settings.N, @@ -49,7 +32,6 @@ internal static ChatCompletionCreateRequestExtended FromUserInput(string modelId PresencePenalty = settings.PresencePenalty, Stream = settings.Stream, TopP = settings.TopP, - // Apply tool calling and structured output settings ResponseFormat = settings.ResponseFormat, ToolChoice = settings.ToolChoice }; @@ -71,16 +53,12 @@ internal static ChatCompletionCreateRequestExtended FromUserInput(string modelId request.Metadata = metadata; } - return request; } -} -internal static class ChatCompletionsRequestResponseExtensions -{ - internal static string ToJson(this ChatCompletionCreateRequestExtended request) + internal static string ToJson(this ChatCompletionCreateRequest request) { - return JsonSerializer.Serialize(request, JsonSerializationContext.Default.ChatCompletionCreateRequestExtended); + return JsonSerializer.Serialize(request, JsonSerializationContext.Default.ChatCompletionCreateRequest); } internal static ChatCompletionCreateResponse ToChatCompletion(this ICoreInterop.Response response, ILogger logger) diff --git a/sdk/cs/src/OpenAI/LiveAudioTranscriptionTypes.cs b/sdk/cs/src/OpenAI/LiveAudioTranscriptionTypes.cs index a0e98542..395d6143 100644 --- a/sdk/cs/src/OpenAI/LiveAudioTranscriptionTypes.cs +++ b/sdk/cs/src/OpenAI/LiveAudioTranscriptionTypes.cs @@ -2,7 +2,6 @@ namespace Microsoft.AI.Foundry.Local.OpenAI; using System.Text.Json; using System.Text.Json.Serialization; -using Betalgo.Ranul.OpenAI.ObjectModels.RealtimeModels; using Microsoft.AI.Foundry.Local; using Microsoft.AI.Foundry.Local.Detail; diff --git a/sdk/cs/src/OpenAI/OpenAITypes.cs b/sdk/cs/src/OpenAI/OpenAITypes.cs new file mode 100644 index 00000000..2e2c0a18 --- /dev/null +++ b/sdk/cs/src/OpenAI/OpenAITypes.cs @@ -0,0 +1,331 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) Microsoft. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.AI.Foundry.Local.OpenAI; + +using System.Text.Json.Serialization; + +/// +/// A chat message in a conversation. +/// +public class ChatMessage +{ + [JsonPropertyName("role")] + public string? Role { get; set; } + + [JsonPropertyName("content")] + public string? Content { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("tool_calls")] + public List? ToolCalls { get; set; } + + [JsonPropertyName("tool_call_id")] + public string? ToolCallId { get; set; } + + public static ChatMessage CreateSystemMessage(string content) => + new() { Role = "system", Content = content }; + + public static ChatMessage CreateUserMessage(string content) => + new() { Role = "user", Content = content }; + + public static ChatMessage CreateAssistantMessage(string content) => + new() { Role = "assistant", Content = content }; + + public static ChatMessage CreateToolMessage(string toolCallId, string content) => + new() { Role = "tool", Content = content, ToolCallId = toolCallId }; +} + +/// +/// A tool call requested by the assistant. +/// +public class ToolCall +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("function")] + public FunctionCall? FunctionCall { get; set; } +} + +/// +/// A function call within a tool call. +/// +public class FunctionCall +{ + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("arguments")] + public string? Arguments { get; set; } +} + +/// +/// Request object for creating a chat completion. +/// +public class ChatCompletionCreateRequest +{ + [JsonPropertyName("model")] + public string? Model { get; set; } + + [JsonPropertyName("messages")] + public List? Messages { get; set; } + + [JsonPropertyName("tools")] + public List? Tools { get; set; } + + [JsonPropertyName("stream")] + public bool? Stream { get; set; } + + [JsonPropertyName("frequency_penalty")] + public float? FrequencyPenalty { get; set; } + + [JsonPropertyName("max_tokens")] + public int? MaxTokens { get; set; } + + [JsonPropertyName("n")] + public int? N { get; set; } + + [JsonPropertyName("temperature")] + public float? Temperature { get; set; } + + [JsonPropertyName("presence_penalty")] + public float? PresencePenalty { get; set; } + + [JsonPropertyName("top_p")] + public float? TopP { get; set; } + + [JsonPropertyName("response_format")] + public ResponseFormat? ResponseFormat { get; set; } + + [JsonPropertyName("tool_choice")] + public ToolChoice? ToolChoice { get; set; } + + [JsonPropertyName("metadata")] + public Dictionary? Metadata { get; set; } +} + +/// +/// Response from a chat completion request. +/// +public class ChatCompletionCreateResponse +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("object")] + public string? ObjectTypeName { get; set; } + + [JsonPropertyName("created")] + public long? Created { get; set; } + + [JsonPropertyName("model")] + public string? Model { get; set; } + + [JsonPropertyName("choices")] + public List Choices { get; set; } = []; + + [JsonPropertyName("usage")] + public CompletionUsage? Usage { get; set; } + + [JsonPropertyName("system_fingerprint")] + public string? SystemFingerprint { get; set; } +} + +/// +/// A single choice in a chat completion response. +/// +public class ChatChoice +{ + [JsonPropertyName("index")] + public int? Index { get; set; } + + [JsonPropertyName("message")] + public ChatMessage? Message { get; set; } + + [JsonPropertyName("finish_reason")] + public string? FinishReason { get; set; } + + [JsonPropertyName("logprobs")] + public object? Logprobs { get; set; } +} + +/// +/// Token usage statistics for a completion request. +/// +public class CompletionUsage +{ + [JsonPropertyName("prompt_tokens")] + public int? PromptTokens { get; set; } + + [JsonPropertyName("completion_tokens")] + public int? CompletionTokens { get; set; } + + [JsonPropertyName("total_tokens")] + public int? TotalTokens { get; set; } +} + +/// +/// Definition of a tool that can be used by the model. +/// +public class ToolDefinition +{ + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("function")] + public FunctionDefinition? Function { get; set; } +} + +/// +/// Definition of a function that can be called as a tool. +/// +public class FunctionDefinition +{ + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("description")] + public string? Description { get; set; } + + [JsonPropertyName("parameters")] + public PropertyDefinition? Parameters { get; set; } +} + +/// +/// JSON Schema property definition for function parameters. +/// +public class PropertyDefinition +{ + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("description")] + public string? Description { get; set; } + + [JsonPropertyName("properties")] + public Dictionary? Properties { get; set; } + + [JsonPropertyName("required")] + public IList? Required { get; set; } + + [JsonPropertyName("enum")] + public IList? Enum { get; set; } +} + +/// +/// Controls which tool the model should use. +/// +public class ToolChoice +{ + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + public static ToolChoice None => new() { Type = "none" }; + public static ToolChoice Auto => new() { Type = "auto" }; + public static ToolChoice Required => new() { Type = "required" }; +} + +/// +/// Format specification for the model response. +/// +public class ResponseFormat +{ + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("json_schema")] + public string? JsonSchema { get; set; } + + [JsonPropertyName("lark_grammar")] + public string? LarkGrammar { get; set; } +} + +/// +/// Request object for creating an audio transcription. +/// +public class AudioCreateTranscriptionRequest +{ + [JsonPropertyName("model")] + public string? Model { get; set; } + + [JsonPropertyName("file_name")] + public string? FileName { get; set; } + + [JsonPropertyName("language")] + public string? Language { get; set; } + + [JsonPropertyName("temperature")] + public float? Temperature { get; set; } + + [JsonPropertyName("metadata")] + public Dictionary? Metadata { get; set; } +} + +/// +/// Response from an audio transcription request. +/// +public class AudioCreateTranscriptionResponse +{ + [JsonPropertyName("text")] + public string? Text { get; set; } + + [JsonPropertyName("task")] + public string? Task { get; set; } + + [JsonPropertyName("language")] + public string? Language { get; set; } + + [JsonPropertyName("duration")] + public double? Duration { get; set; } +} + +/// +/// A conversation item in a realtime audio session. +/// +public class ConversationItem +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("object")] + public string? Object { get; set; } + + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("status")] + public string? Status { get; set; } + + [JsonPropertyName("role")] + public string? Role { get; set; } + + [JsonPropertyName("content")] + public List? Content { get; set; } +} + +/// +/// A content part within a conversation item. +/// +public class ContentPart +{ + [JsonPropertyName("type")] + public string? Type { get; set; } + + [JsonPropertyName("text")] + public string? Text { get; set; } + + [JsonPropertyName("transcript")] + public string? Transcript { get; set; } +} diff --git a/sdk/cs/src/OpenAI/ToolCallingExtensions.cs b/sdk/cs/src/OpenAI/ToolCallingExtensions.cs index fb3a72bf..016e51ad 100644 --- a/sdk/cs/src/OpenAI/ToolCallingExtensions.cs +++ b/sdk/cs/src/OpenAI/ToolCallingExtensions.cs @@ -1,23 +1,2 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) Microsoft. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace Microsoft.AI.Foundry.Local.OpenAI; - -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; - -using System.Text.Json.Serialization; - -// Extend response format beyond the OpenAI spec for LARK grammars -public class ResponseFormatExtended : ResponseFormat -{ - // Ex: - // 1. {"type": "text"} - // 2. {"type": "json_object"} - // 3. {"type": "json_schema", "json_schema": } - // 4. {"type": "lark_grammar", "lark_grammar": } - [JsonPropertyName("lark_grammar")] - public string? LarkGrammar { get; set; } -} +// ResponseFormat (previously ResponseFormatExtended) is now defined in OpenAITypes.cs +// with LarkGrammar support built in. This file is kept for git history continuity. diff --git a/sdk/cs/test/FoundryLocal.Tests/ChatCompletionsTests.cs b/sdk/cs/test/FoundryLocal.Tests/ChatCompletionsTests.cs index 2624f98a..ed1af0cb 100644 --- a/sdk/cs/test/FoundryLocal.Tests/ChatCompletionsTests.cs +++ b/sdk/cs/test/FoundryLocal.Tests/ChatCompletionsTests.cs @@ -9,9 +9,7 @@ namespace Microsoft.AI.Foundry.Local.Tests; using System.Text; using System.Threading.Tasks; -using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; -using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; -using Betalgo.Ranul.OpenAI.ObjectModels.SharedModels; +using Microsoft.AI.Foundry.Local.OpenAI; internal sealed class ChatCompletionsTests { diff --git a/sdk/python/requirements.txt b/sdk/python/requirements.txt index 801f577d..acb9b580 100644 --- a/sdk/python/requirements.txt +++ b/sdk/python/requirements.txt @@ -1,6 +1,5 @@ pydantic>=2.0.0 requests>=2.32.4 -openai>=2.24.0 # Standard native binary packages from the ORT-Nightly PyPI feed. foundry-local-core==0.9.0.dev20260327060216 onnxruntime-core==1.24.3 diff --git a/sdk/python/src/openai/__init__.py b/sdk/python/src/openai/__init__.py index e445ba1d..d67f6f1a 100644 --- a/sdk/python/src/openai/__init__.py +++ b/sdk/python/src/openai/__init__.py @@ -6,5 +6,17 @@ from .chat_client import ChatClient, ChatClientSettings from .audio_client import AudioClient +from .types import ( + ChatCompletion, + ChatCompletionChunk, + ChatCompletionMessageParam, +) -__all__ = ["AudioClient", "ChatClient", "ChatClientSettings"] +__all__ = [ + "AudioClient", + "ChatClient", + "ChatClientSettings", + "ChatCompletion", + "ChatCompletionChunk", + "ChatCompletionMessageParam", +] diff --git a/sdk/python/src/openai/chat_client.py b/sdk/python/src/openai/chat_client.py index 0b0d58bc..c6bbca7d 100644 --- a/sdk/python/src/openai/chat_client.py +++ b/sdk/python/src/openai/chat_client.py @@ -12,12 +12,7 @@ from ..detail.core_interop import CoreInterop, InteropRequest from ..exception import FoundryLocalException -from openai.types.chat.chat_completion_message_param import ChatCompletionMessageParam -from openai.types.chat.completion_create_params import CompletionCreateParamsBase, \ - CompletionCreateParamsNonStreaming, \ - CompletionCreateParamsStreaming -from openai.types.chat import ChatCompletion -from openai.types.chat.chat_completion_chunk import ChatCompletionChunk +from .types import ChatCompletionMessageParam, ChatCompletion, ChatCompletionChunk from typing import Any, Dict, Generator, List, Optional logger = logging.getLogger(__name__) @@ -185,12 +180,7 @@ def _create_request( **self.settings._serialize(), } - if streaming: - chat_request = CompletionCreateParamsStreaming(request) - else: - chat_request = CompletionCreateParamsNonStreaming(request) - - return json.dumps(chat_request) + return json.dumps(request) def complete_chat(self, messages: List[ChatCompletionMessageParam], tools: Optional[List[Dict[str, Any]]] = None): """Perform a non-streaming chat completion. diff --git a/sdk/python/src/openai/types.py b/sdk/python/src/openai/types.py new file mode 100644 index 00000000..234e22a5 --- /dev/null +++ b/sdk/python/src/openai/types.py @@ -0,0 +1,102 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +# -------------------------------------------------------------------------- + +"""OpenAI-compatible DTO types for chat completions.""" + +from __future__ import annotations + +from typing import Any, Dict, List, Optional + +from pydantic import BaseModel + + +# Type alias for chat completion message parameters (plain dicts). +ChatCompletionMessageParam = Dict[str, Any] + + +class FunctionCall(BaseModel): + """A function call within a tool call.""" + name: str + arguments: str + + +class FunctionCallChunk(BaseModel): + """Incremental function call in a streaming response.""" + name: Optional[str] = None + arguments: Optional[str] = None + + +class ChatCompletionMessageToolCall(BaseModel): + """A tool call in a chat completion response.""" + id: str + type: str + function: FunctionCall + + +class ChoiceToolCallChunk(BaseModel): + """Incremental tool call in a streaming chunk.""" + index: int + id: Optional[str] = None + type: Optional[str] = None + function: Optional[FunctionCallChunk] = None + + +class ChatCompletionMessage(BaseModel): + """A message in a chat completion response.""" + role: str + content: Optional[str] = None + tool_calls: Optional[List[ChatCompletionMessageToolCall]] = None + + +class CompletionUsage(BaseModel): + """Token usage statistics.""" + prompt_tokens: int + completion_tokens: int + total_tokens: int + + +class Choice(BaseModel): + """A choice in a non-streaming chat completion response.""" + index: int + message: ChatCompletionMessage + finish_reason: Optional[str] = None + logprobs: Optional[Any] = None + + +class ChatCompletion(BaseModel): + """Non-streaming chat completion response.""" + id: str + object: str + created: int + model: str + choices: List[Choice] + usage: Optional[CompletionUsage] = None + system_fingerprint: Optional[str] = None + + +class ChoiceDelta(BaseModel): + """Delta content in a streaming chunk.""" + role: Optional[str] = None + content: Optional[str] = None + tool_calls: Optional[List[ChoiceToolCallChunk]] = None + + +class StreamChoice(BaseModel): + """A choice in a streaming chat completion chunk.""" + index: int + delta: Optional[ChoiceDelta] = None + finish_reason: Optional[str] = None + logprobs: Optional[Any] = None + + +class ChatCompletionChunk(BaseModel): + """Streaming chat completion chunk.""" + id: str + object: str + created: int + model: str + choices: List[StreamChoice] + usage: Optional[CompletionUsage] = None + system_fingerprint: Optional[str] = None diff --git a/sdk/rust/.cargo/config.toml b/sdk/rust/.cargo/config.toml index 84c57445..552ab806 100644 --- a/sdk/rust/.cargo/config.toml +++ b/sdk/rust/.cargo/config.toml @@ -1,7 +1 @@ [registries] - -[source.crates-io] -replace-with = "ORT-Nightly" - -[source.ORT-Nightly] -registry = "sparse+https://pkgs.dev.azure.com/aiinfra/PublicPackages/_packaging/ORT-Nightly/Cargo/index/" diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 2a6292b7..ada68429 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -24,7 +24,6 @@ tokio-stream = "0.1" futures-core = "0.3" reqwest = { version = "0.12", features = ["json"] } urlencoding = "2" -async-openai = { version = "0.33", default-features = false, features = ["chat-completion-types"] } [build-dependencies] ureq = "3" diff --git a/sdk/rust/GENERATE-DOCS.md b/sdk/rust/GENERATE-DOCS.md index f02b5d99..1be5d6a1 100644 --- a/sdk/rust/GENERATE-DOCS.md +++ b/sdk/rust/GENERATE-DOCS.md @@ -26,8 +26,8 @@ The SDK re-exports all public types from the crate root. Key modules: | `ModelVariant` | Single variant — download, load, unload | | `ChatClient` | OpenAI-compatible chat completions (sync + streaming) | | `AudioClient` | OpenAI-compatible audio transcription (sync + streaming) | -| `CreateChatCompletionResponse` | Typed chat completion response (from `async-openai`) | -| `CreateChatCompletionStreamResponse` | Typed streaming chat chunk (from `async-openai`) | +| `CreateChatCompletionResponse` | Typed chat completion response | +| `CreateChatCompletionStreamResponse` | Typed streaming chat chunk | | `AudioTranscriptionResponse` | Typed audio transcription response | | `FoundryLocalError` | Error enum with variants for all failure modes | diff --git a/sdk/rust/docs/api.md b/sdk/rust/docs/api.md index 278402fb..d68f318e 100644 --- a/sdk/rust/docs/api.md +++ b/sdk/rust/docs/api.md @@ -517,7 +517,7 @@ Implements: `Display`, `Error`, `From`, `From ## Re-exported OpenAI Types -The following types from `async_openai` are re-exported at the crate root for convenience: +The following types are re-exported at the crate root for convenience: **Request types:** - `ChatCompletionRequestMessage` diff --git a/sdk/rust/src/lib.rs b/sdk/rust/src/lib.rs index 872a875c..da1b1e80 100644 --- a/sdk/rust/src/lib.rs +++ b/sdk/rust/src/lib.rs @@ -21,23 +21,21 @@ pub use self::types::{ ModelSettings, Parameter, PromptTemplate, Runtime, }; -// Re-export OpenAI request types so callers can construct typed messages. -pub use async_openai::types::chat::{ +// Re-export OpenAI-compatible chat completion types. +pub use crate::openai::chat_types::{ + ChatChoice, ChatChoiceStream, ChatCompletionMessageToolCall, + ChatCompletionMessageToolCallChunk, ChatCompletionMessageToolCalls, ChatCompletionNamedToolChoice, ChatCompletionRequestAssistantMessage, ChatCompletionRequestMessage, ChatCompletionRequestSystemMessage, ChatCompletionRequestToolMessage, ChatCompletionRequestUserMessage, - ChatCompletionToolChoiceOption, ChatCompletionTools, FunctionObject, + ChatCompletionResponseMessage, ChatCompletionStreamResponseDelta, + ChatCompletionToolChoiceOption, ChatCompletionTools, CompletionUsage, + CreateChatCompletionResponse, CreateChatCompletionStreamResponse, FinishReason, FunctionCall, + FunctionCallStream, FunctionObject, }; -// Re-export OpenAI response types for convenience. +// Re-export audio and streaming types for convenience. pub use crate::openai::{ AudioTranscriptionResponse, AudioTranscriptionStream, ChatCompletionStream, TranscriptionSegment, TranscriptionWord, }; -pub use async_openai::types::chat::{ - ChatChoice, ChatChoiceStream, ChatCompletionMessageToolCall, - ChatCompletionMessageToolCallChunk, ChatCompletionMessageToolCalls, - ChatCompletionResponseMessage, ChatCompletionStreamResponseDelta, CompletionUsage, - CreateChatCompletionResponse, CreateChatCompletionStreamResponse, FinishReason, FunctionCall, - FunctionCallStream, -}; diff --git a/sdk/rust/src/openai/chat_client.rs b/sdk/rust/src/openai/chat_client.rs index 6597de82..81a0d39e 100644 --- a/sdk/rust/src/openai/chat_client.rs +++ b/sdk/rust/src/openai/chat_client.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::sync::Arc; -use async_openai::types::chat::{ +use super::chat_types::{ ChatCompletionRequestMessage, ChatCompletionTools, CreateChatCompletionResponse, CreateChatCompletionStreamResponse, }; diff --git a/sdk/rust/src/openai/chat_types.rs b/sdk/rust/src/openai/chat_types.rs new file mode 100644 index 00000000..95cec2dd --- /dev/null +++ b/sdk/rust/src/openai/chat_types.rs @@ -0,0 +1,299 @@ +//! Custom OpenAI-compatible chat completion types. +//! +//! These replace the `async-openai` dependency with lightweight, serde-driven DTOs +//! that cover the subset of the OpenAI chat completions API used by Foundry Local. + +use serde::{Deserialize, Serialize}; + +// --------------------------------------------------------------------------- +// Request message types +// --------------------------------------------------------------------------- + +/// Union type for all chat message variants. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "role", rename_all = "snake_case")] +pub enum ChatCompletionRequestMessage { + System(ChatCompletionRequestSystemMessage), + User(ChatCompletionRequestUserMessage), + Assistant(ChatCompletionRequestAssistantMessage), + Tool(ChatCompletionRequestToolMessage), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionRequestSystemMessage { + pub content: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionRequestUserMessage { + pub content: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionRequestAssistantMessage { + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_calls: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionRequestToolMessage { + pub content: String, + pub tool_call_id: String, +} + +// -- From impls for ergonomic message construction ------------------------- + +impl From<&str> for ChatCompletionRequestSystemMessage { + fn from(s: &str) -> Self { + Self { + content: s.to_owned(), + name: None, + } + } +} + +impl From for ChatCompletionRequestSystemMessage { + fn from(s: String) -> Self { + Self { + content: s, + name: None, + } + } +} + +impl From<&str> for ChatCompletionRequestUserMessage { + fn from(s: &str) -> Self { + Self { + content: s.to_owned(), + name: None, + } + } +} + +impl From for ChatCompletionRequestUserMessage { + fn from(s: String) -> Self { + Self { + content: s, + name: None, + } + } +} + +impl From for ChatCompletionRequestMessage { + fn from(m: ChatCompletionRequestSystemMessage) -> Self { + Self::System(m) + } +} + +impl From for ChatCompletionRequestMessage { + fn from(m: ChatCompletionRequestUserMessage) -> Self { + Self::User(m) + } +} + +impl From for ChatCompletionRequestMessage { + fn from(m: ChatCompletionRequestAssistantMessage) -> Self { + Self::Assistant(m) + } +} + +impl From for ChatCompletionRequestMessage { + fn from(m: ChatCompletionRequestToolMessage) -> Self { + Self::Tool(m) + } +} + +// --------------------------------------------------------------------------- +// Tool definition types +// --------------------------------------------------------------------------- + +/// Tool definition for function calling. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionTools { + pub r#type: String, + pub function: FunctionObject, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionObject { + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub parameters: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub strict: Option, +} + +// --------------------------------------------------------------------------- +// Tool choice types +// --------------------------------------------------------------------------- + +/// Tool choice configuration. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ChatCompletionToolChoiceOption { + Simple(String), + Named(ChatCompletionNamedToolChoice), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionNamedToolChoice { + pub r#type: String, + pub function: ChatCompletionNamedToolChoiceFunction, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionNamedToolChoiceFunction { + pub name: String, +} + +// --------------------------------------------------------------------------- +// Non-streaming response types +// --------------------------------------------------------------------------- + +/// Non-streaming chat completion response. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CreateChatCompletionResponse { + pub id: String, + pub object: String, + pub created: u64, + pub model: String, + pub choices: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub usage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub system_fingerprint: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatChoice { + pub index: u32, + pub message: ChatCompletionResponseMessage, + #[serde(skip_serializing_if = "Option::is_none")] + pub finish_reason: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub logprobs: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionResponseMessage { + pub role: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_calls: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CompletionUsage { + pub prompt_tokens: u32, + pub completion_tokens: u32, + pub total_tokens: u32, +} + +// --------------------------------------------------------------------------- +// Streaming response types +// --------------------------------------------------------------------------- + +/// Streaming chat completion chunk. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CreateChatCompletionStreamResponse { + pub id: String, + pub object: String, + pub created: u64, + pub model: String, + pub choices: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub usage: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub system_fingerprint: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatChoiceStream { + pub index: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub delta: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub finish_reason: Option, + // Foundry core sometimes returns "message" instead of "delta" in streaming. + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionStreamResponseDelta { + #[serde(skip_serializing_if = "Option::is_none")] + pub role: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_calls: Option>, +} + +// --------------------------------------------------------------------------- +// Tool call types (shared between request and response) +// --------------------------------------------------------------------------- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionMessageToolCall { + pub id: String, + pub r#type: String, + pub function: FunctionCall, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatCompletionMessageToolCallChunk { + pub index: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub function: Option, +} + +/// Discriminated union for tool call types. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ChatCompletionMessageToolCalls { + Function(FunctionCall), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionCall { + pub name: String, + pub arguments: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionCallStream { + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub arguments: Option, +} + +// --------------------------------------------------------------------------- +// FinishReason +// --------------------------------------------------------------------------- + +/// Reason the model stopped generating. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum FinishReason { + Stop, + Length, + ToolCalls, + ContentFilter, + FunctionCall, +} diff --git a/sdk/rust/src/openai/mod.rs b/sdk/rust/src/openai/mod.rs index c3d4a645..42d3a5e0 100644 --- a/sdk/rust/src/openai/mod.rs +++ b/sdk/rust/src/openai/mod.rs @@ -1,3 +1,4 @@ +pub mod chat_types; mod audio_client; mod chat_client; mod json_stream; @@ -7,4 +8,5 @@ pub use self::audio_client::{ TranscriptionSegment, TranscriptionWord, }; pub use self::chat_client::{ChatClient, ChatClientSettings, ChatCompletionStream}; +pub use self::chat_types::*; pub use self::json_stream::JsonStream;