Skip to content

Commit 84d7c8d

Browse files
authored
feat: implement text embedding, text generation and chat completion api (#10)
1 parent 57a0142 commit 84d7c8d

27 files changed

+1067
-709
lines changed

.editorconfig

Lines changed: 159 additions & 135 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 55 additions & 229 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,51 @@ Semantic Kernel Connector to DashScope
33

44
## Get started
55
Add the NuGet package to your project.
6-
```shell
6+
```shell
77
dotnet add package Cnblogs.SemanticKernel.Connectors.DashScope
88
```
99

10-
Add the `dashscope` section to the appsettings.json file.
11-
```json
12-
{
13-
"dashscope": {
14-
"modelId": "qwen-max"
15-
}
16-
}
17-
```
18-
Add the api key to the user-secrets.
19-
```shell
20-
dotnet user-secrets init
21-
dotnet user-secrets set "dashscope:apiKey" "sk-xxx"
22-
```
23-
## Usage
24-
Program.cs
2510
```cs
2611
using Microsoft.SemanticKernel;
2712

2813
var builder = Kernel.CreateBuilder();
29-
builder.AddDashScopeChatCompletion<Program>();
14+
builder.Services.AddDashScopeChatCompletion("your-api-key", "qwen-max");
3015
var kernel = builder.Build();
3116

32-
var prompt = @"<message role=""user"">Tell me about the Cnblogs</message>";
33-
var result = await kernel.InvokePromptAsync(prompt);
34-
Console.WriteLine(result);
17+
var prompt = "<message role=\"user\">Tell me about the Cnblogs</message>";
18+
var response = await kernel.InvokePromptAsync(prompt);
19+
Console.WriteLine(response);
20+
```
21+
22+
## ASP.NET Core
23+
24+
`appsettings.json`
3525

36-
public partial class Program
37-
{ }
26+
```json
27+
{
28+
"dashScope": {
29+
"apiKey": "your-key",
30+
"chatCompletionModelId": "qwen-max",
31+
"textEmbeddingModelId": "text-embedding-v2"
32+
}
33+
}
34+
```
35+
36+
`Program.cs`
37+
```csharp
38+
builder.Services.AddDashScopeChatCompletion(builder.Configuration);
39+
builder.Services.AddScoped<Kernel>(sp => new Kernel(sp));
3840
```
3941

42+
Services
43+
44+
```csharp
45+
public class YourService(Kernel kernel)
46+
{
47+
public async Task<string> GetCompletionAsync(string prompt)
48+
{
49+
var chatResult = await kernel.InvokePromptAsync(prompt);
50+
return chatResult.ToString();
51+
}
52+
}
53+
```

SemanticKernel.DashScope.sln

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1414
EndProject
1515
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DAB267DF-F966-4F95-AD12-56CC78D6F274}"
1616
EndProject
17-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticKernel.DashScope.IntegrationTest", "test\SemanticKernel.DashScope.IntegrationTest\SemanticKernel.DashScope.IntegrationTest.csproj", "{FA28DF4A-5A25-46C5-B2BE-614C30797B23}"
17+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticKernel.DashScope.UnitTest", "test\SemanticKernel.DashScope.UnitTest\SemanticKernel.DashScope.UnitTest.csproj", "{648EBC1D-9409-4205-905E-DD06E4443AAA}"
1818
EndProject
1919
Global
2020
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -26,17 +26,17 @@ Global
2626
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
2727
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
2828
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}.Release|Any CPU.Build.0 = Release|Any CPU
29-
{FA28DF4A-5A25-46C5-B2BE-614C30797B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30-
{FA28DF4A-5A25-46C5-B2BE-614C30797B23}.Debug|Any CPU.Build.0 = Debug|Any CPU
31-
{FA28DF4A-5A25-46C5-B2BE-614C30797B23}.Release|Any CPU.ActiveCfg = Release|Any CPU
32-
{FA28DF4A-5A25-46C5-B2BE-614C30797B23}.Release|Any CPU.Build.0 = Release|Any CPU
29+
{648EBC1D-9409-4205-905E-DD06E4443AAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30+
{648EBC1D-9409-4205-905E-DD06E4443AAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
31+
{648EBC1D-9409-4205-905E-DD06E4443AAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
32+
{648EBC1D-9409-4205-905E-DD06E4443AAA}.Release|Any CPU.Build.0 = Release|Any CPU
3333
EndGlobalSection
3434
GlobalSection(SolutionProperties) = preSolution
3535
HideSolutionNode = FALSE
3636
EndGlobalSection
3737
GlobalSection(NestedProjects) = preSolution
3838
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5} = {BB73FA18-BBBE-4C34-971A-D4206FC118A2}
39-
{FA28DF4A-5A25-46C5-B2BE-614C30797B23} = {DAB267DF-F966-4F95-AD12-56CC78D6F274}
39+
{648EBC1D-9409-4205-905E-DD06E4443AAA} = {DAB267DF-F966-4F95-AD12-56CC78D6F274}
4040
EndGlobalSection
4141
GlobalSection(ExtensibilityGlobals) = postSolution
4242
SolutionGuid = {D0F2E9A4-9782-4C3F-B459-CFCAD4C9AD9F}
Lines changed: 110 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,144 @@
11
using System.Runtime.CompilerServices;
2-
using Microsoft.Extensions.Options;
2+
using Cnblogs.DashScope.Sdk;
33
using Microsoft.SemanticKernel;
44
using Microsoft.SemanticKernel.ChatCompletion;
55
using Microsoft.SemanticKernel.Services;
6-
using Sdcb.DashScope;
7-
using Sdcb.DashScope.TextGeneration;
6+
using Microsoft.SemanticKernel.TextGeneration;
87

98
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
109

11-
public sealed class DashScopeChatCompletionService : IChatCompletionService
10+
/// <summary>
11+
/// DashScope chat completion service.
12+
/// </summary>
13+
public sealed class DashScopeChatCompletionService : IChatCompletionService, ITextGenerationService
1214
{
13-
private readonly DashScopeClient _dashScopeClient;
15+
private readonly IDashScopeClient _dashScopeClient;
16+
private readonly Dictionary<string, object?> _attributes = new();
1417
private readonly string _modelId;
15-
private readonly Dictionary<string, object?> _attribues = [];
1618

17-
public DashScopeChatCompletionService(
18-
IOptions<DashScopeClientOptions> options,
19-
HttpClient httpClient)
19+
/// <summary>
20+
/// Creates a new DashScope chat completion service.
21+
/// </summary>
22+
/// <param name="modelId"></param>
23+
/// <param name="dashScopeClient"></param>
24+
public DashScopeChatCompletionService(string modelId, IDashScopeClient dashScopeClient)
2025
{
21-
_dashScopeClient = new(options.Value.ApiKey, httpClient);
22-
_modelId = options.Value.ModelId;
23-
_attribues.Add(AIServiceExtensions.ModelIdKey, _modelId);
26+
_dashScopeClient = dashScopeClient;
27+
_modelId = modelId;
28+
_attributes.Add(AIServiceExtensions.ModelIdKey, _modelId);
2429
}
2530

26-
public IReadOnlyDictionary<string, object?> Attributes => _attribues;
27-
28-
public async Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, CancellationToken cancellationToken = default)
31+
/// <inheritdoc />
32+
public async Task<IReadOnlyList<ChatMessageContent>> GetChatMessageContentsAsync(
33+
ChatHistory chatHistory,
34+
PromptExecutionSettings? executionSettings = null,
35+
Kernel? kernel = null,
36+
CancellationToken cancellationToken = default)
2937
{
3038
var chatMessages = chatHistory.ToChatMessages();
31-
var chatParameters = executionSettings?.ToChatParameters();
32-
var response = await _dashScopeClient.TextGeneration.Chat(_modelId, chatMessages, chatParameters, cancellationToken);
39+
var chatParameters = DashScopePromptExecutionSettings.FromPromptExecutionSettings(executionSettings);
40+
chatParameters ??= new DashScopePromptExecutionSettings();
41+
chatParameters.IncrementalOutput = false;
42+
chatParameters.ResultFormat = ResultFormats.Message;
43+
var response = await _dashScopeClient.GetTextCompletionAsync(
44+
new ModelRequest<TextGenerationInput, ITextGenerationParameters>
45+
{
46+
Input = new TextGenerationInput { Messages = chatMessages },
47+
Model = string.IsNullOrEmpty(chatParameters.ModelId) ? _modelId : chatParameters.ModelId,
48+
Parameters = chatParameters
49+
},
50+
cancellationToken);
51+
var message = response.Output.Choices![0].Message;
3352
var chatMessageContent = new ChatMessageContent(
34-
new AuthorRole(chatMessages[0].Role),
35-
response.Output.Text,
36-
metadata: response.Usage.ToMetadata());
53+
new AuthorRole(message.Role),
54+
message.Content,
55+
metadata: response.ToMetaData());
3756
return [chatMessageContent];
3857
}
3958

59+
/// <inheritdoc />
4060
public async IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(
4161
ChatHistory chatHistory,
4262
PromptExecutionSettings? executionSettings = null,
4363
Kernel? kernel = null,
4464
[EnumeratorCancellation] CancellationToken cancellationToken = default)
4565
{
4666
var chatMessages = chatHistory.ToChatMessages();
47-
var chatParameters = executionSettings?.ToChatParameters() ?? new ChatParameters();
48-
chatParameters.IncrementalOutput = true;
67+
executionSettings ??= new DashScopePromptExecutionSettings();
68+
var parameters = DashScopePromptExecutionSettings.FromPromptExecutionSettings(executionSettings);
69+
parameters.IncrementalOutput = true;
70+
parameters.ResultFormat = ResultFormats.Message;
71+
var responses = _dashScopeClient.GetTextCompletionStreamAsync(
72+
new ModelRequest<TextGenerationInput, ITextGenerationParameters>
73+
{
74+
Input = new TextGenerationInput { Messages = chatMessages },
75+
Model = string.IsNullOrEmpty(parameters.ModelId) ? _modelId : parameters.ModelId,
76+
Parameters = parameters
77+
},
78+
cancellationToken);
4979

50-
var responses = _dashScopeClient.TextGeneration.ChatStreamed(_modelId, chatMessages, chatParameters, cancellationToken);
5180
await foreach (var response in responses)
5281
{
82+
var message = response.Output.Choices![0].Message;
5383
yield return new StreamingChatMessageContent(
54-
new AuthorRole(chatMessages[0].Role),
84+
new AuthorRole(message.Role),
85+
message.Content,
86+
modelId: _modelId,
87+
metadata: response.ToMetaData());
88+
}
89+
}
90+
91+
/// <inheritdoc />
92+
public IReadOnlyDictionary<string, object?> Attributes => _attributes;
93+
94+
/// <inheritdoc />
95+
public async Task<IReadOnlyList<TextContent>> GetTextContentsAsync(
96+
string prompt,
97+
PromptExecutionSettings? executionSettings = null,
98+
Kernel? kernel = null,
99+
CancellationToken cancellationToken = new())
100+
{
101+
var chatParameters = DashScopePromptExecutionSettings.FromPromptExecutionSettings(executionSettings);
102+
chatParameters ??= new DashScopePromptExecutionSettings();
103+
chatParameters.IncrementalOutput = false;
104+
chatParameters.ResultFormat = ResultFormats.Text;
105+
var response = await _dashScopeClient.GetTextCompletionAsync(
106+
new ModelRequest<TextGenerationInput, ITextGenerationParameters>
107+
{
108+
Input = new TextGenerationInput { Prompt = prompt },
109+
Model = string.IsNullOrEmpty(chatParameters.ModelId) ? _modelId : chatParameters.ModelId,
110+
Parameters = chatParameters
111+
},
112+
cancellationToken);
113+
return [new TextContent(response.Output.Text, _modelId, metadata: response.ToMetaData())];
114+
}
115+
116+
/// <inheritdoc />
117+
public async IAsyncEnumerable<StreamingTextContent> GetStreamingTextContentsAsync(
118+
string prompt,
119+
PromptExecutionSettings? executionSettings = null,
120+
Kernel? kernel = null,
121+
[EnumeratorCancellation] CancellationToken cancellationToken = new())
122+
{
123+
executionSettings ??= new DashScopePromptExecutionSettings();
124+
var parameters = DashScopePromptExecutionSettings.FromPromptExecutionSettings(executionSettings);
125+
parameters.IncrementalOutput = true;
126+
parameters.ResultFormat = ResultFormats.Text;
127+
var responses = _dashScopeClient.GetTextCompletionStreamAsync(
128+
new ModelRequest<TextGenerationInput, ITextGenerationParameters>
129+
{
130+
Input = new TextGenerationInput { Prompt = prompt },
131+
Model = string.IsNullOrEmpty(parameters.ModelId) ? _modelId : parameters.ModelId,
132+
Parameters = parameters
133+
},
134+
cancellationToken);
135+
136+
await foreach (var response in responses)
137+
{
138+
yield return new StreamingTextContent(
55139
response.Output.Text,
56-
metadata: response.Usage.ToMetadata());
140+
modelId: _modelId,
141+
metadata: response.ToMetaData());
57142
}
58143
}
59144
}

src/SemanticKernel.DashScope/DashScopeClientOptions.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Cnblogs.DashScope.Sdk;
2+
using Microsoft.SemanticKernel.ChatCompletion;
3+
4+
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
5+
6+
internal static class DashScopeMapper
7+
{
8+
public static List<ChatMessage> ToChatMessages(this ChatHistory history)
9+
{
10+
return history.Select(x => new ChatMessage(x.Role.Label, x.Content ?? string.Empty)).ToList();
11+
}
12+
13+
public static Dictionary<string, object?>? ToMetaData<TOutput, TUsage>(
14+
this ModelResponse<TOutput, TUsage>? response)
15+
where TUsage : class
16+
where TOutput : class
17+
{
18+
return response == null
19+
? null
20+
: new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
21+
{
22+
{ "Usage", response.Usage }, { "RequestId", response.RequestId }
23+
};
24+
}
25+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
2+
3+
/// <summary>
4+
/// Options about DashScopeClient
5+
/// </summary>
6+
public class DashScopeOptions
7+
{
8+
/// <summary>
9+
/// The text embedding model id.
10+
/// </summary>
11+
public string TextEmbeddingModelId { get; set; } = string.Empty;
12+
13+
/// <summary>
14+
/// Default model name for chat completion.
15+
/// </summary>
16+
public string ChatCompletionModelId { get; set; } = string.Empty;
17+
18+
/// <summary>
19+
/// The DashScope api key.
20+
/// </summary>
21+
public string ApiKey { get; set; } = string.Empty;
22+
}

0 commit comments

Comments
 (0)