diff --git a/sample/Cnblogs.DashScope.Sample/Cnblogs.DashScope.Sample.csproj b/sample/Cnblogs.DashScope.Sample/Cnblogs.DashScope.Sample.csproj index a5cb962..a7ba2f0 100644 --- a/sample/Cnblogs.DashScope.Sample/Cnblogs.DashScope.Sample.csproj +++ b/sample/Cnblogs.DashScope.Sample/Cnblogs.DashScope.Sample.csproj @@ -20,7 +20,7 @@ - + diff --git a/sample/Cnblogs.DashScope.Sample/Program.cs b/sample/Cnblogs.DashScope.Sample/Program.cs index 8245f17..c5958f3 100644 --- a/sample/Cnblogs.DashScope.Sample/Program.cs +++ b/sample/Cnblogs.DashScope.Sample/Program.cs @@ -211,7 +211,7 @@ async Task ChatWithMicrosoftExtensions() new(ChatRole.System, "You are a helpful AI assistant"), new(ChatRole.User, "What is AI?") ]; - var response = await chatClient.CompleteAsync(conversation); + var response = await chatClient.GetResponseAsync(conversation); var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true }; Console.WriteLine(JsonSerializer.Serialize(response, serializerOptions)); } diff --git a/sample/Cnblogs.DashScope.Sample/ToolCallWithExtensions.cs b/sample/Cnblogs.DashScope.Sample/ToolCallWithExtensions.cs index f170c7f..0c30cd4 100644 --- a/sample/Cnblogs.DashScope.Sample/ToolCallWithExtensions.cs +++ b/sample/Cnblogs.DashScope.Sample/ToolCallWithExtensions.cs @@ -10,12 +10,12 @@ public static class ToolCallWithExtensions public static async Task ToolCallWithExtensionAsync(this IDashScopeClient dashScopeClient) { [Description("Gets the weather")] - string GetWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining"; + string GetWeather(string location) => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining"; var chatOptions = new ChatOptions { Tools = [AIFunctionFactory.Create(GetWeather)] }; var client = dashScopeClient.AsChatClient("qwen-max").AsBuilder().UseFunctionInvocation().Build(); - await foreach (var message in client.CompleteStreamingAsync("What is weather today?", chatOptions)) + await foreach (var message in client.GetStreamingResponseAsync("What is weather of LA today?", chatOptions)) { Console.WriteLine(JsonSerializer.Serialize(message)); } diff --git a/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj b/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj index 8a22a11..fe758a6 100644 --- a/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj +++ b/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Cnblogs.DashScope.AI/DashScopeChatClient.cs b/src/Cnblogs.DashScope.AI/DashScopeChatClient.cs index b1c69f2..8b0012f 100644 --- a/src/Cnblogs.DashScope.AI/DashScopeChatClient.cs +++ b/src/Cnblogs.DashScope.AI/DashScopeChatClient.cs @@ -34,7 +34,6 @@ public DashScopeChatClient(IDashScopeClient dashScopeClient, string modelId) _dashScopeClient = dashScopeClient; _modelId = modelId; - Metadata = new ChatClientMetadata("dashscope", _dashScopeClient.BaseAddress, _modelId); } /// @@ -43,7 +42,7 @@ public DashScopeChatClient(IDashScopeClient dashScopeClient, string modelId) public JsonSerializerOptions ToolCallJsonSerializerOptions { get; set; } = new(JsonSerializerDefaults.Web); /// - public async Task CompleteAsync( + public async Task GetResponseAsync( IList chatMessages, ChatOptions? options = null, CancellationToken cancellationToken = default) @@ -52,7 +51,6 @@ public async Task CompleteAsync( var useVlRaw = options?.AdditionalProperties?.GetValueOrDefault("useVl")?.ToString(); var useVl = string.IsNullOrEmpty(useVlRaw) ? modelId.Contains("qwen-vl", StringComparison.OrdinalIgnoreCase) - || chatMessages.Any(c => c.Contents.Any(m => m is ImageContent)) : string.Equals(useVlRaw, "true", StringComparison.OrdinalIgnoreCase); if (useVl) { @@ -71,10 +69,10 @@ public async Task CompleteAsync( }; returnMessage.Contents.Add(new TextContent(response.Output.Choices[0].Message.Content[0].Text)); - var completion = new ChatCompletion(returnMessage) + var completion = new ChatResponse(returnMessage) { RawRepresentation = response, - CompletionId = response.RequestId, + ResponseId = response.RequestId, CreatedAt = DateTimeOffset.Now, ModelId = modelId, FinishReason = ToFinishReason(response.Output.Choices[0].FinishReason), @@ -107,10 +105,10 @@ public async Task CompleteAsync( }, cancellationToken); var returnMessage = ToChatMessage(response.Output.Choices![0].Message); - var completion = new ChatCompletion(returnMessage) + var completion = new ChatResponse(returnMessage) { RawRepresentation = response, - CompletionId = response.RequestId, + ResponseId = response.RequestId, CreatedAt = DateTimeOffset.Now, ModelId = modelId, FinishReason = ToFinishReason(response.Output.Choices[0].FinishReason), @@ -131,15 +129,14 @@ public async Task CompleteAsync( } /// - public async IAsyncEnumerable CompleteStreamingAsync( + public async IAsyncEnumerable GetStreamingResponseAsync( IList chatMessages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var useVlRaw = options?.AdditionalProperties?.GetValueOrDefault("useVl")?.ToString(); - var useVl = string.IsNullOrEmpty(useVlRaw) - ? chatMessages.Any(c => c.Contents.Any(m => m is ImageContent)) - : string.Equals(useVlRaw, "true", StringComparison.OrdinalIgnoreCase); + var useVl = string.Equals(useVlRaw, "true", StringComparison.OrdinalIgnoreCase) + || (options?.ModelId?.Contains("qwen-vl") ?? false); var modelId = options?.ModelId ?? _modelId; ChatRole? streamedRole = null; @@ -167,9 +164,9 @@ public async IAsyncEnumerable CompleteStreamingAs : ToFinishReason(response.Output.Choices[0].FinishReason); completionId ??= response.RequestId; - var update = new StreamingChatCompletionUpdate() + var update = new ChatResponseUpdate() { - CompletionId = completionId, + ResponseId = completionId, CreatedAt = DateTimeOffset.Now, FinishReason = finishReason, ModelId = modelId, @@ -201,10 +198,10 @@ public async IAsyncEnumerable CompleteStreamingAs if (options?.Tools is { Count: > 0 }) { // qwen does not support streaming with function call, fallback to non-streaming - var completion = await CompleteAsync(chatMessages, options, cancellationToken); - yield return new StreamingChatCompletionUpdate() + var completion = await GetResponseAsync(chatMessages, options, cancellationToken); + yield return new ChatResponseUpdate() { - CompletionId = completion.CompletionId, + ResponseId = completion.ResponseId, Role = completion.Message.Role, AdditionalProperties = completion.AdditionalProperties, Contents = completion.Message.Contents, @@ -241,9 +238,9 @@ public async IAsyncEnumerable CompleteStreamingAs : ToFinishReason(response.Output.Choices[0].FinishReason); completionId ??= response.RequestId; - var update = new StreamingChatCompletionUpdate() + var update = new ChatResponseUpdate() { - CompletionId = completionId, + ResponseId = completionId, CreatedAt = DateTimeOffset.Now, FinishReason = finishReason, ModelId = modelId, @@ -289,9 +286,6 @@ public void Dispose() // nothing to dispose. } - /// - public ChatClientMetadata Metadata { get; } - private static ChatFinishReason? ToFinishReason(string? finishReason) => string.IsNullOrEmpty(finishReason) ? null @@ -398,10 +392,12 @@ private List ToMultimodalMessageContents(IList MultimodalMessageContent.TextContent(text.Text), - ImageContent { Data.Length: > 0 } image => MultimodalMessageContent.ImageContent( - image.Data.Value.Span, - image.MediaType ?? throw new InvalidOperationException("image media type should not be null")), - ImageContent { Uri: { } uri } => MultimodalMessageContent.ImageContent(uri), + DataContent { Data.Length: > 0 } data when data.MediaTypeStartsWith("image") => + MultimodalMessageContent.ImageContent( + data.Data.Value.Span, + data.MediaType ?? throw new InvalidOperationException("image media type should not be null")), + DataContent { Uri: { } uri } data when data.MediaTypeStartsWith("image") => + MultimodalMessageContent.ImageContent(uri), _ => null }; if (content is not null) @@ -513,15 +509,13 @@ RequiredChatToolMode required when string.IsNullOrEmpty(required.RequiredFunctio f => new ToolDefinition( "function", new FunctionDefinition( - f.Metadata.Name, - f.Metadata.Description, - GetParameterSchema(f.Metadata.Parameters)))); + f.Name, + f.Description, + GetParameterSchema(f.JsonSchema)))); } - private static JsonSchema GetParameterSchema(IEnumerable metadata) + private static JsonSchema GetParameterSchema(JsonElement metadata) { - return new JsonSchemaBuilder() - .Properties(metadata.Select(c => (c.Name, Schema: c.Schema as JsonSchema ?? EmptyObjectSchema)).ToArray()) - .Build(); + return metadata.Deserialize() ?? EmptyObjectSchema; } } diff --git a/src/Cnblogs.DashScope.Core/Cnblogs.DashScope.Core.csproj b/src/Cnblogs.DashScope.Core/Cnblogs.DashScope.Core.csproj index a9262bd..791029d 100644 --- a/src/Cnblogs.DashScope.Core/Cnblogs.DashScope.Core.csproj +++ b/src/Cnblogs.DashScope.Core/Cnblogs.DashScope.Core.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Cnblogs.DashScope.Sdk/Cnblogs.DashScope.Sdk.csproj b/src/Cnblogs.DashScope.Sdk/Cnblogs.DashScope.Sdk.csproj index 2d88884..3480ef8 100644 --- a/src/Cnblogs.DashScope.Sdk/Cnblogs.DashScope.Sdk.csproj +++ b/src/Cnblogs.DashScope.Sdk/Cnblogs.DashScope.Sdk.csproj @@ -5,7 +5,7 @@ Cnblogs;Dashscope;AI;Sdk;Embedding; - + diff --git a/src/Cnblogs.DashScope.Sdk/QWen/QWenLlm.cs b/src/Cnblogs.DashScope.Sdk/QWen/QWenLlm.cs index 6a55c06..b8dbc98 100644 --- a/src/Cnblogs.DashScope.Sdk/QWen/QWenLlm.cs +++ b/src/Cnblogs.DashScope.Sdk/QWen/QWenLlm.cs @@ -129,4 +129,9 @@ public enum QWenLlm /// qwen-coder-turbo-latest /// QWenCoderTurboLatest = 24, + + /// + /// qvq-72b-preview + /// + QwQ72BPreview = 25 } diff --git a/src/Cnblogs.DashScope.Sdk/QWen/QWenLlmNames.cs b/src/Cnblogs.DashScope.Sdk/QWen/QWenLlmNames.cs index 6e09718..8bc299e 100644 --- a/src/Cnblogs.DashScope.Sdk/QWen/QWenLlmNames.cs +++ b/src/Cnblogs.DashScope.Sdk/QWen/QWenLlmNames.cs @@ -30,6 +30,7 @@ public static string GetModelName(this QWenLlm llm) QWenLlm.QWenPlusLatest => "qwen-plus-latest", QWenLlm.QWenTurboLatest => "qwen-turbo-latest", QWenLlm.QwQ32BPreview => "qwq-32b-preview", + QWenLlm.QwQ72BPreview => "qwq-72b-preview", _ => ThrowHelper.UnknownModelName(nameof(llm), llm) }; } diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/ChatClientTests.cs b/test/Cnblogs.DashScope.Sdk.UnitTests/ChatClientTests.cs index 9a58f70..89ac79c 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/ChatClientTests.cs +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/ChatClientTests.cs @@ -26,7 +26,7 @@ public async Task ChatClient_TextCompletion_SuccessAsync() var parameter = testCase.RequestModel.Parameters; // Act - var response = await client.CompleteAsync( + var response = await client.GetResponseAsync( content, new ChatOptions() { @@ -67,7 +67,7 @@ public async Task ChatClient_TextCompletionStream_SuccessAsync() var parameter = testCase.RequestModel.Parameters; // Act - var response = client.CompleteStreamingAsync( + var response = client.GetStreamingResponseAsync( content, new ChatOptions() { @@ -113,12 +113,12 @@ public async Task ChatClient_ImageRecognition_SuccessAsync() { new( ChatRole.User, - [new ImageContent(contents[0].Image!), new TextContent(contents[1].Text)]) + [new DataContent(contents[0].Image!, "image/png"), new TextContent(contents[1].Text)]) }; var parameter = testCase.RequestModel.Parameters; // Act - var response = await client.CompleteAsync( + var response = await client.GetResponseAsync( messages, new ChatOptions { @@ -157,12 +157,12 @@ public async Task ChatClient_ImageRecognitionStream_SuccessAsync() { new( ChatRole.User, - [new ImageContent(contents[0].Image!), new TextContent(contents[1].Text)]) + [new DataContent(contents[0].Image!, "image/png"), new TextContent(contents[1].Text)]) }; var parameter = testCase.RequestModel.Parameters; // Act - var response = client.CompleteStreamingAsync( + var response = client.GetStreamingResponseAsync( messages, new ChatOptions() { diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/Cnblogs.DashScope.Sdk.UnitTests.csproj b/test/Cnblogs.DashScope.Sdk.UnitTests/Cnblogs.DashScope.Sdk.UnitTests.csproj index 85dc33c..0c74f01 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/Cnblogs.DashScope.Sdk.UnitTests.csproj +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/Cnblogs.DashScope.Sdk.UnitTests.csproj @@ -6,16 +6,16 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/QWenTokenizerTests.cs b/test/Cnblogs.DashScope.Sdk.UnitTests/QWenTokenizerTests.cs index 997f1d4..8f91a35 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/QWenTokenizerTests.cs +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/QWenTokenizerTests.cs @@ -517,6 +517,6 @@ public void QWenTokenizer_CountIndex_SuccessAsync() // Assert maxIndex.Should().Be(155); - tokenCount.Should().BeLessOrEqualTo(100); + tokenCount.Should().BeLessThanOrEqualTo(100); } }