Skip to content

refactor: make library compatible with net6 #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 7, 2025
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
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -384,5 +384,5 @@ dotnet_naming_style.s_camelcase.capitalization = camel_case
indent_size = 2
tab_width = 2

[*.{props,targets,config,nuspec,json}]
[*.{props,targets,config,nuspec,json,yml}]
indent_size = 2
26 changes: 18 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,25 @@ on:
branches: [ "main" ]

jobs:
test:
test-net6:
runs-on: ubuntu-latest
container: mcr.microsoft.com/dotnet/sdk:8.0
container: mcr.microsoft.com/dotnet/sdk:6.0

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: dotnet build -c Release
- name: Test
run: dotnet test -c Release
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: dotnet build src/Cnblogs.DashScope.AspNetCore -c Release
- name: Test
run: dotnet test test/Cnblogs.DashScope.Sdk.UnitTests -c Release
test-net8:
runs-on: ubuntu-latest
container: mcr.microsoft.com/dotnet/sdk:8.0
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: dotnet build src/Cnblogs.DashScope.AI -c Release
- name: Test
run: dotnet test test/Cnblogs.DashScope.AI.UnitTests -c Release

14 changes: 14 additions & 0 deletions Cnblogs.DashScope.Sdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.Sdk.Snaps
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.AI", "src\Cnblogs.DashScope.AI\Cnblogs.DashScope.AI.csproj", "{5D5AD75A-8084-4738-AC56-B8A23E649452}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.AI.UnitTests", "test\Cnblogs.DashScope.AI.UnitTests\Cnblogs.DashScope.AI.UnitTests.csproj", "{25EE79E1-147B-42FD-AFEA-E1550EDD1D36}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cnblogs.DashScope.Tests.Shared", "test\Cnblogs.DashScope.Tests.Shared\Cnblogs.DashScope.Tests.Shared.csproj", "{06F0AF23-445B-4C6F-9E19-570DA9B7435D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -33,6 +37,8 @@ Global
{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}
{25EE79E1-147B-42FD-AFEA-E1550EDD1D36} = {CFC8ECB3-5248-46CD-A56C-EC088F2A3804}
{06F0AF23-445B-4C6F-9E19-570DA9B7435D} = {CFC8ECB3-5248-46CD-A56C-EC088F2A3804}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA6A118A-8D26-4B7A-9952-8504B8A0025B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -63,5 +69,13 @@ Global
{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
{25EE79E1-147B-42FD-AFEA-E1550EDD1D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25EE79E1-147B-42FD-AFEA-E1550EDD1D36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25EE79E1-147B-42FD-AFEA-E1550EDD1D36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25EE79E1-147B-42FD-AFEA-E1550EDD1D36}.Release|Any CPU.Build.0 = Release|Any CPU
{06F0AF23-445B-4C6F-9E19-570DA9B7435D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06F0AF23-445B-4C6F-9E19-570DA9B7435D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06F0AF23-445B-4C6F-9E19-570DA9B7435D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06F0AF23-445B-4C6F-9E19-570DA9B7435D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>Cnblogs</Authors>
Expand Down
9 changes: 3 additions & 6 deletions sample/Cnblogs.DashScope.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ async Task ChatWithToolsAsync()
"获得当前天气",
new JsonSchemaBuilder().FromType<WeatherReportParameters>().Build()))
};
var chatParameters = new TextGenerationParameters() { ResultFormat = ResultFormats.Message, Tools = tools };
var chatParameters = new TextGenerationParameters { ResultFormat = ResultFormats.Message, Tools = tools };
var question = TextChatMessage.User("请问现在杭州的天气如何?");
history.Add(question);
Console.WriteLine($"{question.Role} > {question.Content}");
Expand Down Expand Up @@ -214,18 +214,15 @@ 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?")
];
new() { new(ChatRole.System, "You are a helpful AI assistant"), new(ChatRole.User, "What is AI?") };
var response = await chatClient.GetResponseAsync(conversation);
var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true };
Console.WriteLine(JsonSerializer.Serialize(response, serializerOptions));
}

async Task ApplicationCallAsync(string applicationId, string prompt)
{
var request = new ApplicationRequest() { Input = new ApplicationInput() { Prompt = prompt } };
var request = new ApplicationRequest { Input = new ApplicationInput { Prompt = prompt } };
var response = await dashScopeClient.GetApplicationResponseAsync(applicationId, request);
Console.WriteLine(response.Output.Text);
}
2 changes: 1 addition & 1 deletion sample/Cnblogs.DashScope.Sample/ToolCallWithExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static async Task ToolCallWithExtensionAsync(this IDashScopeClient dashSc
[Description("Gets the weather")]
string GetWeather(string location) => Random.Shared.NextDouble() > 0.5 ? "It's sunny" : "It's raining";

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

var client = dashScopeClient.AsChatClient("qwen-max").AsBuilder().UseFunctionInvocation().Build();
await foreach (var message in client.GetStreamingResponseAsync("What is weather of LA today?", chatOptions))
Expand Down
2 changes: 2 additions & 0 deletions src/Cnblogs.DashScope.AI/Cnblogs.DashScope.AI.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Product>Cnblogs.DashScope.AI</Product>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>Cnblogs;Dashscope;Microsoft.Extensions.AI;Sdk;Embedding;</PackageTags>
Expand All @@ -10,6 +11,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="JsonSchema.Net.Generation" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.AI.Abstractions" Version="9.5.0" />
</ItemGroup>

Expand Down
28 changes: 14 additions & 14 deletions src/Cnblogs.DashScope.AI/DashScopeChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class DashScopeChatClient : IChatClient
private readonly string _modelId;

private static readonly JsonSchema EmptyObjectSchema =
JsonSchema.FromText("""{"type":"object","required":[],"properties":{}}""");
JsonSchema.FromText("{\"type\":\"object\",\"required\":[],\"properties\":{}}");

private static readonly TextGenerationParameters
DefaultTextGenerationParameter = new() { ResultFormat = "message" };
Expand Down Expand Up @@ -55,15 +55,15 @@ public async Task<ChatResponse> GetResponseAsync(
if (useVl)
{
var response = await _dashScopeClient.GetMultimodalGenerationAsync(
new ModelRequest<MultimodalInput, IMultimodalParameters>()
new ModelRequest<MultimodalInput, IMultimodalParameters>
{
Input = new MultimodalInput { Messages = ToMultimodalMessages(chatMessages) },
Parameters = ToMultimodalParameters(options),
Model = modelId
},
cancellationToken);

var returnMessage = new ChatMessage()
var returnMessage = new ChatMessage
{
RawRepresentation = response, Role = ToChatRole(response.Output.Choices[0].Message.Role),
};
Expand All @@ -80,7 +80,7 @@ public async Task<ChatResponse> GetResponseAsync(

if (response.Usage != null)
{
completion.Usage = new UsageDetails()
completion.Usage = new UsageDetails
{
InputTokenCount = response.Usage.InputTokens, OutputTokenCount = response.Usage.OutputTokens,
};
Expand All @@ -92,7 +92,7 @@ public async Task<ChatResponse> GetResponseAsync(
{
var parameters = ToTextGenerationParameters(options) ?? DefaultTextGenerationParameter;
var response = await _dashScopeClient.GetTextCompletionAsync(
new ModelRequest<TextGenerationInput, ITextGenerationParameters>()
new ModelRequest<TextGenerationInput, ITextGenerationParameters>
{
Input = new TextGenerationInput
{
Expand All @@ -116,7 +116,7 @@ public async Task<ChatResponse> GetResponseAsync(

if (response.Usage != null)
{
completion.Usage = new UsageDetails()
completion.Usage = new UsageDetails
{
InputTokenCount = response.Usage.InputTokens,
OutputTokenCount = response.Usage.OutputTokens,
Expand Down Expand Up @@ -147,7 +147,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
var parameter = ToMultimodalParameters(options);
parameter.IncrementalOutput = true;
var stream = _dashScopeClient.GetMultimodalGenerationStreamAsync(
new ModelRequest<MultimodalInput, IMultimodalParameters>()
new ModelRequest<MultimodalInput, IMultimodalParameters>
{
Input = new MultimodalInput { Messages = ToMultimodalMessages(chatMessages) },
Parameters = parameter,
Expand All @@ -164,7 +164,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
: ToFinishReason(response.Output.Choices[0].FinishReason);
completionId ??= response.RequestId;

var update = new ChatResponseUpdate()
var update = new ChatResponseUpdate
{
ResponseId = completionId,
CreatedAt = DateTimeOffset.Now,
Expand Down Expand Up @@ -199,7 +199,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
{
// qwen does not support streaming with function call, fallback to non-streaming
var completion = await GetResponseAsync(chatMessages, options, cancellationToken);
yield return new ChatResponseUpdate()
yield return new ChatResponseUpdate
{
ResponseId = completion.ResponseId,
Role = completion.Messages[0].Role,
Expand All @@ -216,7 +216,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
var parameters = ToTextGenerationParameters(options) ?? DefaultTextGenerationParameter;
parameters.IncrementalOutput = true;
var stream = _dashScopeClient.GetTextCompletionStreamAsync(
new ModelRequest<TextGenerationInput, ITextGenerationParameters>()
new ModelRequest<TextGenerationInput, ITextGenerationParameters>
{
Input = new TextGenerationInput
{
Expand All @@ -238,7 +238,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
: ToFinishReason(response.Output.Choices[0].FinishReason);
completionId ??= response.RequestId;

var update = new ChatResponseUpdate()
var update = new ChatResponseUpdate
{
ResponseId = completionId,
CreatedAt = DateTimeOffset.Now,
Expand All @@ -257,7 +257,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
{
update.Contents.Add(
new UsageContent(
new UsageDetails()
new UsageDetails
{
InputTokenCount = response.Usage.InputTokens,
OutputTokenCount = response.Usage.OutputTokens,
Expand Down Expand Up @@ -299,7 +299,7 @@ public void Dispose()

private static ChatMessage ToChatMessage(TextChatMessage message)
{
var returnMessage = new ChatMessage()
var returnMessage = new ChatMessage
{
RawRepresentation = message, Role = ToChatRole(message.Role),
};
Expand Down Expand Up @@ -485,7 +485,7 @@ private IEnumerable<TextChatMessage> ToTextChatMessages(
format = "json_object";
}

return new TextGenerationParameters()
return new TextGenerationParameters
{
ResultFormat = format,
Temperature = options.Temperature,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public async Task<GeneratedEmbeddings<Embedding<float>>> GenerateAsync(
e => new Embedding<float>(e.Embedding) { ModelId = _modelId, CreatedAt = DateTimeOffset.Now });
var rawUsage = rawResponse.Usage;
var usage = rawUsage != null
? new UsageDetails() { InputTokenCount = rawUsage.TotalTokens, TotalTokenCount = rawUsage.TotalTokens }
? new UsageDetails { InputTokenCount = rawUsage.TotalTokens, TotalTokenCount = rawUsage.TotalTokens }
: null;
return new GeneratedEmbeddings<Embedding<float>>(embeddings)
{
Expand Down
4 changes: 3 additions & 1 deletion src/Cnblogs.DashScope.Core/ApplicationInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ public class ApplicationInput<TBizParams>
/// <summary>
/// Inputs for application call.
/// </summary>
public class ApplicationInput : ApplicationInput<Dictionary<string, object?>>;
public class ApplicationInput : ApplicationInput<Dictionary<string, object?>>
{
}
6 changes: 4 additions & 2 deletions src/Cnblogs.DashScope.Core/ApplicationRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class ApplicationRequest<TBizParams> : IDashScopeWorkspaceConfig
/// <summary>
/// Content of this call.
/// </summary>
public required ApplicationInput<TBizParams> Input { get; set; }
public ApplicationInput<TBizParams> Input { get; set; } = new();

/// <summary>
/// Optional configurations.
Expand All @@ -30,4 +30,6 @@ public class ApplicationRequest<TBizParams> : IDashScopeWorkspaceConfig
/// <summary>
/// Request body for an application call with dictionary biz_content.
/// </summary>
public class ApplicationRequest : ApplicationRequest<Dictionary<string, object?>>;
public class ApplicationRequest : ApplicationRequest<Dictionary<string, object?>>
{
}
2 changes: 1 addition & 1 deletion src/Cnblogs.DashScope.Core/BackgroundGenerationInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class BackgroundGenerationInput
/// <summary>
/// The image url to generation background on.
/// </summary>
public required string BaseImageUrl { get; set; }
public string BaseImageUrl { get; set; } = string.Empty;

/// <summary>
/// The reference image url for.
Expand Down
2 changes: 1 addition & 1 deletion src/Cnblogs.DashScope.Core/BatchGetEmbeddingsInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ public class BatchGetEmbeddingsInput
/// <summary>
/// The url of text file to compute embeddings from.
/// </summary>
public required string Url { get; set; }
public string Url { get; set; } = string.Empty;
}
4 changes: 2 additions & 2 deletions src/Cnblogs.DashScope.Core/Cnblogs.DashScope.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.ML.Tokenizers" Version="1.0.2" />
<EmbeddedResource Include="Internals\qwen.tiktoken" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Internals\qwen.tiktoken" />
<PackageReference Include="Microsoft.DeepDev.TokenizerLib" Version="1.3.3" />
</ItemGroup>

</Project>
6 changes: 3 additions & 3 deletions src/Cnblogs.DashScope.Core/DashScopeClientCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class DashScopeClientCore : IDashScopeClient
new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
PropertyNamingPolicy = JsonSnakeCaseLowerNamingPolicy.SnakeCaseLower,
};

private readonly HttpClient _httpClient;
Expand Down Expand Up @@ -332,7 +332,7 @@ private static HttpRequestMessage BuildRequest<TPayload>(
{
var response = await GetSuccessResponseAsync<OpenAiErrorResponse>(
message,
r => new DashScopeError()
r => new DashScopeError
{
Code = r.Error.Type,
Message = r.Error.Message,
Expand Down Expand Up @@ -367,7 +367,7 @@ private async IAsyncEnumerable<TResponse> StreamAsync<TResponse>(
if (cancellationToken.IsCancellationRequested)
throw new TaskCanceledException();

var line = await reader.ReadLineAsync(cancellationToken);
var line = await reader.ReadLineAsync();
if (line != null && line.StartsWith("data:"))
{
var data = line["data:".Length..];
Expand Down
27 changes: 19 additions & 8 deletions src/Cnblogs.DashScope.Core/DashScopeException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,35 @@
/// <summary>
/// Represents error detail for DashScope API calls.
/// </summary>
/// <param name="apiUrl">The requested api url.</param>
/// <param name="status">The status code of response. Would be 0 if no response is received.</param>
/// <param name="error">The error detail returned by server.</param>
/// <param name="message">The error message.</param>
public class DashScopeException(string? apiUrl, int status, DashScopeError? error, string message) : Exception(message)
public class DashScopeException : Exception
{
/// <summary>
/// Represents error detail for DashScope API calls.
/// </summary>
/// <param name="apiUrl">The requested api url.</param>
/// <param name="status">The status code of response. Would be 0 if no response is received.</param>
/// <param name="error">The error detail returned by server.</param>
/// <param name="message">The error message.</param>
public DashScopeException(string? apiUrl, int status, DashScopeError? error, string message)
: base(message)
{
ApiUrl = apiUrl;
Error = error;
Status = status;
}

/// <summary>
/// The requested api url.
/// </summary>
public string? ApiUrl { get; } = apiUrl;
public string? ApiUrl { get; }

/// <summary>
/// The error detail returned by server.
/// </summary>
public DashScopeError? Error { get; } = error;
public DashScopeError? Error { get; }

/// <summary>
/// The status code of response. Would be 0 if no response is received.
/// </summary>
public int Status { get; } = status;
public int Status { get; }
}
Loading