Skip to content

Commit 2ccade3

Browse files
committed
feat: support sending raw message directly via M.E.AI (#156)
* feat: support sending raw message directly * fix: enable ci for staging * docs: update sample namespace * fix: sample naming
1 parent d4f10e2 commit 2ccade3

File tree

14 files changed

+514
-101
lines changed

14 files changed

+514
-101
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ on:
44
push:
55
branches: [ "main" ]
66
pull_request:
7-
branches: [ "main" ]
7+
branches: [ "main", "staging" ]
88

99
jobs:
1010
test-net6:
1111
runs-on: ubuntu-latest
1212
container: mcr.microsoft.com/dotnet/sdk:6.0
13-
1413
steps:
1514
- name: Checkout
1615
uses: actions/checkout@v4

README.md

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,6 @@ A non-official DashScope (Bailian) service SDK maintained by Cnblogs.
1212

1313
## Quick Start
1414

15-
### Using `Microsoft.Extensions.AI` Interface
16-
17-
Install NuGet package `Cnblogs.DashScope.AI`
18-
```csharp
19-
var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max");
20-
var completion = await client.CompleteAsync("hello");
21-
Console.WriteLine(completion)
22-
```
23-
2415
### Console Application
2516

2617
Install NuGet package `Cnblogs.DashScope.Sdk`
@@ -90,7 +81,93 @@ public class YourService(IDashScopeClient client)
9081
}
9182
}
9283
```
84+
### Using `Microsoft.Extensions.AI` Interface
85+
86+
Install NuGet package `Cnblogs.DashScope.AI`
87+
88+
```csharp
89+
var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max");
90+
var completion = await client.GetResponseAsync("hello");
91+
Console.WriteLine(completion.Text);
92+
```
93+
94+
#### Fallback to raw messages
95+
96+
If you need to use input data or parameters not supported by `Microsoft.Extensions.AI`, you can directly invoke the underlying SDK by passing a raw `TextChatMessage` or `MultimodalMessage` via `RawPresentation`.
97+
98+
Similarly, to pass unsupported parameters, you can also do so directly by setting the `raw` property within `AdditionalProperties`.
99+
100+
Example(Using `qwen-doc-turbo`)
101+
102+
```csharp
103+
var messages = new List<TextChatMessage>()
104+
{
105+
TextChatMessage.DocUrl(
106+
"从这两份产品手册中,提取所有产品信息,并整理成一个标准的JSON数组。每个对象需要包含:model(产品的型号)、name(产品的名称)、price(价格(去除货币符号和逗号))",
107+
[
108+
"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",
109+
"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"
110+
])
111+
};
112+
var parameters = new TextGenerationParameters()
113+
{
114+
ResultFormat = "message", IncrementalOutput = true,
115+
};
116+
117+
var response = client
118+
.AsChatClient("qwen-doc-turbo")
119+
.GetStreamingResponseAsync(
120+
messages.Select(x => new ChatMessage() { RawRepresentation = x }),
121+
new ChatOptions()
122+
{
123+
AdditionalProperties = new AdditionalPropertiesDictionary() { { "raw", parameters } }
124+
});
125+
await foreach (var chunk in response)
126+
{
127+
Console.Write(chunk.Text);
128+
}
129+
```
130+
131+
Similarly, you can also retrieve the raw message from the `RawPresentation` in the message returned by the model.
132+
133+
Example (Getting the token count for an image when calling `qwen3-vl-plus`):
134+
135+
```csharp
136+
var response = client
137+
.AsChatClient("qwen3-vl-plus")
138+
.GetStreamingResponseAsync(
139+
new List<ChatMessage>()
140+
{
141+
new(
142+
ChatRole.User,
143+
new List<AIContent>()
144+
{
145+
new UriContent(
146+
"https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg",
147+
MediaTypeNames.Image.Jpeg),
148+
new UriContent(
149+
"https://dashscope.oss-cn-beijing.aliyuncs.com/images/tiger.png",
150+
MediaTypeNames.Image.Jpeg),
151+
new TextContent("这些图展现了什么内容?")
152+
})
153+
},
154+
new ChatOptions());
155+
var lastChunk = (ChatResponseUpdate?)null;
156+
await foreach (var chunk in response)
157+
{
158+
Console.Write(chunk.Text);
159+
lastChunk = chunk;
160+
}
161+
162+
Console.WriteLine();
163+
164+
// Access underlying raw response
165+
var raw = lastChunk?.RawRepresentation as ModelResponse<MultimodalOutput, MultimodalTokenUsage>;
166+
Console.WriteLine($"Image token usage: {raw?.Usage?.ImageTokens}");
167+
```
168+
93169
## Supported APIs
170+
94171
- [Text Generation](#text-generation) - QWen3, DeepSeek, etc. Supports reasoning/tool calling/web search/translation scenarios
95172
- [Conversation](#conversation)
96173
- [Thinking Models](#thinking-models)

README.zh-Hans.md

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,6 @@
1212

1313
## 快速开始
1414

15-
### 使用 `Microsoft.Extensions.AI` 接口
16-
17-
安装 NuGet 包 `Cnblogs.DashScope.AI`
18-
19-
```csharp
20-
var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max");
21-
var completion = await client.CompleteAsync("hello");
22-
Console.WriteLine(completion)
23-
```
24-
2515
### 控制台应用
2616

2717
安装 NuGet 包 `Cnblogs.DashScope.Sdk`
@@ -88,6 +78,93 @@ public class YourService(IDashScopeClient client)
8878
}
8979
```
9080

81+
### 使用 `Microsoft.Extensions.AI` 接口
82+
83+
安装 NuGet 包 `Cnblogs.DashScope.AI`
84+
85+
```csharp
86+
var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max");
87+
var completion = await client.GetResponseAsync("hello");
88+
Console.WriteLine(completion.Text);
89+
```
90+
91+
#### 调用原始 SDK
92+
93+
如果需要使用 `Microsoft.Extensions.AI` 不支持的输入数据或参数,可以通过 `RawPresentation` 直接传入原始的 `TextChatMessage` 或者 `MultimodalMessage` 来直接调用底层 SDK。
94+
95+
类似地,当需要传入不支持的参数时,也可以通过设置 `AdditionalProperties` 里的 `raw` 直接传入原始参数。
96+
97+
示例(调用 `qwen-doc-turbo`
98+
99+
```csharp
100+
var messages = new List<TextChatMessage>()
101+
{
102+
TextChatMessage.DocUrl(
103+
"从这两份产品手册中,提取所有产品信息,并整理成一个标准的JSON数组。每个对象需要包含:model(产品的型号)、name(产品的名称)、price(价格(去除货币符号和逗号))",
104+
[
105+
"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",
106+
"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"
107+
])
108+
};
109+
var parameters = new TextGenerationParameters()
110+
{
111+
ResultFormat = "message", IncrementalOutput = true,
112+
};
113+
114+
var response = client
115+
.AsChatClient("qwen-doc-turbo")
116+
.GetStreamingResponseAsync(
117+
messages.Select(x => new ChatMessage() { RawRepresentation = x }),
118+
new ChatOptions()
119+
{
120+
AdditionalProperties = new AdditionalPropertiesDictionary() { { "raw", parameters } }
121+
});
122+
await foreach (var chunk in response)
123+
{
124+
Console.Write(chunk.Text);
125+
}
126+
```
127+
128+
类似地,也可以通过模型返回消息里的 `RawPresentation` 获取原始消息。
129+
130+
示例(调用 `qwen3-vl-plus` 时获取图像消耗的 Token 数):
131+
132+
```csharp
133+
var response = client
134+
.AsChatClient("qwen3-vl-plus")
135+
.GetStreamingResponseAsync(
136+
new List<ChatMessage>()
137+
{
138+
new(
139+
ChatRole.User,
140+
new List<AIContent>()
141+
{
142+
new UriContent(
143+
"https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg",
144+
MediaTypeNames.Image.Jpeg),
145+
new UriContent(
146+
"https://dashscope.oss-cn-beijing.aliyuncs.com/images/tiger.png",
147+
MediaTypeNames.Image.Jpeg),
148+
new TextContent("这些图展现了什么内容?")
149+
})
150+
},
151+
new ChatOptions());
152+
var lastChunk = (ChatResponseUpdate?)null;
153+
await foreach (var chunk in response)
154+
{
155+
Console.Write(chunk.Text);
156+
lastChunk = chunk;
157+
}
158+
159+
Console.WriteLine();
160+
161+
// 访问原始消息
162+
var raw = lastChunk?.RawRepresentation as ModelResponse<MultimodalOutput, MultimodalTokenUsage>;
163+
Console.WriteLine($"Image token usage: {raw?.Usage?.ImageTokens}");
164+
```
165+
166+
167+
91168
## 支持的 API
92169

93170
- [文本生成](#文本生成) - QWen3, DeepSeek 等,支持推理/工具调用/网络搜索/翻译等场景
@@ -97,6 +174,10 @@ public class YourService(IDashScopeClient client)
97174
- [工具调用](#工具调用)
98175
- [前缀续写](#前缀续写)
99176
- [长上下文(Qwen-Long)](#长上下文(Qwen-Long))
177+
- [翻译能力(Qwen-MT)](#翻译能力(Qwen-MT))
178+
- [角色扮演(Qwen-Character)](#角色扮演(Qwen-Character))
179+
- [数据挖掘(Qwen-doc-turbo)](#数据挖掘(Qwen-doc-turbo))
180+
- [深入研究(Qwen-Deep-Research)](#深入研究(Qwen-Deep-Research))
100181
- [多模态](#多模态) - QWen-VL,QVQ 等,支持推理/视觉理解/OCR/音频理解等场景
101182
- [视觉理解/推理](#视觉理解/推理) - 图像/视频输入与理解,支持推理模式
102183
- [文字提取](#文字提取) - OCR 任务,读取表格/文档/公式等
@@ -2670,7 +2751,7 @@ var completion = client.GetMultimodalGenerationAsync(
26702751
26712752
示例:
26722753
2673-
![倾斜的图像](sample/Cnblogs.DashScope.Sample/tilted.png)
2754+
![网页](sample/Cnblogs.DashScope.Sample/webpage.jpg)
26742755
26752756
```csharp
26762757
Console.WriteLine("Text:");
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using Cnblogs.DashScope.Core;
2+
using Microsoft.Extensions.AI;
3+
4+
namespace Cnblogs.DashScope.Sample.MsExtensionsAI;
5+
6+
public class RawInputExample : MsExtensionsAiSample
7+
{
8+
/// <inheritdoc />
9+
public override string Description => "Chat with raw message and parameter input";
10+
11+
/// <inheritdoc />
12+
public override async Task RunAsync(IDashScopeClient client)
13+
{
14+
var messages = new List<TextChatMessage>()
15+
{
16+
TextChatMessage.DocUrl(
17+
"从这两份产品手册中,提取所有产品信息,并整理成一个标准的JSON数组。每个对象需要包含:model(产品的型号)、name(产品的名称)、price(价格(去除货币符号和逗号))",
18+
[
19+
"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",
20+
"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"
21+
])
22+
};
23+
var parameters = new TextGenerationParameters()
24+
{
25+
ResultFormat = "message", IncrementalOutput = true,
26+
};
27+
28+
var response = client
29+
.AsChatClient("qwen-doc-turbo")
30+
.GetStreamingResponseAsync(
31+
messages.Select(x => new ChatMessage() { RawRepresentation = x }),
32+
new ChatOptions()
33+
{
34+
AdditionalProperties = new AdditionalPropertiesDictionary() { { "raw", parameters } }
35+
});
36+
await foreach (var chunk in response)
37+
{
38+
Console.Write(chunk.Text);
39+
}
40+
}
41+
}
42+
43+
/*
44+
```json
45+
[
46+
{
47+
"model": "PRO-100",
48+
"name": "智能打印机",
49+
"price": "8999"
50+
},
51+
{
52+
"model": "PRO-200",
53+
"name": "智能扫描仪",
54+
"price": "12999"
55+
},
56+
{
57+
"model": "PRO-300",
58+
"name": "智能会议系统",
59+
"price": "25999"
60+
},
61+
{
62+
"model": "PRO-400",
63+
"name": "智能考勤机",
64+
"price": "6999"
65+
},
66+
{
67+
"model": "PRO-500",
68+
"name": "智能文件柜",
69+
"price": "15999"
70+
},
71+
{
72+
"model": "SEC-100",
73+
"name": "智能监控摄像头",
74+
"price": "3999"
75+
},
76+
{
77+
"model": "SEC-200",
78+
"name": "智能门禁系统",
79+
"price": "15999"
80+
},
81+
{
82+
"model": "SEC-300",
83+
"name": "智能报警系统",
84+
"price": "28999"
85+
},
86+
{
87+
"model": "SEC-400",
88+
"name": "智能访客系统",
89+
"price": "9999"
90+
},
91+
{
92+
"model": "SEC-500",
93+
"name": "智能停车管理",
94+
"price": "22999"
95+
}
96+
]
97+
```
98+
*/

0 commit comments

Comments
 (0)