diff --git a/OpenAI.SDK/Betalgo.Ranul.OpenAI.csproj b/OpenAI.SDK/Betalgo.Ranul.OpenAI.csproj index 57d627f8..3fda3adf 100644 --- a/OpenAI.SDK/Betalgo.Ranul.OpenAI.csproj +++ b/OpenAI.SDK/Betalgo.Ranul.OpenAI.csproj @@ -10,7 +10,7 @@ Betalgo-Ranul-OpenAI-icon.png true OpenAI SDK by Betalgo - 8.9.0 + 8.10.0 Tolga Kayhan, Betalgo Betalgo Up Ltd. OpenAI .NET library by Betalgo Ranul @@ -74,4 +74,8 @@ + + + + \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIChatClient.cs b/OpenAI.SDK/Managers/OpenAIChatClient.cs new file mode 100644 index 00000000..57ba3117 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIChatClient.cs @@ -0,0 +1,390 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; +using Betalgo.Ranul.OpenAI.ObjectModels; +using Betalgo.Ranul.OpenAI.ObjectModels.RequestModels; +using Betalgo.Ranul.OpenAI.ObjectModels.ResponseModels; +using Betalgo.Ranul.OpenAI.ObjectModels.SharedModels; +using Microsoft.Extensions.AI; +using ChatMessage = Microsoft.Extensions.AI.ChatMessage; + +namespace Betalgo.Ranul.OpenAI.Managers; + +public partial class OpenAIService : IChatClient +{ + private ChatClientMetadata? _chatMetadata; + + /// + ChatClientMetadata IChatClient.Metadata => _chatMetadata ??= new(nameof(OpenAIService), _httpClient.BaseAddress, _defaultModelId); + + /// + TService? IChatClient.GetService(object? key) where TService : class + { + return this as TService; + } + + /// + void IDisposable.Dispose() + { + } + + /// + async Task IChatClient.CompleteAsync(IList chatMessages, ChatOptions? options, CancellationToken cancellationToken) + { + var request = CreateRequest(chatMessages, options); + + var response = await ChatCompletion.CreateCompletion(request, options?.ModelId, cancellationToken); + ThrowIfNotSuccessful(response); + + string? finishReason = null; + List responseMessages = []; + foreach (var choice in response.Choices) + { + finishReason ??= choice.FinishReason; + + ChatMessage m = new() + { + Role = new(choice.Message.Role), + AuthorName = choice.Message.Name, + RawRepresentation = choice + }; + + PopulateContents(choice.Message, m.Contents); + + if (response.ServiceTier is string serviceTier) + { + (m.AdditionalProperties ??= [])[nameof(response.ServiceTier)] = serviceTier; + } + + if (response.SystemFingerPrint is string fingerprint) + { + (m.AdditionalProperties ??= [])[nameof(response.SystemFingerPrint)] = fingerprint; + } + + responseMessages.Add(m); + } + + return new(responseMessages) + { + CreatedAt = response.CreatedAt, + CompletionId = response.Id, + FinishReason = finishReason is not null ? new(finishReason) : null, + ModelId = response.Model, + RawRepresentation = response, + Usage = response.Usage is { } usage ? GetUsageDetails(usage) : null + }; + } + + /// + async IAsyncEnumerable IChatClient.CompleteStreamingAsync(IList chatMessages, ChatOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var request = CreateRequest(chatMessages, options); + + await foreach (var response in ChatCompletion.CreateCompletionAsStream(request, options?.ModelId, cancellationToken: cancellationToken)) + { + ThrowIfNotSuccessful(response); + + foreach (var choice in response.Choices) + { + StreamingChatCompletionUpdate update = new() + { + AuthorName = choice.Delta.Name, + CompletionId = response.Id, + CreatedAt = response.CreatedAt, + FinishReason = choice.FinishReason is not null ? new(choice.FinishReason) : null, + ModelId = response.Model, + RawRepresentation = response, + Role = choice.Delta.Role is not null ? new(choice.Delta.Role) : null + }; + + if (choice.Index is not null) + { + update.ChoiceIndex = choice.Index.Value; + } + + if (response.ServiceTier is string serviceTier) + { + (update.AdditionalProperties ??= [])[nameof(response.ServiceTier)] = serviceTier; + } + + if (response.SystemFingerPrint is string fingerprint) + { + (update.AdditionalProperties ??= [])[nameof(response.SystemFingerPrint)] = fingerprint; + } + + PopulateContents(choice.Delta, update.Contents); + + yield return update; + + if (response.Usage is { } usage) + { + yield return new() + { + AuthorName = choice.Delta.Name, + CompletionId = response.Id, + Contents = [new UsageContent(GetUsageDetails(usage))], + CreatedAt = response.CreatedAt, + FinishReason = choice.FinishReason is not null ? new(choice.FinishReason) : null, + ModelId = response.Model, + Role = choice.Delta.Role is not null ? new(choice.Delta.Role) : null + }; + } + } + } + } + + private static void ThrowIfNotSuccessful(ChatCompletionCreateResponse response) + { + if (!response.Successful) + { + throw new InvalidOperationException(response.Error is { } error ? $"{response.Error.Code}: {response.Error.Message}" : "Betalgo.Ranul Unknown error"); + } + } + + private ChatCompletionCreateRequest CreateRequest(IList chatMessages, ChatOptions? options) + { + ChatCompletionCreateRequest request = new() + { + Model = options?.ModelId ?? _defaultModelId + }; + + if (options is not null) + { + // Strongly-typed properties from options + request.MaxCompletionTokens = options.MaxOutputTokens; + request.Temperature = options.Temperature; + request.TopP = options.TopP; + request.FrequencyPenalty = options.FrequencyPenalty; + request.PresencePenalty = options.PresencePenalty; + request.StopAsList = options.StopSequences; + + // Non-strongly-typed properties from additional properties + request.LogitBias = options.AdditionalProperties?.TryGetValue(nameof(request.LogitBias), out var logitBias) is true ? logitBias : null; + request.LogProbs = options.AdditionalProperties?.TryGetValue(nameof(request.LogProbs), out bool logProbs) is true ? logProbs : null; + request.N = options.AdditionalProperties?.TryGetValue(nameof(request.N), out int n) is true ? n : null; + request.ParallelToolCalls = options.AdditionalProperties?.TryGetValue(nameof(request.ParallelToolCalls), out bool parallelToolCalls) is true ? parallelToolCalls : null; + request.Seed = options.AdditionalProperties?.TryGetValue(nameof(request.Seed), out int seed) is true ? seed : null; + request.ServiceTier = options.AdditionalProperties?.TryGetValue(nameof(request.ServiceTier), out string? serviceTier) is true ? serviceTier : null!; + request.User = options.AdditionalProperties?.TryGetValue(nameof(request.User), out string? user) is true ? user : null!; + request.TopLogprobs = options.AdditionalProperties?.TryGetValue(nameof(request.TopLogprobs), out int topLogprobs) is true ? topLogprobs : null; + + // Response format + switch (options.ResponseFormat) + { + case ChatResponseFormatText: + request.ResponseFormat = new() { Type = StaticValues.CompletionStatics.ResponseFormat.Text }; + break; + + case ChatResponseFormatJson { Schema: not null } json: + request.ResponseFormat = new() + { + Type = StaticValues.CompletionStatics.ResponseFormat.JsonSchema, + JsonSchema = new() + { + Name = json.SchemaName ?? "JsonSchema", + Schema = JsonSerializer.Deserialize(json.Schema), + Description = json.SchemaDescription + } + }; + break; + + case ChatResponseFormatJson: + request.ResponseFormat = new() { Type = StaticValues.CompletionStatics.ResponseFormat.Json }; + break; + } + + // Tools + request.Tools = options.Tools + ?.OfType() + .Select(f => + { + return ToolDefinition.DefineFunction(new() + { + Name = f.Metadata.Name, + Description = f.Metadata.Description, + Parameters = CreateParameters(f) + }); + }) + .ToList() is { Count: > 0 } tools + ? tools + : null; + if (request.Tools is not null) + { + request.ToolChoice = options.ToolMode is RequiredChatToolMode r ? new() + { + Type = StaticValues.CompletionStatics.ToolChoiceType.Required, + Function = r.RequiredFunctionName is null ? null : new ToolChoice.FunctionTool() { Name = r.RequiredFunctionName } + } : + options.ToolMode is AutoChatToolMode ? new() { Type = StaticValues.CompletionStatics.ToolChoiceType.Auto } : new ToolChoice() { Type = StaticValues.CompletionStatics.ToolChoiceType.None }; + } + } + + // Messages + request.Messages = []; + foreach (var message in chatMessages) + { + foreach (var content in message.Contents) + { + switch (content) + { + case TextContent tc: + request.Messages.Add(new() + { + Content = tc.Text, + Name = message.AuthorName, + Role = message.Role.ToString() + }); + break; + + case ImageContent ic: + request.Messages.Add(new() + { + Contents = + [ + new() + { + Type = "image_url", + ImageUrl = new() + { + Url = ic.Uri, + Detail = ic.AdditionalProperties?.TryGetValue(nameof(MessageImageUrl.Detail), out string? detail) is true ? detail : null + } + } + ], + Name = message.AuthorName, + Role = message.Role.ToString() + }); + break; + + case FunctionResultContent frc: + request.Messages.Add(new() + { + ToolCallId = frc.CallId, + Content = frc.Result?.ToString(), + Name = message.AuthorName, + Role = message.Role.ToString() + }); + break; + } + } + + var functionCallContents = message.Contents.OfType().ToArray(); + if (functionCallContents.Length > 0) + { + request.Messages.Add(new() + { + Name = message.AuthorName, + Role = message.Role.ToString(), + ToolCalls = functionCallContents.Select(fcc => new ToolCall() + { + Type = "function", + Id = fcc.CallId, + FunctionCall = new() + { + Name = fcc.Name, + Arguments = JsonSerializer.Serialize(fcc.Arguments) + } + }) + .ToList() + }); + } + } + + return request; + } + + private static PropertyDefinition CreateParameters(AIFunction f) + { + List required = []; + Dictionary properties = []; + + var parameters = f.Metadata.Parameters; + + foreach (var parameter in parameters) + { + properties.Add(parameter.Name, parameter.Schema is JsonElement e ? e.Deserialize()! : PropertyDefinition.DefineObject(null, null, null, null, null)); + + if (parameter.IsRequired) + { + required.Add(parameter.Name); + } + } + + return PropertyDefinition.DefineObject(properties, required, null, null, null); + } + + private static void PopulateContents(ObjectModels.RequestModels.ChatMessage source, IList destination) + { + if (source.Content is not null) + { + destination.Add(new TextContent(source.Content)); + } + + if (source.Contents is { } contents) + { + foreach (var content in contents) + { + if (content.Text is string text) + { + destination.Add(new TextContent(text)); + } + + if (content.ImageUrl is { } url) + { + destination.Add(new ImageContent(url.Url)); + } + } + } + + if (source.ToolCalls is { } toolCalls) + { + foreach (var tc in toolCalls) + { + destination.Add(new FunctionCallContent(tc.Id ?? string.Empty, tc.FunctionCall?.Name ?? string.Empty, tc.FunctionCall?.Arguments is string a ? JsonSerializer.Deserialize>(a) : null)); + } + } + } + + private static UsageDetails GetUsageDetails(UsageResponse usage) + { + var details = new UsageDetails() + { + InputTokenCount = usage.PromptTokens, + OutputTokenCount = usage.CompletionTokens, + TotalTokenCount = usage.TotalTokens + }; + + if (usage.PromptTokensDetails is { } promptDetails) + { + Dictionary d = new(StringComparer.OrdinalIgnoreCase); + (details.AdditionalProperties ??= [])[nameof(usage.PromptTokensDetails)] = d; + + if (promptDetails.CachedTokens is int cachedTokens) + { + d[nameof(promptDetails.CachedTokens)] = cachedTokens; + } + + if (promptDetails.AudioTokens is int audioTokens) + { + d[nameof(promptDetails.AudioTokens)] = audioTokens; + } + } + + if (usage.CompletionTokensDetails is { } completionDetails) + { + Dictionary d = new(StringComparer.OrdinalIgnoreCase); + (details.AdditionalProperties ??= [])[nameof(usage.CompletionTokensDetails)] = d; + + if (completionDetails.ReasoningTokens is int reasoningTokens) + { + d[nameof(completionDetails.ReasoningTokens)] = reasoningTokens; + } + + if (completionDetails.AudioTokens is int audioTokens) + { + d[nameof(promptDetails.AudioTokens)] = audioTokens; + } + } + + return details; + } +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIEmbeddingGenerator.cs b/OpenAI.SDK/Managers/OpenAIEmbeddingGenerator.cs new file mode 100644 index 00000000..4b7e5717 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIEmbeddingGenerator.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.AI; + +namespace Betalgo.Ranul.OpenAI.Managers; + +public partial class OpenAIService : IEmbeddingGenerator> +{ + private EmbeddingGeneratorMetadata? _embeddingMetadata; + + EmbeddingGeneratorMetadata IEmbeddingGenerator>.Metadata => + _embeddingMetadata ??= new(nameof(OpenAIService), _httpClient.BaseAddress, _defaultModelId); + + TService? IEmbeddingGenerator>.GetService(object? key) where TService : class => + this as TService; + + async Task>> IEmbeddingGenerator>.GenerateAsync(IEnumerable values, EmbeddingGenerationOptions? options, CancellationToken cancellationToken) + { + var response = await this.Embeddings.CreateEmbedding(new() + { + Model = options?.ModelId ?? _defaultModelId, + Dimensions = options?.Dimensions, + InputAsList = values.ToList(), + }, cancellationToken); + + if (!response.Successful) + { + throw new InvalidOperationException(response.Error is { } error ? + $"{response.Error.Code}: {response.Error.Message}" : + "Betalgo.Ranul Unknown error"); + } + + return new(response.Data.Select(e => new Embedding(e.Embedding.Select(d => (float)d).ToArray()) { ModelId = response.Model })) + { + Usage = response.Usage is { } usage ? new() + { + InputTokenCount = usage.PromptTokens, + OutputTokenCount = usage.CompletionTokens, + TotalTokenCount = usage.TotalTokens, + } : null, + }; + } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/ChatCompletionCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/ChatCompletionCreateRequest.cs index 4763a194..af60d842 100644 --- a/OpenAI.SDK/ObjectModels/RequestModels/ChatCompletionCreateRequest.cs +++ b/OpenAI.SDK/ObjectModels/RequestModels/ChatCompletionCreateRequest.cs @@ -308,4 +308,17 @@ public IEnumerable Validate() /// [JsonPropertyName("service_tier")] public string? ServiceTier { get; set; } -} \ No newline at end of file + + + /// + /// Whether or not to store the output of this chat completion request for use in our model distillation or evals products. + /// https://platform.openai.com/docs/api-reference/chat/create?lang=python#chat-create-store + /// + /// + /// more about distillation: https://platform.openai.com/docs/guides/distillation + /// + /// more about evals: https://platform.openai.com/docs/guides/evals + /// + [JsonPropertyName("store")] + public bool? Store { get; set; } = false; +} diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/BatchResponseModel/BatchResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/BatchResponseModel/BatchResponse.cs index 8d2512e3..714fb9ff 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/BatchResponseModel/BatchResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/BatchResponseModel/BatchResponse.cs @@ -53,7 +53,12 @@ public record BatchResponse : BaseResponse, IOpenAIModels.IMetaData /// The Unix timestamp (in seconds) for when the batch was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + /// + /// DateTimeOffset for when the batch was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The Unix timestamp (in seconds) for when the batch started processing. diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/ChatCompletionCreateResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/ChatCompletionCreateResponse.cs index c4207770..8bd01d3a 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/ChatCompletionCreateResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/ChatCompletionCreateResponse.cs @@ -38,11 +38,10 @@ public record ChatCompletionCreateResponse : BaseResponse, IOpenAIModels.IId, IO [JsonPropertyName("service_tier")] public string? ServiceTier { get; set; } - /// - /// The Unix timestamp (in seconds) of when the chat completion was created. - /// [JsonPropertyName("created")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// A unique identifier for the chat completion. diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/CompletionCreateResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/CompletionCreateResponse.cs index c4165a1a..05a94c83 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/CompletionCreateResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/CompletionCreateResponse.cs @@ -15,7 +15,9 @@ public record CompletionCreateResponse : BaseResponse, IOpenAIModels.IId, IOpenA public UsageResponse Usage { get; set; } [JsonPropertyName("created")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); [JsonPropertyName("id")] public string Id { get; set; } diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/EditCreateResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/EditCreateResponse.cs index 8e933fca..2064d42a 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/EditCreateResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/EditCreateResponse.cs @@ -15,5 +15,7 @@ public record EditCreateResponse : BaseResponse, IOpenAIModels.ICreatedAt public UsageResponse Usage { get; set; } [JsonPropertyName("created")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); } \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FileResponseModels/FileUploadResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FileResponseModels/FileUploadResponse.cs index 652c0615..585be3f3 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/FileResponseModels/FileUploadResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/FileResponseModels/FileUploadResponse.cs @@ -18,5 +18,7 @@ public record FileUploadResponse : BaseResponse, IOpenAIModels.ICreatedAt public string Purpose { get; set; } [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); } \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuneResponseModels/FineTuneResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuneResponseModels/FineTuneResponse.cs index f75e5305..8f3eca59 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuneResponseModels/FineTuneResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuneResponseModels/FineTuneResponse.cs @@ -33,7 +33,9 @@ public record FineTuneResponse : BaseResponse, IOpenAIModels.IId, IOpenAIModels. public int? UpdatedAt { get; set; } [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); [JsonPropertyName("id")] public string Id { get; set; } diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs index c7aa4c78..b3f940a8 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/FineTuningJobResponseModels/FineTuningJobResponse.cs @@ -67,7 +67,12 @@ public record FineTuningJobResponse : BaseResponse, IOpenAIModels.IId, IOpenAIMo /// The Unix timestamp (in seconds) for when the fine-tuning job was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + /// + /// for when the fine-tuning job was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The object identifier, which can be referenced in the API endpoints. diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/ImageResponseModel/ImageCreateResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/ImageResponseModel/ImageCreateResponse.cs index db02d838..4542c5b3 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/ImageResponseModel/ImageCreateResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/ImageResponseModel/ImageCreateResponse.cs @@ -9,7 +9,8 @@ public record ImageCreateResponse : BaseResponse, IOpenAIModels.ICreatedAt public List Results { get; set; } [JsonPropertyName("created")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); public record ImageDataResult { diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/RunStepListResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/RunStepListResponse.cs index 41be6e64..5b9946a4 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/RunStepListResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/RunStepListResponse.cs @@ -93,7 +93,9 @@ public record RunStepResponse : BaseResponse, IOpenAIModels.IId, IOpenAIModels.I /// The Unix timestamp (in seconds) for when the run step was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The identifier of the run step, which can be referenced in API endpoints. diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileBatchObject.cs b/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileBatchObject.cs index 60229ad4..e3643682 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileBatchObject.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileBatchObject.cs @@ -15,7 +15,11 @@ public record VectorStoreFileBatchObject : BaseResponse /// The Unix timestamp (in seconds) for when the vector store files batch was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + /// + /// for when the vector store files batch was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The ID of the [vector store](/docs/api-reference/vector-stores/object) that the [File](/docs/api-reference/files) diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileObject.cs b/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileObject.cs index d26dcb60..e0d77be1 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileObject.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreFileObject.cs @@ -26,7 +26,12 @@ public record VectorStoreFileObject : BaseResponse /// The Unix timestamp (in seconds) for when the vector store file was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + /// + /// for when the vector store file was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The ID of the [vector store](/docs/api-reference/vector-stores/object) that the [File](/docs/api-reference/files) diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreObjectResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreObjectResponse.cs index b56b9ed6..46e79bc3 100644 --- a/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreObjectResponse.cs +++ b/OpenAI.SDK/ObjectModels/ResponseModels/VectorStoreResponseModels/VectorStoreObjectResponse.cs @@ -15,7 +15,12 @@ public record VectorStoreObjectResponse : BaseResponse /// The Unix timestamp (in seconds) for when the vector store was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + /// + /// for when the vector store was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The name of the vector store. diff --git a/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs index 98ad7930..9fe7166c 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/AssistantFileResponse.cs @@ -15,7 +15,9 @@ public record AssistantFileResponse : BaseResponse, IOpenAIModels.IId, IOpenAIMo /// The Unix timestamp (in seconds) for when the assistant file was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The identifier, which can be referenced in API endpoints. diff --git a/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs index 82d2f3eb..268e4947 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/AssistantResponse.cs @@ -58,11 +58,29 @@ public record AssistantResponse : BaseResponse, IOpenAIModels.IId, IOpenAIModels [JsonPropertyName("response_format")] public ResponseFormatOneOfType ResponseFormatOneOfType { get; set; } + /// + /// What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while + /// lower values like 0.2 will make it more focused and deterministic. + /// + [JsonPropertyName("temperature")] + public float? Temperature { get; set; } + + /// + /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the + /// tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are + /// considered. + /// We generally recommend altering this or temperature but not both. + /// + [JsonPropertyName("top_p")] + public double? TopP { get; set; } + /// /// The Unix timestamp (in seconds) for when the assistant was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The identifier, which can be referenced in API endpoints. diff --git a/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs index ae0d6766..35b6f06f 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/EventResponse.cs @@ -2,7 +2,7 @@ namespace Betalgo.Ranul.OpenAI.ObjectModels.SharedModels; -public record EventResponse +public record EventResponse:IOpenAIModels.ICreatedAt { [JsonPropertyName("object")] public string? ObjectTypeName { get; set; } @@ -11,7 +11,9 @@ public record EventResponse public string? Id { get; set; } [JsonPropertyName("created_at")] - public int? CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); + [JsonPropertyName("level")] public string Level { get; set; } diff --git a/OpenAI.SDK/ObjectModels/SharedModels/FileResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/FileResponse.cs index 3c8f9ccb..97f90049 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/FileResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/FileResponse.cs @@ -20,7 +20,9 @@ public record FileResponse : BaseResponse, IOpenAIModels.IId, IOpenAIModels.ICre public string Status { get; set; } [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); [JsonPropertyName("id")] public string Id { get; set; } diff --git a/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs b/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs index ad402598..10aaba7c 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/IOpenAiModels.cs @@ -36,7 +36,8 @@ public interface IAssistantId public interface ICreatedAt { - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + public DateTimeOffset CreatedAt { get; } } public interface ICompletedAt diff --git a/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs index 1a6ac767..1d2c5cc5 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/MessageResponse.cs @@ -14,6 +14,7 @@ public MessageResponse Delta { set => Content = value.Content; } + /// /// The thread ID that this message belongs to. /// @@ -82,7 +83,11 @@ public MessageResponse Delta /// The Unix timestamp (in seconds) for when the message was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + /// + /// for when the message was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The identifier, which can be referenced in API endpoints. diff --git a/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs index 93205b0f..19568ed5 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/RunResponse.cs @@ -143,7 +143,12 @@ public record RunResponse : BaseResponse, IOpenAIModels.IId, IOpenAIModels.IMode /// The Unix timestamp (in seconds) for when the run was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + /// + /// for when the run was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The list of File IDs the assistant used for this run. diff --git a/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs b/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs index cb97217d..c8c7e209 100644 --- a/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs +++ b/OpenAI.SDK/ObjectModels/SharedModels/ThreadResponse.cs @@ -18,7 +18,12 @@ public record ThreadResponse : BaseResponse, IOpenAIModels.IId, IOpenAIModels.IC /// The Unix timestamp (in seconds) for when the assistant was created. /// [JsonPropertyName("created_at")] - public int CreatedAt { get; set; } + public long CreatedAtUnix { get; set; } + + /// + /// for when the assistant was created. + /// + public DateTimeOffset CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnix); /// /// The identifier, which can be referenced in API endpoints. diff --git a/OpenAI.UtilitiesPlayground/OpenAI.UtilitiesPlayground.csproj b/OpenAI.UtilitiesPlayground/OpenAI.UtilitiesPlayground.csproj index 9d21e5ac..488bebec 100644 --- a/OpenAI.UtilitiesPlayground/OpenAI.UtilitiesPlayground.csproj +++ b/OpenAI.UtilitiesPlayground/OpenAI.UtilitiesPlayground.csproj @@ -18,6 +18,7 @@ + diff --git a/Readme.md b/Readme.md index 7767efa6..1d9e8102 100644 --- a/Readme.md +++ b/Readme.md @@ -117,6 +117,14 @@ Due to time constraints, not all methods have been thoroughly tested or fully do Needless to say, I cannot accept responsibility for any damage caused by using the library. ## Changelog +### 8.10.0 +- Added support for `Microsoft.Extensions.AI` `IChatClient` and `IEmbeddingGenerator` (more information will be coming soon to the Wiki). +- Added missing `Temperature` and `TopP` parameters to `AssistantResponse`. +- Added missing `Store` parameter to `ChatCompletionCreateRequest`. + +- Breaking Changes: + - ⚠️ `CreatedAt` parameter renamed to `CreatedAtUnix` and converted to `long` instead of `int`. Added `CreatedAt` parameter as `DateTimeOffset` type, which will automatically convert Unix time to `DateTime`. + ### 8.9.0 - Realtime API implementation is completed. As usual this is the first version and it may contain bugs. Please report any issues you encounter. - [Realtime Sample](https://github.com/betalgo/openai/wiki/realtime)