diff --git a/README.md b/README.md index 76f2676..9fdcbcb 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ Console.WriteLine($"Image token usage: {raw?.Usage?.ImageTokens}"); - [Tool Calling](#tool-calling) - [Prefix Completion](#prefix-completion) - [Long Context (Qwen-Long)](#long-context-qwen-long) + - [Code Interpreter](#code-interpreter) - [Multimodal](#multimodal) - QWen-VL, QVQ, etc. Supports reasoning/visual understanding/OCR/audio understanding - [Upload file for multimodal usage](#upload-file-for-multimodal-usage) - [Image Recognition/Thinking](#image-recognition/thinking) @@ -595,7 +596,118 @@ Console.WriteLine(completion.Output.Choices[0].Message.Content); await dashScopeClient.OpenAiCompatibleDeleteFileAsync(uploadedFile.Id); ``` +### Code Interpreter + +**This capability is mutually exclusive with Function Call and cannot be used simultaneously.** + +Use the `EnableCodeInterpreter` parameter in `Parameters` to allow the model to write code and call an internal code interpreter for calculations. + +Example Request: + +```csharp +var completion = client.GetTextCompletionStreamAsync( + new ModelRequest() + { + Model = "qwen3-max-preview", + Input = new TextGenerationInput() { Messages = messages }, + Parameters = new TextGenerationParameters() + { + ResultFormat = "message", + EnableThinking = true, + EnableCodeInterpreter = true, + IncrementalOutput = true + } + }); +``` + +The code that model generated will be included in `chunk.Output.ToolInfo.CodeInterpreter`. The invocation process can be considered part of the reasoning process. + +Full example, + +```csharp +var messages = new List(); +const string input = "123的21次方是多少?"; +Console.Write($"User > {input}"); +messages.Add(TextChatMessage.User(input)); +var completion = client.GetTextCompletionStreamAsync( + new ModelRequest + { + Model = "qwen3-max-preview", + Input = new TextGenerationInput { Messages = messages }, + Parameters = new TextGenerationParameters + { + ResultFormat = "message", + EnableThinking = true, + EnableCodeInterpreter = true, + IncrementalOutput = true + } + }); +var reply = new StringBuilder(); +var codeGenerated = false; +var reasoning = false; +TextGenerationTokenUsage? usage = null; +await foreach (var chunk in completion) +{ + var choice = chunk.Output.Choices![0]; + var tool = chunk.Output.ToolInfo?.FirstOrDefault(); + if (codeGenerated == false && tool?.CodeInterpreter != null) + { + Console.WriteLine($"Code > {tool.CodeInterpreter.Code}"); + codeGenerated = true; + } + + if (string.IsNullOrEmpty(choice.Message.ReasoningContent) == false) + { + // reasoning + if (reasoning == false) + { + Console.WriteLine(); + Console.Write("Reasoning > "); + reasoning = true; + } + + Console.Write(choice.Message.ReasoningContent); + continue; + } + + if (reasoning && string.IsNullOrEmpty(choice.Message.Content.Text) == false) + { + reasoning = false; + Console.WriteLine(); + Console.Write("Assistant > "); + } + + Console.Write(choice.Message.Content); + reply.Append(choice.Message.Content); + usage = chunk.Usage; +} + +Console.WriteLine(); +messages.Add(TextChatMessage.Assistant(reply.ToString())); +if (usage != null) +{ + Console.WriteLine( + $"Usage: in({usage.InputTokens})/out({usage.OutputTokens})/reasoning({usage.OutputTokensDetails?.ReasoningTokens})/plugins({usage.Plugins?.CodeInterpreter?.Count})/total({usage.TotalTokens})"); +} + +/* +User > 123的21次方是多少? +Reasoning > 用户问的是123的21次方是多少。这是一个大数计算问题,我需要使用代码计算器来计算这个值。 + +我需要调用code_interpreter函数,传入计算123**21的Python代码。 +123**21 +用户询问123的21次方是多少,我使用代码计算器计算出了结果。结果是一个非常大的数字:77269364466549865653073473388030061522211723 + +我应该直接给出这个结果,因为这是一个精确的数学计算问题,不需要额外的解释或 +Assistant > 123的21次方是:77269364466549865653073473388030061522211723 +Usage: in(704)/out(234)/reasoning(142)/plugins(1)/total(938) +*/ +``` + + + ## Multimodal + Use `GetMultimodalGenerationAsync`/`GetMultimodalGenerationStreamAsync` [Official Documentation](https://help.aliyun.com/zh/model-studio/multimodal) diff --git a/README.zh-Hans.md b/README.zh-Hans.md index b1c2b2e..e471642 100644 --- a/README.zh-Hans.md +++ b/README.zh-Hans.md @@ -12,6 +12,16 @@ ## 快速开始 +### 使用 `Microsoft.Extensions.AI` 接口 + +安装 NuGet 包 `Cnblogs.DashScope.AI` + +```csharp +var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max"); +var completion = await client.CompleteAsync("hello"); +Console.WriteLine(completion) +``` + ### 控制台应用 安装 NuGet 包 `Cnblogs.DashScope.Sdk`。 @@ -78,93 +88,6 @@ public class YourService(IDashScopeClient client) } ``` -### 使用 `Microsoft.Extensions.AI` 接口 - -安装 NuGet 包 `Cnblogs.DashScope.AI` - -```csharp -var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max"); -var completion = await client.GetResponseAsync("hello"); -Console.WriteLine(completion.Text); -``` - -#### 调用原始 SDK - -如果需要使用 `Microsoft.Extensions.AI` 不支持的输入数据或参数,可以通过 `RawPresentation` 直接传入原始的 `TextChatMessage` 或者 `MultimodalMessage` 来直接调用底层 SDK。 - -类似地,当需要传入不支持的参数时,也可以通过设置 `AdditionalProperties` 里的 `raw` 直接传入原始参数。 - -示例(调用 `qwen-doc-turbo`) - -```csharp -var messages = new List() -{ - TextChatMessage.DocUrl( - "从这两份产品手册中,提取所有产品信息,并整理成一个标准的JSON数组。每个对象需要包含:model(产品的型号)、name(产品的名称)、price(价格(去除货币符号和逗号))", - [ - "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251107/jockge/%E7%A4%BA%E4%BE%8B%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8CA.docx", - "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251107/ztwxzr/%E7%A4%BA%E4%BE%8B%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8CB.docx" - ]) -}; -var parameters = new TextGenerationParameters() -{ - ResultFormat = "message", IncrementalOutput = true, -}; - -var response = client - .AsChatClient("qwen-doc-turbo") - .GetStreamingResponseAsync( - messages.Select(x => new ChatMessage() { RawRepresentation = x }), - new ChatOptions() - { - AdditionalProperties = new AdditionalPropertiesDictionary() { { "raw", parameters } } - }); -await foreach (var chunk in response) -{ - Console.Write(chunk.Text); -} -``` - -类似地,也可以通过模型返回消息里的 `RawPresentation` 获取原始消息。 - -示例(调用 `qwen3-vl-plus` 时获取图像消耗的 Token 数): - -```csharp -var response = client - .AsChatClient("qwen3-vl-plus") - .GetStreamingResponseAsync( - new List() - { - new( - ChatRole.User, - new List() - { - new UriContent( - "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg", - MediaTypeNames.Image.Jpeg), - new UriContent( - "https://dashscope.oss-cn-beijing.aliyuncs.com/images/tiger.png", - MediaTypeNames.Image.Jpeg), - new TextContent("这些图展现了什么内容?") - }) - }, - new ChatOptions()); -var lastChunk = (ChatResponseUpdate?)null; -await foreach (var chunk in response) -{ - Console.Write(chunk.Text); - lastChunk = chunk; -} - -Console.WriteLine(); - -// 访问原始消息 -var raw = lastChunk?.RawRepresentation as ModelResponse; -Console.WriteLine($"Image token usage: {raw?.Usage?.ImageTokens}"); -``` - - - ## 支持的 API - [文本生成](#文本生成) - QWen3, DeepSeek 等,支持推理/工具调用/网络搜索/翻译等场景 @@ -172,6 +95,7 @@ Console.WriteLine($"Image token usage: {raw?.Usage?.ImageTokens}"); - [深度思考](#深度思考) - [联网搜索](#联网搜索) - [工具调用](#工具调用) + - [代码解释器](#代码解释器) - [前缀续写](#前缀续写) - [长上下文(Qwen-Long)](#长上下文(Qwen-Long)) - [翻译能力(Qwen-MT)](#翻译能力(Qwen-MT)) @@ -189,7 +113,6 @@ Console.WriteLine($"Image token usage: {raw?.Usage?.ImageTokens}"); - [通用文本识别](#通用文本识别) - [多语言识别](#多语言识别) - [界面交互](#界面交互) - - [音频理解](#音频理解) - [语音合成](#语音合成) - CosyVoice,Sambert 等,支持 TTS 等应用场景 - [图像生成](#图像生成) - wanx2.1 等,支持文生图,人像风格重绘等应用场景 - [应用调用](#应用调用) @@ -1090,6 +1013,112 @@ Usage: in(302)/out(19)/total(321) */ ``` +### 代码解释器 + +**该能力与 Function call 互斥,无法同时使用。** + +通过 `Parameters` 里的 `EnableCodeInterpreter` 允许模型编写代码并调用内部代码解释器来进行计算。 + +示例请求: + +```csharp +var completion = client.GetTextCompletionStreamAsync( + new ModelRequest() + { + Model = "qwen3-max-preview", + Input = new TextGenerationInput() { Messages = messages }, + Parameters = new TextGenerationParameters() + { + ResultFormat = "message", + EnableThinking = true, + EnableCodeInterpreter = true, + IncrementalOutput = true + } + }); +``` + +完整示例,大模型生成的代码将被包含在 `chunk.Output.ToolInfo.CodeInterpreter` 里,调用过程可以视为思考过程的一部分。 + +```csharp +var messages = new List(); +const string input = "123的21次方是多少?"; +Console.Write($"User > {input}"); +messages.Add(TextChatMessage.User(input)); +var completion = client.GetTextCompletionStreamAsync( + new ModelRequest + { + Model = "qwen3-max-preview", + Input = new TextGenerationInput { Messages = messages }, + Parameters = new TextGenerationParameters + { + ResultFormat = "message", + EnableThinking = true, + EnableCodeInterpreter = true, + IncrementalOutput = true + } + }); +var reply = new StringBuilder(); +var codeGenerated = false; +var reasoning = false; +TextGenerationTokenUsage? usage = null; +await foreach (var chunk in completion) +{ + var choice = chunk.Output.Choices![0]; + var tool = chunk.Output.ToolInfo?.FirstOrDefault(); + if (codeGenerated == false && tool?.CodeInterpreter != null) + { + Console.WriteLine($"Code > {tool.CodeInterpreter.Code}"); + codeGenerated = true; + } + + if (string.IsNullOrEmpty(choice.Message.ReasoningContent) == false) + { + // reasoning + if (reasoning == false) + { + Console.WriteLine(); + Console.Write("Reasoning > "); + reasoning = true; + } + + Console.Write(choice.Message.ReasoningContent); + continue; + } + + if (reasoning && string.IsNullOrEmpty(choice.Message.Content.Text) == false) + { + reasoning = false; + Console.WriteLine(); + Console.Write("Assistant > "); + } + + Console.Write(choice.Message.Content); + reply.Append(choice.Message.Content); + usage = chunk.Usage; +} + +Console.WriteLine(); +messages.Add(TextChatMessage.Assistant(reply.ToString())); +if (usage != null) +{ + Console.WriteLine( + $"Usage: in({usage.InputTokens})/out({usage.OutputTokens})/reasoning({usage.OutputTokensDetails?.ReasoningTokens})/plugins({usage.Plugins?.CodeInterpreter?.Count})/total({usage.TotalTokens})"); +} + +/* +User > 123的21次方是多少? +Reasoning > 用户问的是123的21次方是多少。这是一个大数计算问题,我需要使用代码计算器来计算这个值。 + +我需要调用code_interpreter函数,传入计算123**21的Python代码。 +123**21 +用户询问123的21次方是多少,我使用代码计算器计算出了结果。结果是一个非常大的数字:77269364466549865653073473388030061522211723 + +我应该直接给出这个结果,因为这是一个精确的数学计算问题,不需要额外的解释或 +Assistant > 123的21次方是:77269364466549865653073473388030061522211723 +Usage: in(704)/out(234)/reasoning(142)/plugins(1)/total(938) +*/ +``` + ### 结构化输出(JSON 输出) 设置 `Parameter` 里的 `ResponseFormat` (注意不是 `ResultFormat` )为 JSON 即可强制大模型以 JSON 格式输出。 @@ -1255,46 +1284,13 @@ Usage: in(31)/out(34)/reasoning()/total(65) 尽管 QWen-Long 支持直接传入字符串,但还是推荐先将文件上传后再通过 FileId 的形式传入 `message` 数组中。 -上传文件,使用 `OpenAiCompatibleUploadFileAsync()` 方法传入文件: - -```csharp -var file1 = await client.OpenAiCompatibleUploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); -``` - -如果文件比较大,服务端可能需要几秒的时间进行解析。根据返回的 `file.Status` 属性是否为 `processed` 可以判断是否解析完成。未解析完成的文件无法被模型使用,需要等待解析完成。以下是一个示例方法,自动等待文档解析完毕或超时抛出异常: - -```csharp -private static async Task EnsureFileProcessedAsync( - IDashScopeClient client, - DashScopeFileId id, - int timeoutInSeconds = 5) -{ - var timeout = Task.Delay(TimeSpan.FromSeconds(timeoutInSeconds)); - while (timeout.IsCompleted == false) - { - var file = await client.GetFileAsync(id); - if (file.Status == "processed") - { - return; - } - - await Task.Delay(1000); - } - - throw new InvalidOperationException($"File not processed within timeout, fileId: {id}"); -} -``` - -调用方式: +上传文件,使用 `UploadFileAsync()` 方法传入文件(注意不是 `UploadTemporaryFileAsync`, 后者是用于上传媒体文件的): ```csharp -if (file.Status != "processed") -{ - await EnsureFileProcessedAsync(client, file.Id, 3); // 最多等待 3 秒 -} +var file1 = await client.UploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); ``` -待文档解析完成后,将文件作为 `system` 消息传入消息数组中,注意第一条 `system` 消息不能省略,否则模型可能会将文件里的内容当作 `System prompt` 。 +然后将文件作为 `system` 消息传入消息数组中,注意第一条 `system` 消息不能省略,否则模型可能会将文件里的内容当作 System prompt 。 ```csharp var messages = new List(); @@ -1326,10 +1322,10 @@ var completion = client.GetTextCompletionStreamAsync( }); ``` -最后可以通过 `OpenAiCompatibleDeleteFileAsync()` 方法删除上传的文件。 +最后可以通过 `DeleteFileAsync()` 方法删除上传的文件 ```csharp -var result = await client.OpenAiCompatibleDeleteFileAsync(file1.Id); +var result = await client.DeleteFileAsync(file1.Id); Console.WriteLine(result.Deleted ? "Success" : "Failed"); ``` @@ -1337,14 +1333,11 @@ Console.WriteLine(result.Deleted ? "Success" : "Failed"); ```csharp Console.WriteLine("Uploading file1..."); -var file1 = await client.OpenAiCompatibleUploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); +var file1 = await client.UploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); Console.WriteLine("Uploading file2..."); -var file2 = await client.OpenAiCompatibleUploadFileAsync(File.OpenRead("1024-2.txt"), "file2.txt"); +var file2 = await client.UploadFileAsync(File.OpenRead("1024-2.txt"), "file2.txt"); Console.WriteLine($"Uploaded, file1 id: {file1.Id.ToUrl()}, file2 id: {file2.Id.ToUrl()}"); -await EnsureFileProcessedAsync(client, file1); -await EnsureFileProcessedAsync(client, file2); - var messages = new List(); messages.Add(TextChatMessage.System("You are a helpful assistant")); messages.Add(TextChatMessage.File(file1.Id)); @@ -1410,31 +1403,6 @@ Console.Write("Deleting file2..."); result = await client.DeleteFileAsync(file2.Id); Console.WriteLine(result.Deleted ? "Success" : "Failed"); -private static async Task EnsureFileProcessedAsync( - IDashScopeClient client, - DashScopeFile file, - int timeoutInSeconds = 5) -{ - if (file.Status == "processed") - { - return; - } - - var timeout = Task.Delay(TimeSpan.FromSeconds(timeoutInSeconds)); - while (timeout.IsCompleted == false) - { - var realtime = await client.GetFileAsync(file.Id); - if (realtime.Status == "processed") - { - return; - } - - await Task.Delay(1000); - } - - throw new InvalidOperationException($"File not processed within timeout, fileId: {file.Id}"); -} - /* Uploading file1... Uploading file2... @@ -1650,10 +1618,10 @@ Usage: in(147)/out(130)/total(277) ### 数据挖掘(Qwen-doc-turbo) -上传文件,使用 `OpenAiCompatibleUploadFileAsync()` 方法传入文件(注意不是 `UploadTemporaryFileAsync`, 后者是用于上传媒体文件的): +上传文件,使用 `UploadFileAsync()` 方法传入文件(注意不是 `UploadTemporaryFileAsync`, 后者是用于上传媒体文件的): ```csharp -var file1 = await client.OpenAiCompatibleUploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); +var file1 = await client.UploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); ``` 然后将文件作为 `system` 消息传入消息数组中,注意第一条 `system` 消息不能省略,否则模型可能会将文件里的内容当作 System prompt 。 @@ -1688,28 +1656,11 @@ var completion = client.GetTextCompletionStreamAsync( }); ``` -如果文件来自公网 URL,也可以使用 `TextChatMessage.DocUrl` 传入,此时不再需要额外添加一个 User 信息。 - -示例: - -```csharp -var messages = new List - { - TextChatMessage.System("You are a helpful assistant"), - TextChatMessage.DocUrl( - "从这两份产品手册中,提取所有产品信息,并整理成一个标准的JSON数组。每个对象需要包含:model(产品的型号)、name(产品的名称)、price(价格(去除货币符号和逗号))", - [ - "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251107/jockge/%E7%A4%BA%E4%BE%8B%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8CA.docx", - "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251107/ztwxzr/%E7%A4%BA%E4%BE%8B%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8CB.docx" - ]), - }; -``` - 完整示例代码: ````csharp Console.WriteLine("Uploading file1..."); -var file1 = await client.OpenAiCompatibleUploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); +var file1 = await client.UploadFileAsync(File.OpenRead("1024-1.txt"), "file1.txt"); var messages = new List(); messages.Add(TextChatMessage.System("You are a helpful assistant")); messages.Add(TextChatMessage.File(file1.Id)); @@ -1753,7 +1704,7 @@ if (usage != null) // Deleting files Console.Write("Deleting file1..."); -var result = await client.OpenAiCompatibleDeleteFileAsync(file1.Id); +var result = await client.DeleteFileAsync(file1.Id); Console.WriteLine(result.Deleted ? "Success" : "Failed"); /* @@ -2751,7 +2702,7 @@ var completion = client.GetMultimodalGenerationAsync( 示例: -![网页](sample/Cnblogs.DashScope.Sample/webpage.jpg) +![倾斜的图像](sample/Cnblogs.DashScope.Sample/tilted.png) ```csharp Console.WriteLine("Text:"); @@ -2771,7 +2722,7 @@ foreach (var info in completion.Output.Choices[0].Message.Content[0].OcrResult!. 输出结果: ````csharp -Text: +Text: ```json [ {"rotate_rect": [236, 254, 115, 299, 90], "text": "OpenAI 兼容"}, @@ -2781,7 +2732,7 @@ Text: {"rotate_rect": [712, 684, 115, 85, 90], "text": "curl"} ] ``` -WordsInfo: +WordsInfo: OpenAI 兼容 Location: [46,55,205,55,205,87,46,87] RotateRect: [125,71,159,32,0] @@ -3400,7 +3351,7 @@ Salam! "y": , "description": "" } - + ### TYPE - **功能**: 输入文本。 - **Parameters模板**: @@ -3408,7 +3359,7 @@ Salam! "text": "", "needs_enter": } - + ### SCROLL - **功能**: 滚动窗口。 - **Parameters模板**: @@ -3416,28 +3367,28 @@ Salam! "direction": "<'up' or 'down'>", "amount": "<'small', 'medium', or 'large'>" } - + ### KEY_PRESS - **功能**: 按下功能键。 - **Parameters模板**: { "key": "" } - + ### FINISH - **功能**: 任务成功完成。 - **Parameters模板**: { "message": "" } - + ### FAILE - **功能**: 任务无法完成。 - **Parameters模板**: { "reason": "" } - + ## 4. 思维与决策框架 在生成每一步操作前,请严格遵循以下思考-验证流程: @@ -3491,78 +3442,6 @@ var completion = client.GetMultimodalGenerationStreamAsync( 随后您需要自行实现大模型返回的操作(这里是点击屏幕上的位置),然后返回下一步的截图和意图。 -### 音频理解 - -`qwen-audio` 无法用于生产环境,这里以使用 `Qwen3-Omni-Captioner` 为例: - -示例请求: - -```csharp -// upload file -await using var audio = File.OpenRead("noise.wav"); -var ossLink = await client.UploadTemporaryFileAsync("qwen3-omni-30b-a3b-captioner", audio, "noise.wav"); -Console.WriteLine($"File uploaded: {ossLink}"); -var messages = new List -{ - MultimodalMessage.User( - [ - // 也可以直接传入公网地址 - MultimodalMessageContent.AudioContent(ossLink), - ]) -}; -var completion = client.GetMultimodalGenerationStreamAsync( - new ModelRequest() - { - Model = "qwen3-omni-30b-a3b-captioner", - Input = new MultimodalInput() { Messages = messages }, - Parameters = new MultimodalParameters() { IncrementalOutput = true, } - }); -``` - -这里开启了流式增量输出,遍历返回的 `IAsyncEnumerable` 即可获取模型回复,示例: - -```csharp -var reply = new StringBuilder(); -var first = true; -MultimodalTokenUsage? usage = null; -await foreach (var chunk in completion) -{ - var choice = chunk.Output.Choices[0]; - if (first) - { - first = false; - Console.WriteLine(); - Console.Write("Assistant > "); - } - - if (choice.Message.Content.Count == 0) - { - continue; - } - - Console.Write(choice.Message.Content[0].Text); - reply.Append(choice.Message.Content[0].Text); - usage = chunk.Usage; -} - -Console.WriteLine(); -messages.Add(MultimodalMessage.Assistant([MultimodalMessageContent.TextContent(reply.ToString())])); -``` - -示例输出: - -``` -Assistant > The audio clip opens with a rapid, percussive metallic clatter, reminiscent of a typewriter or similar mechanical device, which continues in a steady rhythm throughout the recording. This clatter is slightly left-of-center in the stereo field and is accompanied by a faint, low-frequency hum, likely from a household appliance or HVAC system. The acoustic environment is a small, enclosed room with hard surfaces, indicated by the short, bright reverberation of both the clatter and the speaker’s voice. The audio quality is moderate, with a noticeable electronic hiss and some loss of high-frequency detail, but no digital distortion or clipping. - -At the one-second mark, a male voice enters, positioned slightly right-of-center and closer to the microphone. He speaks in standard Mandarin, with a tone of weary exasperation: “哎 呀,这样我还怎么安静工作啊?” (“Aiyā, zěnyàng wǒ hái zěnme ānjìng gōngzuò a?”), which translates to “Oh, how can I possibly work quietly like this?” His speech is clear, with a slightly rising pitch on “安静” (“quietly”) and a falling pitch on “啊” (“a”), conveying a sense of complaint and fatigue. The accent is standard, with no regional inflection, and the voice is that of a young to middle-aged adult male. - -Throughout the clip, the mechanical clatter remains constant and prominent, occasionally competing with the voice for clarity. There are no other sounds, such as footsteps, additional voices, or environmental noises, and the background is otherwise quiet. The interplay between the persistent mechanical noise and the speaker’s complaint creates a vivid sense of disruption and frustration, suggesting an environment where work is being impeded by an external, uncontrolled sound source. - -Culturally, the use of Mandarin, standard pronunciation, and modern recording quality indicate a contemporary, urban Chinese setting. The language and tone are universally relatable, reflecting a common experience of being disturbed during work. The lack of regional markers or distinctive background noises suggests a generic, possibly domestic or office-like space, but with no clear indicators of a specific location or social context. - -In summary, the audio portrays a modern Mandarin-speaking man, exasperated by a constant, distracting mechanical noise (likely a typewriter or similar device), attempting to work in a small, reverberant room. The recording’s technical and acoustic features reinforce the sense of disruption and frustration, while the language and setting suggest a contemporary, urban Chinese context. -``` - ## 语音合成 通过 `dashScopeClient.CreateSpeechSynthesizerSocketSessionAsync()` 来创建一个语音合成会话。 diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/ImageInputSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/ImageInputSample.cs index 85ed8d0..f223d17 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/ImageInputSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/ImageInputSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class ImageInputSample: MultimodalSample +public class ImageInputSample : MultimodalSample { /// public override string Description => "Chat with image input"; @@ -11,15 +11,16 @@ public class ImageInputSample: MultimodalSample /// public override async Task RunAsync(IDashScopeClient client) { - var messages = new List(); - messages.Add( + var messages = new List + { MultimodalMessage.User( [ MultimodalMessageContent.ImageContent( "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"), MultimodalMessageContent.ImageContent("https://dashscope.oss-cn-beijing.aliyuncs.com/images/tiger.png"), MultimodalMessageContent.TextContent("这些图展现了什么内容?") - ])); + ]) + }; var completion = client.GetMultimodalGenerationStreamAsync( new ModelRequest() { diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/ImageUploadSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/ImageUploadSample.cs index 4104cef..a98d260 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/ImageUploadSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/ImageUploadSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class ImageUploadSample: MultimodalSample +public class ImageUploadSample : MultimodalSample { /// public override string Description => "Upload image from file system"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrAdvancedRecognitionSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrAdvancedRecognitionSample.cs index 611128b..42bd1ab 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrAdvancedRecognitionSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrAdvancedRecognitionSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrAdvancedRecognitionSample: MultimodalSample +public class OcrAdvancedRecognitionSample : MultimodalSample { /// public override string Description => "OCR Advanced Recognition Task Sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrDocumentToLaTeXSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrDocumentToLaTeXSample.cs index 84986ec..105ac03 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrDocumentToLaTeXSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrDocumentToLaTeXSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrDocumentToLaTeXSample: MultimodalSample +public class OcrDocumentToLaTeXSample : MultimodalSample { /// public override string Description => "OCR parsing scanned document to LaTeX sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrFormulaRecognitionSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrFormulaRecognitionSample.cs index f15a989..8cb86a0 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrFormulaRecognitionSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrFormulaRecognitionSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrFormulaRecognitionSample: MultimodalSample +public class OcrFormulaRecognitionSample : MultimodalSample { /// public override string Description => "OCR Math Formula Recognition Sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrKeyInformationExtractionSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrKeyInformationExtractionSample.cs index 9abf9a3..ff2e408 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrKeyInformationExtractionSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrKeyInformationExtractionSample.cs @@ -4,7 +4,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrKeyInformationExtractionSample: MultimodalSample +public class OcrKeyInformationExtractionSample : MultimodalSample { /// public override string Description => "OCR Key Information Extraction Sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrMultilanguageSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrMultilanguageSample.cs index dd4c5c9..c3ebe05 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrMultilanguageSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrMultilanguageSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrMultilanguageSample: MultimodalSample +public class OcrMultilanguageSample : MultimodalSample { /// public override string Description => "OCR Text Recognition(Multilanguage) Sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrSample.cs index c4b1924..921f885 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrSample: MultimodalSample +public class OcrSample : MultimodalSample { /// public override string Description => "OCR Sample with rotate enabled"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTableParsingSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTableParsingSample.cs index 31403b8..4b22199 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTableParsingSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTableParsingSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrTableParsingSample: MultimodalSample +public class OcrTableParsingSample : MultimodalSample { /// public override string Description => "OCR Table Parsing Sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTextRecognition.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTextRecognition.cs index 5ab9329..ed8052f 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTextRecognition.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/OcrTextRecognition.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class OcrTextRecognition: MultimodalSample +public class OcrTextRecognition : MultimodalSample { /// public override string Description => "OCR Text Recognition(Chinese and English) Sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Multimodal/VideoInputSample.cs b/sample/Cnblogs.DashScope.Sample/Multimodal/VideoInputSample.cs index bd2af6d..02b3557 100644 --- a/sample/Cnblogs.DashScope.Sample/Multimodal/VideoInputSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Multimodal/VideoInputSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Multimodal; -public class VideoInputSample: MultimodalSample +public class VideoInputSample : MultimodalSample { /// public override string Description => "Video input sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/ChatSample.cs b/sample/Cnblogs.DashScope.Sample/Text/ChatSample.cs index 74c309b..b46b84e 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/ChatSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/ChatSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class ChatSample: TextSample +public class ChatSample : TextSample { /// public override string Description => "Basic chat completion"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/ChatStreamSample.cs b/sample/Cnblogs.DashScope.Sample/Text/ChatStreamSample.cs index 0ecce02..704cfef 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/ChatStreamSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/ChatStreamSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class ChatStreamSample: TextSample +public class ChatStreamSample : TextSample { /// public override string Description => "Chat completion with stream output"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/ChatThinkingBudgetSample.cs b/sample/Cnblogs.DashScope.Sample/Text/ChatThinkingBudgetSample.cs index cb7e789..c1663f4 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/ChatThinkingBudgetSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/ChatThinkingBudgetSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class ChatThinkingBudgetSample: TextSample +public class ChatThinkingBudgetSample : TextSample { /// public override string Description => "Chat completion with thinking budget"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/ChatToolCallingSample.cs b/sample/Cnblogs.DashScope.Sample/Text/ChatToolCallingSample.cs index 1789a60..434f60b 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/ChatToolCallingSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/ChatToolCallingSample.cs @@ -7,7 +7,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class ChatToolCallingSample: TextSample +public class ChatToolCallingSample : TextSample { /// public override string Description => "Chat with tool calling"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/ChatWebSearchSample.cs b/sample/Cnblogs.DashScope.Sample/Text/ChatWebSearchSample.cs index c8b5302..8587625 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/ChatWebSearchSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/ChatWebSearchSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class ChatWebSearchSample: TextSample +public class ChatWebSearchSample : TextSample { /// public override string Description => "Chat with web search enabled"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/CodeInterpreterSample.cs b/sample/Cnblogs.DashScope.Sample/Text/CodeInterpreterSample.cs new file mode 100644 index 0000000..63b0f46 --- /dev/null +++ b/sample/Cnblogs.DashScope.Sample/Text/CodeInterpreterSample.cs @@ -0,0 +1,92 @@ +using System.Text; +using Cnblogs.DashScope.Core; + +namespace Cnblogs.DashScope.Sample.Text; + +public class CodeInterpreterSample : TextSample +{ + /// + public override string Description => "Chat with code interpreter"; + + /// + public async override Task RunAsync(IDashScopeClient client) + { + var messages = new List(); + const string input = "123的21次方是多少?"; + Console.Write($"User > {input}"); + messages.Add(TextChatMessage.User(input)); + var completion = client.GetTextCompletionStreamAsync( + new ModelRequest + { + Model = "qwen3-max-preview", + Input = new TextGenerationInput { Messages = messages }, + Parameters = new TextGenerationParameters + { + ResultFormat = "message", + EnableThinking = true, + EnableCodeInterpreter = true, + IncrementalOutput = true + } + }); + var reply = new StringBuilder(); + var codeGenerated = false; + var reasoning = false; + TextGenerationTokenUsage? usage = null; + await foreach (var chunk in completion) + { + var choice = chunk.Output.Choices![0]; + var tool = chunk.Output.ToolInfo?.FirstOrDefault(); + if (codeGenerated == false && tool?.CodeInterpreter != null) + { + Console.WriteLine(tool.CodeInterpreter.Code); + codeGenerated = true; + } + + if (string.IsNullOrEmpty(choice.Message.ReasoningContent) == false) + { + // reasoning + if (reasoning == false) + { + Console.WriteLine(); + Console.Write("Reasoning > "); + reasoning = true; + } + + Console.Write(choice.Message.ReasoningContent); + continue; + } + + if (reasoning && string.IsNullOrEmpty(choice.Message.Content.Text) == false) + { + reasoning = false; + Console.WriteLine(); + Console.Write("Assistant > "); + } + + Console.Write(choice.Message.Content); + reply.Append(choice.Message.Content); + usage = chunk.Usage; + } + + Console.WriteLine(); + messages.Add(TextChatMessage.Assistant(reply.ToString())); + if (usage != null) + { + Console.WriteLine( + $"Usage: in({usage.InputTokens})/out({usage.OutputTokens})/reasoning({usage.OutputTokensDetails?.ReasoningTokens})/plugins({usage.Plugins?.CodeInterpreter?.Count})/total({usage.TotalTokens})"); + } + } +} + +/* +User > 123的21次方是多少? +Reasoning > 用户问的是123的21次方是多少。这是一个大数计算问题,我需要使用代码计算器来计算这个值。 + +我需要调用code_interpreter函数,传入计算123**21的Python代码。 +Code > 123**21 +用户询问123的21次方是多少,我使用代码计算器计算出了结果。结果是一个非常大的数字:77269364466549865653073473388030061522211723 + +我应该直接给出这个结果,因为这是一个精确的数学计算问题,不需要额外的解释或 +Assistant > 123的21次方是:77269364466549865653073473388030061522211723 +Usage: in(704)/out(234)/reasoning(142)/plugins(1)/total(938) + */ diff --git a/sample/Cnblogs.DashScope.Sample/Text/DataMiningSample.cs b/sample/Cnblogs.DashScope.Sample/Text/DataMiningSample.cs index 44c5085..eb846a2 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/DataMiningSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/DataMiningSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class DataMiningSample: TextSample +public class DataMiningSample : TextSample { /// public override string Description => "Data Mining with Qwen-Doc-Turbo"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/DeepResearchSample.cs b/sample/Cnblogs.DashScope.Sample/Text/DeepResearchSample.cs index 5949928..ad471a0 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/DeepResearchSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/DeepResearchSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class DeepResearchSample: TextSample +public class DeepResearchSample : TextSample { /// public override string Description => "Deep research sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/JsonOutputSample.cs b/sample/Cnblogs.DashScope.Sample/Text/JsonOutputSample.cs index e6477a7..335d01e 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/JsonOutputSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/JsonOutputSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class JsonOutputSample: TextSample +public class JsonOutputSample : TextSample { /// public override string Description => "JSON output text sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/LongContextSample.cs b/sample/Cnblogs.DashScope.Sample/Text/LongContextSample.cs index 0598d18..777de16 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/LongContextSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/LongContextSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class LongContextSample: TextSample +public class LongContextSample : TextSample { /// public override string Description => "File upload and long context model sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/PrefixCompletionSample.cs b/sample/Cnblogs.DashScope.Sample/Text/PrefixCompletionSample.cs index bead37a..f903b8a 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/PrefixCompletionSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/PrefixCompletionSample.cs @@ -3,7 +3,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class PrefixCompletionSample: TextSample +public class PrefixCompletionSample : TextSample { /// public override string Description => "Prefix completion sample"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/RolePlaySample.cs b/sample/Cnblogs.DashScope.Sample/Text/RolePlaySample.cs index 3ce6cff..bd9e010 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/RolePlaySample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/RolePlaySample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class RolePlaySample: TextSample +public class RolePlaySample : TextSample { /// public override string Description => "Role play with qwen-character"; diff --git a/sample/Cnblogs.DashScope.Sample/Text/TranslationSample.cs b/sample/Cnblogs.DashScope.Sample/Text/TranslationSample.cs index 28868e3..8e24873 100644 --- a/sample/Cnblogs.DashScope.Sample/Text/TranslationSample.cs +++ b/sample/Cnblogs.DashScope.Sample/Text/TranslationSample.cs @@ -2,7 +2,7 @@ namespace Cnblogs.DashScope.Sample.Text; -public class TranslationSample: TextSample +public class TranslationSample : TextSample { /// public override string Description => "Translate with Qwen-MT models"; diff --git a/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj b/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj index 9aa095e..d75ae37 100644 --- a/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj +++ b/src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj @@ -1,6 +1,5 @@  - net8.0 Cnblogs.DashScope.AI true Cnblogs;Dashscope;Microsoft.Extensions.AI;Sdk;Embedding; diff --git a/src/Cnblogs.DashScope.Core/ITextGenerationParameters.cs b/src/Cnblogs.DashScope.Core/ITextGenerationParameters.cs index 46566e1..f904ea4 100644 --- a/src/Cnblogs.DashScope.Core/ITextGenerationParameters.cs +++ b/src/Cnblogs.DashScope.Core/ITextGenerationParameters.cs @@ -99,4 +99,9 @@ public interface ITextGenerationParameters /// or visit the official doc for more information: https://help.aliyun.com/zh/model-studio/role-play /// Dictionary? LogitBias { get; set; } + + /// + /// Allow model to call internal Python interpreter. Can not use with tools. + /// + bool? EnableCodeInterpreter { get; set; } } diff --git a/src/Cnblogs.DashScope.Core/Internals/TextChatMessageContentConvertor.cs b/src/Cnblogs.DashScope.Core/Internals/TextChatMessageContentConvertor.cs index 11ad871..104a10e 100644 --- a/src/Cnblogs.DashScope.Core/Internals/TextChatMessageContentConvertor.cs +++ b/src/Cnblogs.DashScope.Core/Internals/TextChatMessageContentConvertor.cs @@ -30,8 +30,8 @@ internal class TextChatMessageContentConvertor : JsonConverter x is { DocUrl: not null, FileParsingStrategy: not null }) ?? throw new JsonException("No doc_url and file_parsing_strategy were found"); - var docUrls = docUrlContent?.DocUrl; - var strategy = docUrlContent?.FileParsingStrategy; + var docUrls = docUrlContent.DocUrl; + var strategy = docUrlContent.FileParsingStrategy; return new TextChatMessageContent(text, docUrls, strategy); } diff --git a/src/Cnblogs.DashScope.Core/TextGenerationCodeInterpreterPluginUsage.cs b/src/Cnblogs.DashScope.Core/TextGenerationCodeInterpreterPluginUsage.cs new file mode 100644 index 0000000..0b641b7 --- /dev/null +++ b/src/Cnblogs.DashScope.Core/TextGenerationCodeInterpreterPluginUsage.cs @@ -0,0 +1,21 @@ +namespace Cnblogs.DashScope.Core; + +/// +/// Token usages of code interpreter plugin. +/// +public class TextGenerationCodeInterpreterPluginUsage +{ + /// + /// Initialize a with count. + /// + /// Usage count. + public TextGenerationCodeInterpreterPluginUsage(int count) + { + Count = count; + } + + /// + /// Token usage count. + /// + public int Count { get; set; } +} diff --git a/src/Cnblogs.DashScope.Core/TextGenerationOutput.cs b/src/Cnblogs.DashScope.Core/TextGenerationOutput.cs index f379ac7..1f3dbf1 100644 --- a/src/Cnblogs.DashScope.Core/TextGenerationOutput.cs +++ b/src/Cnblogs.DashScope.Core/TextGenerationOutput.cs @@ -29,4 +29,9 @@ public class TextGenerationOutput /// Not null when . configured to show source. /// public TextGenerationWebSearchInfo? SearchInfo { get; set; } + + /// + /// Outputs from the tool being called by model. + /// + public List? ToolInfo { get; set; } } diff --git a/src/Cnblogs.DashScope.Core/TextGenerationParameters.cs b/src/Cnblogs.DashScope.Core/TextGenerationParameters.cs index 84578ba..f87d0d9 100644 --- a/src/Cnblogs.DashScope.Core/TextGenerationParameters.cs +++ b/src/Cnblogs.DashScope.Core/TextGenerationParameters.cs @@ -74,6 +74,9 @@ public class TextGenerationParameters : ITextGenerationParameters /// public Dictionary? LogitBias { get; set; } + /// + public bool? EnableCodeInterpreter { get; set; } + /// public bool? IncrementalOutput { get; set; } } diff --git a/src/Cnblogs.DashScope.Core/TextGenerationPluginUsages.cs b/src/Cnblogs.DashScope.Core/TextGenerationPluginUsages.cs index 40b5d9a..76588af 100644 --- a/src/Cnblogs.DashScope.Core/TextGenerationPluginUsages.cs +++ b/src/Cnblogs.DashScope.Core/TextGenerationPluginUsages.cs @@ -3,5 +3,26 @@ /// /// Plugin usages. /// -/// Usage of search plugin. -public record TextGenerationPluginUsages(TextGenerationSearchPluginUsage? Search); +public class TextGenerationPluginUsages +{ + /// + /// Plugin usages. + /// + /// Usage of search plugin. + /// Usage of code interpreter plugin. + public TextGenerationPluginUsages( + TextGenerationSearchPluginUsage? search = null, + TextGenerationCodeInterpreterPluginUsage? codeInterpreter = null) + { + Search = search; + CodeInterpreter = codeInterpreter; + } + + /// Usage of search plugin. + public TextGenerationSearchPluginUsage? Search { get; set; } + + /// + /// Usage of code interpreter plugin. + /// + public TextGenerationCodeInterpreterPluginUsage? CodeInterpreter { get; set; } +} diff --git a/src/Cnblogs.DashScope.Core/ToolInfoCodeInterpreterOutput.cs b/src/Cnblogs.DashScope.Core/ToolInfoCodeInterpreterOutput.cs new file mode 100644 index 0000000..62f055a --- /dev/null +++ b/src/Cnblogs.DashScope.Core/ToolInfoCodeInterpreterOutput.cs @@ -0,0 +1,12 @@ +namespace Cnblogs.DashScope.Core; + +/// +/// Output from the code interpreter tool. +/// +public class ToolInfoCodeInterpreterOutput +{ + /// + /// Code that being executed. + /// + public string Code { get; set; } = string.Empty; +} diff --git a/src/Cnblogs.DashScope.Core/ToolInfoOutput.cs b/src/Cnblogs.DashScope.Core/ToolInfoOutput.cs new file mode 100644 index 0000000..4c1b2e6 --- /dev/null +++ b/src/Cnblogs.DashScope.Core/ToolInfoOutput.cs @@ -0,0 +1,17 @@ +namespace Cnblogs.DashScope.Core; + +/// +/// Outputs from the tools. +/// +public class ToolInfoOutput +{ + /// + /// Output from the code interpreter. + /// + public ToolInfoCodeInterpreterOutput? CodeInterpreter { get; set; } + + /// + /// Type of the tool. + /// + public string Type { get; set; } = string.Empty; +} diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/TextGenerationSerializationTests.cs b/test/Cnblogs.DashScope.Sdk.UnitTests/TextGenerationSerializationTests.cs index 8bfc404..87f9c7a 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/TextGenerationSerializationTests.cs +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/TextGenerationSerializationTests.cs @@ -220,7 +220,8 @@ public async Task ConversationCompletion_DeepResearchSse_SuccessAsync( Snapshots.TextGeneration.MessageFormat.SingleMessageIncremental, Snapshots.TextGeneration.MessageFormat.SingleMessageReasoningIncremental, Snapshots.TextGeneration.MessageFormat.SingleMessageWebSearchIncremental, - Snapshots.TextGeneration.MessageFormat.SingleMessageWithToolsIncremental); + Snapshots.TextGeneration.MessageFormat.SingleMessageWithToolsIncremental, + Snapshots.TextGeneration.MessageFormat.SingleMessageWithCodeInterpreterIncremental); public static readonly TheoryData, ModelResponse>> ConversationMessageFormatSseData = new( diff --git a/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.request.body.json b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.request.body.json new file mode 100644 index 0000000..099f90c --- /dev/null +++ b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.request.body.json @@ -0,0 +1,17 @@ +{ + "model": "qwen3-max-preview", + "input": { + "messages": [ + { + "role": "user", + "content": "123的21次方是多少?" + } + ] + }, + "parameters": { + "result_format": "message", + "enable_thinking": true, + "enable_code_interpreter": true, + "incremental_output": true + } +} diff --git a/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.request.header.txt b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.request.header.txt new file mode 100644 index 0000000..52b0b00 --- /dev/null +++ b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.request.header.txt @@ -0,0 +1,8 @@ +POST /api/v1/services/aigc/text-generation/generation HTTP/1.1 +Accept: text/event-stream +Content-Type: application/json +Cache-Control: no-cache +Host: dashscope.aliyuncs.com +Accept-Encoding: gzip, deflate, br +Connection: keep-alive +Content-Length: 394 diff --git a/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.response.body.txt b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.response.body.txt new file mode 100644 index 0000000..58959c2 --- /dev/null +++ b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.response.body.txt @@ -0,0 +1,280 @@ +id:1 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"用户","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":284,"output_tokens":3,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":1},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:2 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"问","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":285,"output_tokens":4,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":2},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:3 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"的是","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":286,"output_tokens":5,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":3},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:4 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"123","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":289,"output_tokens":8,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":6},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:5 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"的21次方是多少","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":295,"output_tokens":14,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":12},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:6 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"。这是一个数学计算问题,","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":301,"output_tokens":20,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":18},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:7 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"我需要计算123","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":307,"output_tokens":26,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":24},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:8 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"^21的","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":311,"output_tokens":30,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":28},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:9 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"值。\n\n我可以使用代码计算器","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":317,"output_tokens":36,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":34},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:10 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"工具来计算这个","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":321,"output_tokens":40,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":38},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:11 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"大数。我需要","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":326,"output_tokens":45,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":43},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:12 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"调用code_interpreter函数","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":332,"output_tokens":51,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":49},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:13 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":",传入计算","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":336,"output_tokens":55,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":53},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:14 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"123**","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":340,"output_tokens":59,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":57},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:15 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"21的Python代码。\n\n","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":346,"output_tokens":65,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":63},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:16 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"让我准备这个函数调","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":351,"output_tokens":70,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":68},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:17 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"用。","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":355,"output_tokens":74,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:18 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":364,"output_tokens":83,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:19 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":369,"output_tokens":88,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:20 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":373,"output_tokens":92,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:21 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":379,"output_tokens":98,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:22 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":383,"output_tokens":102,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:23 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}]},"usage":{"total_tokens":383,"output_tokens":102,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:24 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":383,"output_tokens":102,"input_tokens":281,"output_tokens_details":{"reasoning_tokens":69},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:25 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"用户","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":828,"output_tokens":105,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":70},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:26 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"询问","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":829,"output_tokens":106,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":71},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:27 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"1","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":830,"output_tokens":107,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":72},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:28 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"23的","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":833,"output_tokens":110,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":75},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:29 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"21次方是多少","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":838,"output_tokens":115,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":80},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:30 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":",我使用代码","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":842,"output_tokens":119,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":84},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:31 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"计算器计算出了结果。结果","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":848,"output_tokens":125,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":90},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:32 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"是一个非常大的数字","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":852,"output_tokens":129,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":94},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:33 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":":7726","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":857,"output_tokens":134,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":99},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:34 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"936446","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":863,"output_tokens":140,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":105},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:35 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"654986","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":869,"output_tokens":146,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":111},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:36 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"565307","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":875,"output_tokens":152,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":117},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:37 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"347338","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":881,"output_tokens":158,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":123},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:38 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"803006","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":887,"output_tokens":164,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":129},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:39 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"152221","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":893,"output_tokens":170,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":135},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:40 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"1723\n\n","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":898,"output_tokens":175,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":140},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:41 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"我需要将这个","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":902,"output_tokens":179,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":144},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:42 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"结果清晰地呈现给","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":907,"output_tokens":184,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":149},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:43 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"12","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":913,"output_tokens":190,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:44 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"3的21次方","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":919,"output_tokens":196,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:45 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"是:\n\n772","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":924,"output_tokens":201,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:46 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"693644","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":930,"output_tokens":207,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:47 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"665498","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":936,"output_tokens":213,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:48 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"656530","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":942,"output_tokens":219,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:49 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"734733","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":948,"output_tokens":225,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:50 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"880300","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":954,"output_tokens":231,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:51 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"615222","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":960,"output_tokens":237,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:52 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"11723\n\n","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":966,"output_tokens":243,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:53 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"这是一个非常大的数字","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":970,"output_tokens":247,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:54 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":",共有42位数","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":976,"output_tokens":253,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:55 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"。","reasoning_content":"","role":"assistant"},"finish_reason":"null"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":977,"output_tokens":254,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + +id:56 +event:result +:HTTP_STATUS/200 +data:{"output":{"choices":[{"message":{"content":"","reasoning_content":"","role":"assistant"},"finish_reason":"stop"}],"tool_info":[{"code_interpreter":{"code":"123**21"},"type":"code_interpreter"}]},"usage":{"total_tokens":977,"output_tokens":254,"input_tokens":723,"output_tokens_details":{"reasoning_tokens":150},"plugins":{"code_interpreter":{"count":1}},"prompt_tokens_details":{"cached_tokens":0}},"request_id":"752a7de3-d3aa-4aeb-82ab-a8b08b41524b"} + diff --git a/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.response.header.txt b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.response.header.txt new file mode 100644 index 0000000..4a62628 --- /dev/null +++ b/test/Cnblogs.DashScope.Tests.Shared/RawHttpData/single-generation-message-with-code-interpreter-sse.response.header.txt @@ -0,0 +1,14 @@ +HTTP/1.1 200 OK +vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers +x-request-id: 752a7de3-d3aa-4aeb-82ab-a8b08b41524b +content-type: text/event-stream;charset=UTF-8 +x-dashscope-call-gateway: true +x-dashscope-timeout: 298 +x-dashscope-finished: false +req-cost-time: 901 +req-arrive-time: 1764771416660 +resp-start-time: 1764771417561 +x-envoy-upstream-service-time: 893 +date: Wed, 03 Dec 2025 14:16:57 GMT +server: istio-envoy +transfer-encoding: chunked diff --git a/test/Cnblogs.DashScope.Tests.Shared/Utils/RequestSnapshot.cs b/test/Cnblogs.DashScope.Tests.Shared/Utils/RequestSnapshot.cs index 63cbf5e..19ef073 100644 --- a/test/Cnblogs.DashScope.Tests.Shared/Utils/RequestSnapshot.cs +++ b/test/Cnblogs.DashScope.Tests.Shared/Utils/RequestSnapshot.cs @@ -17,6 +17,12 @@ public string GetRequestJson(bool sse) public List GetRequestForm(bool sse) { var body = GetRequestBody(sse); + if (body.Contains("\r\n") == false) + { + // update CRLF + body = body.Replace("\n", "\r\n"); + } + var blocks = body .Split($"--{Boundary}", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) .SkipLast(1) @@ -57,13 +63,15 @@ public string GetRequestBody(bool sse, string ext = "txt") public HttpMethod GetRequestMethod(bool sse) { - var firstLine = File.ReadAllLines(Path.Combine("RawHttpData", $"{GetSnapshotCaseName(sse)}.request.header.txt"))[0]; + var firstLine = + File.ReadAllLines(Path.Combine("RawHttpData", $"{GetSnapshotCaseName(sse)}.request.header.txt"))[0]; return new HttpMethod(firstLine.Split(' ')[0]); } public string GetRequestPathAndQuery(bool sse) { - var firstLine = File.ReadAllLines(Path.Combine("RawHttpData", $"{GetSnapshotCaseName(sse)}.request.header.txt"))[0]; + var firstLine = + File.ReadAllLines(Path.Combine("RawHttpData", $"{GetSnapshotCaseName(sse)}.request.header.txt"))[0]; return firstLine.Split(' ')[1]; } } diff --git a/test/Cnblogs.DashScope.Tests.Shared/Utils/Snapshots.TextGeneration.cs b/test/Cnblogs.DashScope.Tests.Shared/Utils/Snapshots.TextGeneration.cs index af7698d..cdafc36 100644 --- a/test/Cnblogs.DashScope.Tests.Shared/Utils/Snapshots.TextGeneration.cs +++ b/test/Cnblogs.DashScope.Tests.Shared/Utils/Snapshots.TextGeneration.cs @@ -268,13 +268,13 @@ public static partial class MessageFormat FinishReason = "stop", Message = TextChatMessage.Assistant("2"), Logprobs = new TextGenerationLogprobs( - new List() + new List { new( "2", new byte[] { 50 }, 0.0f, - new List() + new List { new("2", new byte[] { 50 }, 0.0f) }), @@ -297,12 +297,12 @@ public static readonly ModelResponse> SingleMessageRolePlay = new( "single-generation-message-roleplay", - new ModelRequest() + new ModelRequest { Model = "qwen-plus-character", - Input = new TextGenerationInput() + Input = new TextGenerationInput { - Messages = new List() + Messages = new List { TextChatMessage.System( "你是江让,男性,从 3 岁起你就入门编程,小学就开始研习算法,初一时就已经在算法竞赛斩获全国金牌。目前你在初二年级,作为老师的助教帮忙辅导初一的竞赛生。\n你的性格特点:聪明,早慧,一路畅通的你有时很难理解其他人为什么连这么简单的问题都不会做,但除开编程范围之外,你还是一个普通的初二学生。\n你的行事风格:在编程方面乐于助人,会将自己的知识的倾囊相授,虽然问的人并不一定能跟上你的思路。\n你可以将动作、神情语气、心理活动、故事背景放在()中来表示,为对话提供补充信息。"), @@ -310,7 +310,7 @@ public static readonly TextChatMessage.User("我是蒟蒻,还在准备模拟赛。你能教我 splay 树怎么写吗?") }, }, - Parameters = new TextGenerationParameters() + Parameters = new TextGenerationParameters { ResultFormat = "message", N = 2, @@ -323,11 +323,11 @@ public static readonly } } }, - new ModelResponse() + new ModelResponse { - Output = new TextGenerationOutput() + Output = new TextGenerationOutput { - Choices = new List() + Choices = new List { new() { @@ -347,7 +347,7 @@ public static readonly } } }, - Usage = new TextGenerationTokenUsage() + Usage = new TextGenerationTokenUsage { InputTokens = 186, OutputTokens = 83, @@ -374,13 +374,13 @@ public static readonly { ResultFormat = "message", IncrementalOutput = false, - TranslationOptions = new TextGenerationTranslationOptions() + TranslationOptions = new TextGenerationTranslationOptions { SourceLang = "Chinese", TargetLang = "English", Domains = "This text is a promotion.", - Terms = new List() { new("博客园", "cnblogs") }, - TmList = new List() { new("代码改变世界", "Coding changes world") } + Terms = new List { new("博客园", "cnblogs") }, + TmList = new List { new("代码改变世界", "Coding changes world") } } } }, @@ -426,7 +426,7 @@ public static readonly { ResultFormat = "message", EnableSearch = true, - SearchOptions = new TextGenerationSearchOptions() + SearchOptions = new TextGenerationSearchOptions { EnableSource = true, EnableCitation = true, @@ -452,7 +452,7 @@ public static readonly } }, SearchInfo = new TextGenerationWebSearchInfo( - new List() + new List { new( "无", @@ -485,7 +485,7 @@ public static readonly "阿里巴巴投資者關係-阿里巴巴集團", "https://www.alibabagroup.com/zh-HK/investor-relations") }, - new List() + new List { new( "阿里巴巴美股:\n实时价格167.05USD\n上个交易日收盘价165.09USD\n日环比%1.19%\n月环比%-6.53\n日同比%66.33\n月同比%74.05\n历史价格列表[{\"date\":\"2025-10-17\",\"endPri\":\"167.050\"},{\"date\":\"2025-10-16\",\"endPri\":\"165.090\"},{\"date\":\"2025-10-15\",\"endPri\":\"165.910\"},{\"date\":\"2025-10-14\",\"endPri\":\"162.860\"},{\"date\":\"2025-10-13\",\"endPri\":\"166.810\"},{\"date\":\"2025-10-10\",\"endPri\":\"159.010\"},{\"date\":\"2025-10-09\",\"endPri\":\"173.680\"},{\"date\":\"2025-10-08\",\"endPri\":\"181.120\"},{\"date\":\"2025-10-07\",\"endPri\":\"181.330\"},{\"date\":\"2025-10-06\",\"endPri\":\"187.220\"},{\"date\":\"2025-10-03\",\"endPri\":\"188.030\"},{\"date\":\"2025-10-02\",\"endPri\":\"189.340\"},{\"date\":\"2025-10-01\",\"endPri\":\"182.780\"},{\"date\":\"2025-09-30\",\"endPri\":\"178.730\"},{\"date\":\"2025-09-29\",\"endPri\":\"179.900\"},{\"date\":\"2025-09-26\",\"endPri\":\"171.910\"},{\"date\":\"2025-09-25\",\"endPri\":\"175.470\"},{\"date\":\"2025-09-24\",\"endPri\":\"176.440\"},{\"date\":\"2025-09-23\",\"endPri\":\"163.080\"},{\"date\":\"2025-09-22\",\"endPri\":\"164.250\"},{\"date\":\"2025-09-19\",\"endPri\":\"162.810\"},{\"date\":\"2025-09-18\",\"endPri\":\"162.480\"},{\"date\":\"2025-09-17\",\"endPri\":\"166.170\"},{\"date\":\"2025-09-16\",\"endPri\":\"162.210\"},{\"date\":\"2025-09-15\",\"endPri\":\"158.040\"},{\"date\":\"2025-09-12\",\"endPri\":\"155.060\"},{\"date\":\"2025-09-11\",\"endPri\":\"155.440\"},{\"date\":\"2025-09-10\",\"endPri\":\"143.930\"},{\"date\":\"2025-09-09\",\"endPri\":\"147.100\"},{\"date\":\"2025-09-08\",\"endPri\":\"141.200\"}]\n\n", @@ -507,19 +507,19 @@ public static readonly ModelResponse> SingleMessageWebSearchIncremental = new( "single-generation-message-search", - new ModelRequest() + new ModelRequest { Model = "qwen-plus", - Input = new TextGenerationInput() + Input = new TextGenerationInput { - Messages = new List() { TextChatMessage.User("杭州明天的天气") } + Messages = new List { TextChatMessage.User("杭州明天的天气") } }, - Parameters = new TextGenerationParameters() + Parameters = new TextGenerationParameters { ResultFormat = "message", EnableSearch = true, IncrementalOutput = true, - SearchOptions = new TextGenerationSearchOptions() + SearchOptions = new TextGenerationSearchOptions { ForcedSearch = true, EnableSource = true, @@ -528,12 +528,12 @@ public static readonly } } }, - new ModelResponse() + new ModelResponse { - Output = new TextGenerationOutput() + Output = new TextGenerationOutput { SearchInfo = new TextGenerationWebSearchInfo( - new List() + new List { new( "厦门时空科技有限公司", @@ -567,7 +567,7 @@ public static readonly "https://tianqi.eastday.com/lishi/hangzhou.html"), }, null), - Choices = new List() + Choices = new List { new() { @@ -577,7 +577,7 @@ public static readonly } } }, - Usage = new TextGenerationTokenUsage() + Usage = new TextGenerationTokenUsage { TotalTokens = 810, InputTokens = 709, @@ -870,6 +870,70 @@ public static readonly } }); + public static readonly + RequestSnapshot, + ModelResponse> + SingleMessageWithCodeInterpreterIncremental = + new( + "single-generation-message-with-code-interpreter", + new ModelRequest + { + Model = "qwen3-max-preview", + Input = new TextGenerationInput + { + Messages = + new List { TextChatMessage.User("123的21次方是多少?"), } + }, + Parameters = new TextGenerationParameters + { + ResultFormat = "message", + IncrementalOutput = true, + EnableThinking = true, + EnableCodeInterpreter = true + } + }, + new ModelResponse + { + Output = new TextGenerationOutput + { + Choices = + new List + { + new() + { + FinishReason = "stop", + Message = + TextChatMessage.Assistant( + "123的21次方是:\n\n77269364466549865653073473388030061522211723\n\n这是一个非常大的数字,共有42位数。", + null, + null, + "用户问的是123的21次方是多少。这是一个数学计算问题,我需要计算123^21的值。\n\n我可以使用代码计算器工具来计算这个大数。我需要调用code_interpreter函数,传入计算123**21的Python代码。\n\n让我准备这个函数调用。用户询问123的21次方是多少,我使用代码计算器计算出了结果。结果是一个非常大的数字:77269364466549865653073473388030061522211723\n\n我需要将这个结果清晰地呈现给") + } + }, + ToolInfo = + new List + { + new() + { + Type = "code_interpreter", + CodeInterpreter = + new ToolInfoCodeInterpreterOutput { Code = "123**21" } + } + } + }, + RequestId = "752a7de3-d3aa-4aeb-82ab-a8b08b41524b", + Usage = new TextGenerationTokenUsage + { + InputTokens = 723, + OutputTokens = 254, + TotalTokens = 977, + OutputTokensDetails = new TextGenerationOutputTokenDetails(150), + Plugins = new TextGenerationPluginUsages( + codeInterpreter: new TextGenerationCodeInterpreterPluginUsage(1)), + PromptTokensDetails = new TextGenerationPromptTokenDetails(0) + } + }); + public static readonly RequestSnapshot, ModelResponse> SingleMessageWithToolsIncremental = @@ -1129,12 +1193,12 @@ public static readonly ModelResponse> ConversationMessageWithDocUrlsIncremental = new( "conversation-generation-message-with-doc-url", - new ModelRequest() + new ModelRequest { Model = "qwen-doc-turbo", - Input = new TextGenerationInput() + Input = new TextGenerationInput { - Messages = new List() + Messages = new List { TextChatMessage.System("You are a helpful assistant."), TextChatMessage.DocUrl( @@ -1146,17 +1210,14 @@ public static readonly }) } }, - Parameters = new TextGenerationParameters() - { - ResultFormat = "message", IncrementalOutput = true - } + Parameters = new TextGenerationParameters { ResultFormat = "message", IncrementalOutput = true } }, - new ModelResponse() + new ModelResponse { RequestId = "ee1a01a9-4c9e-4729-ae35-f5948124b302", - Output = new TextGenerationOutput() + Output = new TextGenerationOutput { - Choices = new List() + Choices = new List { new() { @@ -1166,7 +1227,7 @@ public static readonly } } }, - Usage = new TextGenerationTokenUsage() + Usage = new TextGenerationTokenUsage { TotalTokens = 2180, OutputTokens = 354, diff --git a/test/Cnblogs.DashScope.Tests.Shared/Utils/Sut.cs b/test/Cnblogs.DashScope.Tests.Shared/Utils/Sut.cs index a24149e..c390bcf 100644 --- a/test/Cnblogs.DashScope.Tests.Shared/Utils/Sut.cs +++ b/test/Cnblogs.DashScope.Tests.Shared/Utils/Sut.cs @@ -7,13 +7,13 @@ namespace Cnblogs.DashScope.Tests.Shared.Utils; public static class Sut { - public static async Task<(IDashScopeClient Client, MockHttpMessageHandler Handler)> GetTestClientAsync( + public static Task<(IDashScopeClient Client, MockHttpMessageHandler Handler)> GetTestClientAsync( HttpResponseMessage response) { var pair = GetTestClient(); pair.Handler.Configure().MockSend(Arg.Any(), Arg.Any()) .Returns(response); - return pair; + return Task.FromResult<(IDashScopeClient Client, MockHttpMessageHandler Handler)>(pair); } public static async Task<(IDashScopeClient Client, MockHttpMessageHandler Handler)> GetTestClientAsync(