Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d90113f

Browse files
feat: implement DashScopeChatCompletionService (#1)
1 parent 032ac1b commit d90113f

13 files changed

+697
-0
lines changed

‎.editorconfig‎

Lines changed: 364 additions & 0 deletions
Large diffs are not rendered by default.

‎SemanticKernel.DashScope.sln‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SemanticKernel.DashScope", "src\SemanticKernel.DashScope\SemanticKernel.DashScope.csproj", "{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BB73FA18-BBBE-4C34-971A-D4206FC118A2}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6D288B49-252A-4ADB-A899-E36F21AA87DD}"
11+
ProjectSection(SolutionItems) = preProject
12+
.editorconfig = .editorconfig
13+
EndProjectSection
14+
EndProject
15+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DAB267DF-F966-4F95-AD12-56CC78D6F274}"
16+
EndProject
17+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticKernel.DashScope.IntegrationTest", "test\SemanticKernel.DashScope.IntegrationTest\SemanticKernel.DashScope.IntegrationTest.csproj", "{FA28DF4A-5A25-46C5-B2BE-614C30797B23}"
18+
EndProject
19+
Global
20+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
21+
Debug|Any CPU = Debug|Any CPU
22+
Release|Any CPU = Release|Any CPU
23+
EndGlobalSection
24+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
25+
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26+
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
27+
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
28+
{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
33+
EndGlobalSection
34+
GlobalSection(SolutionProperties) = preSolution
35+
HideSolutionNode = FALSE
36+
EndGlobalSection
37+
GlobalSection(NestedProjects) = preSolution
38+
{B9EF31C7-48D7-4CA8-8D15-D6340450D3F5} = {BB73FA18-BBBE-4C34-971A-D4206FC118A2}
39+
{FA28DF4A-5A25-46C5-B2BE-614C30797B23} = {DAB267DF-F966-4F95-AD12-56CC78D6F274}
40+
EndGlobalSection
41+
GlobalSection(ExtensibilityGlobals) = postSolution
42+
SolutionGuid = {D0F2E9A4-9782-4C3F-B459-CFCAD4C9AD9F}
43+
EndGlobalSection
44+
EndGlobal
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Runtime.CompilerServices;
2+
using Microsoft.Extensions.Options;
3+
using Microsoft.SemanticKernel;
4+
using Microsoft.SemanticKernel.ChatCompletion;
5+
using Microsoft.SemanticKernel.Services;
6+
using Sdcb.DashScope;
7+
using Sdcb.DashScope.TextGeneration;
8+
9+
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
10+
11+
public sealed class DashScopeChatCompletionService : IChatCompletionService
12+
{
13+
private readonly DashScopeClient _dashScopeClient;
14+
private readonly string _modelId;
15+
private readonly Dictionary<string, object?> _attribues = [];
16+
17+
public DashScopeChatCompletionService(
18+
IOptions<DashScopeClientOptions> options,
19+
HttpClient httpClient)
20+
{
21+
_dashScopeClient = new(options.Value.ApiKey, httpClient);
22+
_modelId = options.Value.ModelId;
23+
_attribues.Add(AIServiceExtensions.ModelIdKey, _modelId);
24+
}
25+
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)
29+
{
30+
var chatMessages = chatHistory.ToChatMessages();
31+
var chatParameters = executionSettings?.ToChatParameters();
32+
var response = await _dashScopeClient.TextGeneration.Chat(_modelId, chatMessages, chatParameters, cancellationToken);
33+
return [new ChatMessageContent(new AuthorRole(chatMessages[0].Role), response.Output.Text)];
34+
}
35+
36+
public async IAsyncEnumerable<StreamingChatMessageContent> GetStreamingChatMessageContentsAsync(
37+
ChatHistory chatHistory,
38+
PromptExecutionSettings? executionSettings = null,
39+
Kernel? kernel = null,
40+
[EnumeratorCancellation] CancellationToken cancellationToken = default)
41+
{
42+
var chatMessages = chatHistory.ToChatMessages();
43+
var chatParameters = executionSettings?.ToChatParameters() ?? new ChatParameters();
44+
chatParameters.IncrementalOutput = true;
45+
46+
var responses = _dashScopeClient.TextGeneration.ChatStreamed(_modelId, chatMessages, chatParameters, cancellationToken);
47+
48+
await foreach (var response in responses)
49+
{
50+
yield return new StreamingChatMessageContent(new AuthorRole(chatMessages[0].Role), response.Output.Text);
51+
}
52+
}
53+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.Extensions.Options;
2+
3+
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
4+
5+
public class DashScopeClientOptions : IOptions<DashScopeClientOptions>
6+
{
7+
public string ModelId { get; set; } = string.Empty;
8+
9+
public string ApiKey { get; set; } = string.Empty;
10+
11+
public DashScopeClientOptions Value => this;
12+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Cnblogs.SemanticKernel.Connectors.DashScope;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.SemanticKernel.ChatCompletion;
4+
5+
namespace Microsoft.SemanticKernel;
6+
7+
public static class DashScopeServiceCollectionExtensions
8+
{
9+
public static IKernelBuilder AddDashScopeChatCompletion(
10+
this IKernelBuilder builder,
11+
string? serviceId = null,
12+
Action<HttpClient>? configureClient = null,
13+
string configSectionPath = "dashscope")
14+
{
15+
Func<IServiceProvider, object?, DashScopeChatCompletionService> factory = (serviceProvider, _) =>
16+
serviceProvider.GetRequiredService<DashScopeChatCompletionService>();
17+
18+
if (configureClient == null)
19+
{
20+
builder.Services.AddHttpClient<DashScopeChatCompletionService>();
21+
}
22+
else
23+
{
24+
builder.Services.AddHttpClient<DashScopeChatCompletionService>(configureClient);
25+
}
26+
27+
builder.Services.AddOptions<DashScopeClientOptions>().BindConfiguration(configSectionPath);
28+
builder.Services.AddKeyedSingleton<IChatCompletionService>(serviceId, factory);
29+
return builder;
30+
}
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.SemanticKernel.ChatCompletion;
2+
using Sdcb.DashScope.TextGeneration;
3+
4+
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
5+
6+
public static class ChatHistoryExtensions
7+
{
8+
public static IReadOnlyList<ChatMessage> ToChatMessages(this ChatHistory chatHistory)
9+
{
10+
return chatHistory
11+
.Where(x => !string.IsNullOrEmpty(x.Content))
12+
.Select(x => new ChatMessage(x.Role.ToString(), x.Content!)).
13+
ToList();
14+
}
15+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Text.Json;
2+
using System.Text.Json.Serialization;
3+
using Microsoft.SemanticKernel;
4+
using Sdcb.DashScope.TextGeneration;
5+
6+
namespace Cnblogs.SemanticKernel.Connectors.DashScope;
7+
8+
public static class PromptExecutionSettingsExtensions
9+
{
10+
private readonly static JsonSerializerOptions JsonSerializerOptions = new()
11+
{
12+
NumberHandling = JsonNumberHandling.AllowReadingFromString
13+
};
14+
15+
public static ChatParameters? ToChatParameters(this PromptExecutionSettings executionSettings)
16+
{
17+
ChatParameters? chatParameters = null;
18+
19+
if (executionSettings?.ExtensionData?.Count > 0)
20+
{
21+
var json = JsonSerializer.Serialize(executionSettings.ExtensionData);
22+
chatParameters = JsonSerializer.Deserialize<ChatParameters>(json, JsonSerializerOptions);
23+
}
24+
25+
return chatParameters;
26+
}
27+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<RootNamespace>Cnblogs.SemanticKernel.Connectors.DashScope</RootNamespace>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<FrameworkReference Include="Microsoft.AspNetCore.App"></FrameworkReference>
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.3.0" />
16+
<PackageReference Include="Sdcb.DashScope" Version="1.0.1" />
17+
</ItemGroup>
18+
19+
</Project>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System.Diagnostics;
2+
using System.Text;
3+
using Microsoft.Extensions.Configuration;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.SemanticKernel;
6+
7+
namespace SemanticKernel.DashScope.IntegrationTest;
8+
9+
public class DashScopeChatCompletionTests
10+
{
11+
[Fact]
12+
public async Task ChatCompletion_InvokePromptAsync_WorksCorrectly()
13+
{
14+
// Arrange
15+
var builder = Kernel.CreateBuilder();
16+
builder.Services.AddSingleton(GetConfiguration());
17+
builder.AddDashScopeChatCompletion();
18+
var kernel = builder.Build();
19+
20+
var prompt = @"<message role=""user"">博客园是什么网站</message>";
21+
PromptExecutionSettings settings = new()
22+
{
23+
ExtensionData = new Dictionary<string, object>()
24+
{
25+
{ "temperature", "0.8" }
26+
}
27+
};
28+
KernelArguments kernelArguments = new(settings);
29+
30+
// Act
31+
var result = await kernel.InvokePromptAsync(prompt, kernelArguments);
32+
33+
// Assert
34+
Assert.Contains("博客园", result.ToString());
35+
Trace.WriteLine(result.ToString());
36+
}
37+
38+
[Fact]
39+
public async Task ChatCompletion_InvokePromptStreamingAsync_WorksCorrectly()
40+
{
41+
// Arrange
42+
var builder = Kernel.CreateBuilder();
43+
builder.Services.AddSingleton(GetConfiguration());
44+
builder.AddDashScopeChatCompletion();
45+
var kernel = builder.Build();
46+
47+
// Act
48+
var prompt = @"<message role=""user"">博客园是什么网站</message>";
49+
var result = kernel.InvokePromptStreamingAsync(prompt);
50+
51+
// Assert
52+
var sb = new StringBuilder();
53+
await foreach (var message in result)
54+
{
55+
Trace.WriteLine(message);
56+
sb.Append(message);
57+
}
58+
Assert.Contains("博客园", sb.ToString());
59+
}
60+
61+
private static IConfiguration GetConfiguration()
62+
{
63+
return new ConfigurationBuilder()
64+
.SetBasePath(Directory.GetCurrentDirectory())
65+
.AddJsonFile("appsettings.json")
66+
.AddUserSecrets<DashScopeChatCompletionTests>()
67+
.Build();
68+
}
69+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global using Xunit;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /