From 6b6d2d2e09294da7729cf3a91bfc63d0147c6936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=88=E6=98=9F=E7=B9=81?= Date: Mon, 17 Mar 2025 22:44:51 +0800 Subject: [PATCH] feat: allow setting workspace id per request --- .../ApplicationRequest.cs | 17 ++++++++++--- .../DashScopeClientCore.cs | 11 ++++---- .../Internals/IDashScopeWorkspaceConfig.cs | 12 +++++++++ .../TextGenerationTokenDetails.cs | 7 ++++++ .../TextGenerationTokenUsage.cs | 5 ++++ ...le-generation-text-nosse.response.body.txt | 13 +--------- ...-generation-text-nosse.response.header.txt | 25 ++++++++++--------- .../Utils/Snapshots.Application.cs | 24 ++++++++++++++++++ .../Utils/Snapshots.TextGeneration.cs | 11 ++++---- .../WorkspaceIdTests.cs | 25 +++++++++++++++++++ 10 files changed, 112 insertions(+), 38 deletions(-) create mode 100644 src/Cnblogs.DashScope.Core/Internals/IDashScopeWorkspaceConfig.cs create mode 100644 src/Cnblogs.DashScope.Core/TextGenerationTokenDetails.cs create mode 100644 test/Cnblogs.DashScope.Sdk.UnitTests/WorkspaceIdTests.cs diff --git a/src/Cnblogs.DashScope.Core/ApplicationRequest.cs b/src/Cnblogs.DashScope.Core/ApplicationRequest.cs index b367d5e..bdafef5 100644 --- a/src/Cnblogs.DashScope.Core/ApplicationRequest.cs +++ b/src/Cnblogs.DashScope.Core/ApplicationRequest.cs @@ -1,21 +1,30 @@ -namespace Cnblogs.DashScope.Core; +using System.Text.Json.Serialization; +using Cnblogs.DashScope.Core.Internals; + +namespace Cnblogs.DashScope.Core; /// /// Request body for an application all. /// /// Type of the biz_content -public class ApplicationRequest +public class ApplicationRequest : IDashScopeWorkspaceConfig where TBizParams : class { /// /// Content of this call. /// - public required ApplicationInput Input { get; init; } + public required ApplicationInput Input { get; set; } /// /// Optional configurations. /// - public ApplicationParameters? Parameters { get; init; } + public ApplicationParameters? Parameters { get; set; } + + /// + /// Optional workspace id. + /// + [JsonIgnore] + public string? WorkspaceId { get; set; } } /// diff --git a/src/Cnblogs.DashScope.Core/DashScopeClientCore.cs b/src/Cnblogs.DashScope.Core/DashScopeClientCore.cs index 9b917d0..7cca41f 100644 --- a/src/Cnblogs.DashScope.Core/DashScopeClientCore.cs +++ b/src/Cnblogs.DashScope.Core/DashScopeClientCore.cs @@ -275,7 +275,9 @@ public async Task ListFilesAsync(CancellationToken cancellati } /// - public async Task DeleteFileAsync(DashScopeFileId id, CancellationToken cancellationToken = default) + public async Task DeleteFileAsync( + DashScopeFileId id, + CancellationToken cancellationToken = default) { var request = BuildRequest(HttpMethod.Delete, ApiLinks.Files + $"/{id}"); return (await SendCompatibleAsync(request, cancellationToken))!; @@ -297,8 +299,7 @@ private static HttpRequestMessage BuildRequest( string url, TPayload? payload = null, bool sse = false, - bool isTask = false, - string? workspaceId = null) + bool isTask = false) where TPayload : class { var message = new HttpRequestMessage(method, url) @@ -316,9 +317,9 @@ private static HttpRequestMessage BuildRequest( message.Headers.Add("X-DashScope-Async", "enable"); } - if (string.IsNullOrWhiteSpace(workspaceId) == false) + if (payload is IDashScopeWorkspaceConfig config && string.IsNullOrWhiteSpace(config.WorkspaceId) == false) { - message.Headers.Add("X-DashScope-WorkspaceId", workspaceId); + message.Headers.Add("X-DashScope-WorkSpace", config.WorkspaceId); } return message; diff --git a/src/Cnblogs.DashScope.Core/Internals/IDashScopeWorkspaceConfig.cs b/src/Cnblogs.DashScope.Core/Internals/IDashScopeWorkspaceConfig.cs new file mode 100644 index 0000000..752056b --- /dev/null +++ b/src/Cnblogs.DashScope.Core/Internals/IDashScopeWorkspaceConfig.cs @@ -0,0 +1,12 @@ +namespace Cnblogs.DashScope.Core.Internals; + +/// +/// Workspace configuration. +/// +internal interface IDashScopeWorkspaceConfig +{ + /// + /// Unique id of workspace to use. + /// + public string? WorkspaceId { get; } +} diff --git a/src/Cnblogs.DashScope.Core/TextGenerationTokenDetails.cs b/src/Cnblogs.DashScope.Core/TextGenerationTokenDetails.cs new file mode 100644 index 0000000..d8e5b18 --- /dev/null +++ b/src/Cnblogs.DashScope.Core/TextGenerationTokenDetails.cs @@ -0,0 +1,7 @@ +namespace Cnblogs.DashScope.Core; + +/// +/// Token usage details. +/// +/// Token count of cached input tokens +public record TextGenerationTokenDetails(int CachedTokens); diff --git a/src/Cnblogs.DashScope.Core/TextGenerationTokenUsage.cs b/src/Cnblogs.DashScope.Core/TextGenerationTokenUsage.cs index 8021388..7662d7d 100644 --- a/src/Cnblogs.DashScope.Core/TextGenerationTokenUsage.cs +++ b/src/Cnblogs.DashScope.Core/TextGenerationTokenUsage.cs @@ -11,6 +11,11 @@ public class TextGenerationTokenUsage /// This number may larger than input if internet search is enabled. public int InputTokens { get; set; } + /// + /// Input token details. + /// + public TextGenerationTokenDetails? PromptTokensDetails { get; set; } + /// /// The number of output token. /// diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.body.txt b/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.body.txt index 8d85edb..76cd3dc 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.body.txt +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.body.txt @@ -1,12 +1 @@ -{ - "output": { - "finish_reason": "stop", - "text": "1+1 等于 2。这是最基本的数学加法之一,在十进制计数体系中,任何情况下两个一相加的结果都是二。" - }, - "usage": { - "total_tokens": 43, - "output_tokens": 35, - "input_tokens": 8 - }, - "request_id": "4ef2ed16-4dc3-9083-a723-fb2e80c84d3b" -} +{"output":{"finish_reason":"stop","text":"1+1等于2。这是最基本的数学加法运算之一。"},"usage":{"prompt_tokens_details":{"cached_tokens":0},"total_tokens":30,"output_tokens":14,"input_tokens":16},"request_id":"7e3d5586-cb70-98ce-97bf-8a2ac0091c3f"} \ No newline at end of file diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.header.txt b/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.header.txt index 3b4f8a2..ada9654 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.header.txt +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/RawHttpData/single-generation-text-nosse.response.header.txt @@ -1,13 +1,14 @@ -HTTP/1.1 200 OK -eagleeye-traceid: 5bc840127c9dd7ed1de5586c0865cf01 -content-type: application/json +HTTP/1.1 200 OK +Vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Accept-Encoding +X-Request-ID: 7e3d5586-cb70-98ce-97bf-8a2ac0091c3f +x-dashscope-timeout: 180 x-dashscope-call-gateway: true -req-cost-time: 6004 -req-arrive-time: 1708924139239 -resp-start-time: 1708924145244 -x-envoy-upstream-service-time: 5998 -content-encoding: gzip -vary: Accept-Encoding -date: Mon, 26 Feb 2024 05:09:05 GMT -server: istio-envoy -transfer-encoding: chunked +x-dashscope-finished: true +req-cost-time: 974 +req-arrive-time: 1742222245417 +resp-start-time: 1742222246391 +x-envoy-upstream-service-time: 964 +Set-Cookie: acw_tc=7e3d5586-cb70-98ce-97bf-8a2ac0091c3f7d8fa666bbce18dfce67d4b0ced27ae0;path=/;HttpOnly;Max-Age=1800 +Date: Mon, 17 Mar 2025 14:37:26 GMT +Server: istio-envoy +Transfer-Encoding: chunked diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.Application.cs b/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.Application.cs index 6ad5bb1..59cfe01 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.Application.cs +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.Application.cs @@ -273,6 +273,30 @@ public static readonly RequestSnapshot, ApplicationResponse> + WorkflowInDifferentWorkSpaceNoSse = + new( + "application-workflow", + new ApplicationRequest() + { + Input = new ApplicationInput() + { + BizParams = new TestApplicationBizParam("code"), Prompt = "请你跟我这样说" + }, + Parameters = new ApplicationParameters() + { + TopK = 100, + TopP = 0.8f, + Seed = 1234, + Temperature = 0.85f, + }, + WorkspaceId = "workspaceId" + }, + new ApplicationResponse( + "10990f51-e2d0-9338-9c52-319af5f4858b", + new ApplicationOutput("code", "stop", "5a20b47dac2f43a7b1cbb8924ca66c47", null, null), + new ApplicationUsage(null))); + public static readonly RequestSnapshot ConversationSessionIdNoSse = new( "application-conversation-generation-session-id", diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.TextGeneration.cs b/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.TextGeneration.cs index 6f8fe94..4a6e30a 100644 --- a/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.TextGeneration.cs +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/Utils/Snapshots.TextGeneration.cs @@ -36,14 +36,15 @@ public static class TextFormat { Output = new TextGenerationOutput { - FinishReason = "stop", Text = "1+1 等于 2。这是最基本的数学加法之一,在十进制计数体系中,任何情况下两个一相加的结果都是二。" + FinishReason = "stop", Text = "1+1等于2。这是最基本的数学加法运算之一。" }, - RequestId = "4ef2ed16-4dc3-9083-a723-fb2e80c84d3b", + RequestId = "7e3d5586-cb70-98ce-97bf-8a2ac0091c3f", Usage = new TextGenerationTokenUsage { - InputTokens = 8, - OutputTokens = 35, - TotalTokens = 43 + InputTokens = 16, + OutputTokens = 14, + TotalTokens = 30, + PromptTokensDetails = new TextGenerationTokenDetails(0) } }); diff --git a/test/Cnblogs.DashScope.Sdk.UnitTests/WorkspaceIdTests.cs b/test/Cnblogs.DashScope.Sdk.UnitTests/WorkspaceIdTests.cs new file mode 100644 index 0000000..c6b3a98 --- /dev/null +++ b/test/Cnblogs.DashScope.Sdk.UnitTests/WorkspaceIdTests.cs @@ -0,0 +1,25 @@ +using Cnblogs.DashScope.Sdk.UnitTests.Utils; +using NSubstitute; + +namespace Cnblogs.DashScope.Sdk.UnitTests; + +public class WorkspaceIdTests +{ + [Fact] + public async Task ApplicationCall_WithWorkspaceId_ApplyAsync() + { + // Arrange + const bool sse = false; + var testCase = Snapshots.Application.WorkflowInDifferentWorkSpaceNoSse; + var (client, handler) = await Sut.GetTestClientAsync(sse, testCase); + + // Act + await client.GetApplicationResponseAsync("anyId", testCase.RequestModel); + + // Assert + handler.Received().MockSend( + Arg.Is( + m => m.Headers.GetValues("X-DashScope-WorkSpace").First() == testCase.RequestModel.WorkspaceId), + Arg.Any()); + } +}