Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cnblogs.DashScope.Sdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.Core", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.Sdk.SnapshotGenerator", "test\Cnblogs.DashScope.Sdk.SnapshotGenerator\Cnblogs.DashScope.Sdk.SnapshotGenerator.csproj", "{5088DE77-1CE3-46FB-B9D0-27A6C9A5EED1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.AI", "src\Cnblogs.DashScope.AI\Cnblogs.DashScope.AI.csproj", "{5D5AD75A-8084-4738-AC56-B8A23E649452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -30,6 +32,7 @@ Global
{C910495B-87AB-4AC1-989C-B6720695A139} = {008988ED-0A3B-4272-BCC3-7B4110699345}
{CC389455-A3EA-4F09-B524-4DC351A1E1AA} = {008988ED-0A3B-4272-BCC3-7B4110699345}
{5088DE77-1CE3-46FB-B9D0-27A6C9A5EED1} = {CFC8ECB3-5248-46CD-A56C-EC088F2A3804}
{5D5AD75A-8084-4738-AC56-B8A23E649452} = {008988ED-0A3B-4272-BCC3-7B4110699345}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA6A118A-8D26-4B7A-9952-8504B8A0025B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand All @@ -56,5 +59,9 @@ Global
{5088DE77-1CE3-46FB-B9D0-27A6C9A5EED1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5088DE77-1CE3-46FB-B9D0-27A6C9A5EED1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5088DE77-1CE3-46FB-B9D0-27A6C9A5EED1}.Release|Any CPU.Build.0 = Release|Any CPU
{5D5AD75A-8084-4738-AC56-B8A23E649452}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D5AD75A-8084-4738-AC56-B8A23E649452}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D5AD75A-8084-4738-AC56-B8A23E649452}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D5AD75A-8084-4738-AC56-B8A23E649452}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ An unofficial DashScope SDK maintained by Cnblogs.

# Quick Start

## Using `Microsoft.Extensions.AI`

Install `Cnblogs.Extensions.AI.DashScope` Package

```csharp
var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max");
var completion = await client.CompleteAsync("hello");
Console.WriteLine(completion)
```

## Console App

Install `Cnblogs.DashScope.Sdk` package.
Expand Down
10 changes: 10 additions & 0 deletions README.zh-Hans.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@

# 快速开始

## 使用 `Microsoft.Extensions.AI` 接口

安装 NuGet 包 `Cnblogs.Extensions.AI.DashScope`

```csharp
var client = new DashScopeClient("your-api-key").AsChatClient("qwen-max");
var completion = await client.CompleteAsync("hello");
Console.WriteLine(completion)
```

## 控制台应用

安装 NuGet 包 `Cnblogs.DashScope.Sdk`。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<ItemGroup>
<ProjectReference Include="..\..\src\Cnblogs.DashScope.Sdk\Cnblogs.DashScope.Sdk.csproj" />
<ProjectReference Include="..\..\src\Cnblogs.DashScope.AI\Cnblogs.DashScope.AI.csproj" />
</ItemGroup>

<ItemGroup>
Expand All @@ -18,4 +19,8 @@
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI" Version="9.0.1-preview.1.24570.5" />
</ItemGroup>

</Project>
65 changes: 47 additions & 18 deletions sample/Cnblogs.DashScope.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@
using Cnblogs.DashScope.Sdk.QWen;
using Json.Schema;
using Json.Schema.Generation;
using Microsoft.Extensions.AI;

const string apiKey = "sk-**";
var dashScopeClient = new DashScopeClient(apiKey);
Console.WriteLine("Reading key from environment variable DASHSCOPE_KEY");
var apiKey = Environment.GetEnvironmentVariable("DASHSCOPE_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
Console.Write("ApiKey > ");
apiKey = Console.ReadLine();
}

var dashScopeClient = new DashScopeClient(apiKey!);

Console.WriteLine("Choose the sample you want to run:");
foreach (var sampleType in Enum.GetValues<SampleType>())
Expand Down Expand Up @@ -42,6 +50,12 @@
case SampleType.ChatCompletionWithFiles:
await ChatWithFilesAsync();
break;
case SampleType.MicrosoftExtensionsAi:
await ChatWithMicrosoftExtensions();
break;
case SampleType.MicrosoftExtensionsAiToolCall:
await dashScopeClient.ToolCallWithExtensionAsync();
break;
}

return;
Expand All @@ -68,16 +82,17 @@ async Task TextCompletionStreamAsync(string prompt)

async Task ChatStreamAsync()
{
var history = new List<ChatMessage>();
var history = new List<TextChatMessage>();
while (true)
{
Console.Write("user > ");
var input = Console.ReadLine()!;
history.Add(ChatMessage.User(input));
var stream = dashScopeClient.GetQWenChatStreamAsync(
QWenLlm.QWenMax,
history,
new TextGenerationParameters { IncrementalOutput = true, ResultFormat = ResultFormats.Message });
history.Add(TextChatMessage.User(input));
var stream = dashScopeClient
.GetQWenChatStreamAsync(
QWenLlm.QWenMax,
history,
new TextGenerationParameters { IncrementalOutput = true, ResultFormat = ResultFormats.Message });
var role = string.Empty;
var message = new StringBuilder();
await foreach (var modelResponse in stream)
Expand All @@ -94,25 +109,25 @@ async Task ChatStreamAsync()
}

Console.WriteLine();
history.Add(new ChatMessage(role, message.ToString()));
history.Add(new TextChatMessage(role, message.ToString()));
}

// ReSharper disable once FunctionNeverReturns
}

async Task ChatWithFilesAsync()
{
var history = new List<ChatMessage>();
var history = new List<TextChatMessage>();
Console.WriteLine("uploading file \"test.txt\" ");
var file = new FileInfo("test.txt");
var uploadedFile = await dashScopeClient.UploadFileAsync(file.OpenRead(), file.Name);
Console.WriteLine("file uploaded, id: " + uploadedFile.Id);
Console.WriteLine();

var fileMessage = ChatMessage.File(uploadedFile.Id);
var fileMessage = TextChatMessage.File(uploadedFile.Id);
history.Add(fileMessage);
Console.WriteLine("system > " + fileMessage.Content);
var userPrompt = ChatMessage.User("该文件的内容是什么");
var userPrompt = TextChatMessage.User("该文件的内容是什么");
history.Add(userPrompt);
Console.WriteLine("user > " + userPrompt.Content);
var stream = dashScopeClient.GetQWenChatStreamAsync(
Expand All @@ -135,7 +150,7 @@ async Task ChatWithFilesAsync()
}

Console.WriteLine();
history.Add(new ChatMessage(role, message.ToString()));
history.Add(new TextChatMessage(role, message.ToString()));

Console.WriteLine();
Console.WriteLine("Deleting file by id: " + uploadedFile.Id);
Expand All @@ -145,7 +160,7 @@ async Task ChatWithFilesAsync()

async Task ChatWithToolsAsync()
{
var history = new List<ChatMessage>();
var history = new List<TextChatMessage>();
var tools = new List<ToolDefinition>
{
new(
Expand All @@ -156,19 +171,19 @@ async Task ChatWithToolsAsync()
new JsonSchemaBuilder().FromType<WeatherReportParameters>().Build()))
};
var chatParameters = new TextGenerationParameters() { ResultFormat = ResultFormats.Message, Tools = tools };
var question = ChatMessage.User("请问现在杭州的天气如何?");
var question = TextChatMessage.User("请问现在杭州的天气如何?");
history.Add(question);
Console.WriteLine($"{question.Role} > {question.Content}");

var response = await dashScopeClient.GetQWenChatCompletionAsync(QWenLlm.QWenMax, history, chatParameters);
var toolCallMessage = response.Output.Choices![0].Message;
history.Add(toolCallMessage);
Console.WriteLine(
$"{toolCallMessage.Role} > {toolCallMessage.ToolCalls![0].Function!.Name}{toolCallMessage.ToolCalls[0].Function!.Arguments}");
$"{toolCallMessage.Role} > {toolCallMessage.ToolCalls![0].Function.Name}{toolCallMessage.ToolCalls[0].Function.Arguments}");

var toolResponse = GetWeather(
JsonSerializer.Deserialize<WeatherReportParameters>(toolCallMessage.ToolCalls[0].Function!.Arguments!)!);
var toolMessage = ChatMessage.Tool(toolResponse, nameof(GetWeather));
JsonSerializer.Deserialize<WeatherReportParameters>(toolCallMessage.ToolCalls[0].Function.Arguments!)!);
var toolMessage = TextChatMessage.Tool(toolResponse, nameof(GetWeather));
history.Add(toolMessage);
Console.WriteLine($"{toolMessage.Role} > {toolMessage.Content}");

Expand All @@ -186,3 +201,17 @@ string GetWeather(WeatherReportParameters parameters)
};
}
}

async Task ChatWithMicrosoftExtensions()
{
Console.WriteLine("Requesting model...");
var chatClient = dashScopeClient.AsChatClient("qwen-max");
List<ChatMessage> conversation =
[
new(ChatRole.System, "You are a helpful AI assistant"),
new(ChatRole.User, "What is AI?")
];
var response = await chatClient.CompleteAsync(conversation);
var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true };
Console.WriteLine(JsonSerializer.Serialize(response, serializerOptions));
}
15 changes: 6 additions & 9 deletions sample/Cnblogs.DashScope.Sample/SampleType.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
using System.ComponentModel;

namespace Cnblogs.DashScope.Sample;
namespace Cnblogs.DashScope.Sample;

public enum SampleType
{
[Description("Simple prompt completion")]
TextCompletion,

[Description("Simple prompt completion with incremental output")]
TextCompletionSse,

[Description("Conversation between user and assistant")]
ChatCompletion,

[Description("Conversation with tools")]
ChatCompletionWithTool,

[Description("Conversation with files")]
ChatCompletionWithFiles
ChatCompletionWithFiles,

MicrosoftExtensionsAi,

MicrosoftExtensionsAiToolCall
}
2 changes: 2 additions & 0 deletions sample/Cnblogs.DashScope.Sample/SampleTypeDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public static string GetDescription(this SampleType sampleType)
SampleType.ChatCompletion => "Conversation between user and assistant",
SampleType.ChatCompletionWithTool => "Function call sample",
SampleType.ChatCompletionWithFiles => "File upload sample using qwen-long",
SampleType.MicrosoftExtensionsAi => "Use with Microsoft.Extensions.AI",
SampleType.MicrosoftExtensionsAiToolCall => "Use tool call with Microsoft.Extensions.AI interfaces",
_ => throw new ArgumentOutOfRangeException(nameof(sampleType), sampleType, "Unsupported sample option")
};
}
Expand Down
25 changes: 25 additions & 0 deletions sample/Cnblogs.DashScope.Sample/ToolCallWithExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.ComponentModel;
using System.Text.Json;
using Cnblogs.DashScope.Core;
using Microsoft.Extensions.AI;

namespace Cnblogs.DashScope.Sample;

public static class ToolCallWithExtensions
{
public static async Task ToolCallWithExtensionAsync(this IDashScopeClient dashScopeClient)
{
[Description("Gets the weather")]
string GetWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining";

var chatOptions = new ChatOptions { Tools = [AIFunctionFactory.Create(GetWeather)] };

var client = dashScopeClient.AsChatClient("qwen-max").AsBuilder().UseFunctionInvocation().Build();
await foreach (var message in client.CompleteStreamingAsync("What is weather today?", chatOptions))
{
Console.WriteLine(JsonSerializer.Serialize(message));
}

Console.WriteLine();
}
}
16 changes: 16 additions & 0 deletions src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Product>Cnblogs.DashScope.AI</Product>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>Cnblogs;Dashscope;Microsoft.Extensions.AI;Sdk;Embedding;</PackageTags>
<Description>Implementation of generative AI abstractions for DashScope endpoints.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Cnblogs.DashScope.Sdk\Cnblogs.DashScope.Sdk.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.0.1-preview.1.24570.5"/>
</ItemGroup>

</Project>
Loading