From d7171b43e6a439e4910e81f0080a6bfbdda6ae16 Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Thu, 3 Sep 2026 15:28:12 +0800 Subject: [PATCH 1/2] fix(protocol): fill missing module in sdk-client tracking header AsrPhraseFinetuneOption and OSSUtils requests sent x-dashscope-sdk-client without the module segment; they now report "audio" and "utils" respectively. Add unit and wire-level tests locking the client/version[/module] + session-id header contract. --- .../asr/phrase/AsrPhraseFinetuneOption.java | 5 + .../com/alibaba/dashscope/utils/OSSUtils.java | 4 +- .../TestSdkClientTrackingHeader.java | 187 ++++++++++++++++ .../protocol/TestDashScopeHeaders.java | 211 ++++++++++++++++++ 4 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java create mode 100644 src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java diff --git a/src/main/java/com/alibaba/dashscope/audio/asr/phrase/AsrPhraseFinetuneOption.java b/src/main/java/com/alibaba/dashscope/audio/asr/phrase/AsrPhraseFinetuneOption.java index 2953ec80..dfeb27f9 100644 --- a/src/main/java/com/alibaba/dashscope/audio/asr/phrase/AsrPhraseFinetuneOption.java +++ b/src/main/java/com/alibaba/dashscope/audio/asr/phrase/AsrPhraseFinetuneOption.java @@ -59,4 +59,9 @@ public String httpUrl() { return "/fine-tunes/outputs"; } } + + @Override + public String getModule() { + return "audio"; + } } diff --git a/src/main/java/com/alibaba/dashscope/utils/OSSUtils.java b/src/main/java/com/alibaba/dashscope/utils/OSSUtils.java index 1ce094d6..f940b15d 100644 --- a/src/main/java/com/alibaba/dashscope/utils/OSSUtils.java +++ b/src/main/java/com/alibaba/dashscope/utils/OSSUtils.java @@ -145,7 +145,9 @@ public static DashScopeResult get_upload_certificate(String model, String apiKey false, false, "", - new HashMap()))) + new HashMap(), + null, + "utils"))) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) { diff --git a/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java b/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java new file mode 100644 index 00000000..abe12a63 --- /dev/null +++ b/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java @@ -0,0 +1,187 @@ +// Copyright (c) Alibaba, Inc. and its affiliates. + +package com.alibaba.dashscope; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.alibaba.dashscope.aigc.generation.Generation; +import com.alibaba.dashscope.aigc.generation.models.QwenParam; +import com.alibaba.dashscope.api.SynchronizeHalfDuplexApi; +import com.alibaba.dashscope.common.DashScopeResult; +import com.alibaba.dashscope.common.OutputMode; +import com.alibaba.dashscope.common.ResultCallback; +import com.alibaba.dashscope.protocol.ApiServiceOption; +import com.alibaba.dashscope.protocol.AsyncTaskOption; +import com.alibaba.dashscope.protocol.HttpMethod; +import com.alibaba.dashscope.protocol.Protocol; +import com.alibaba.dashscope.protocol.StreamingMode; +import com.alibaba.dashscope.task.AsyncTaskParam; +import com.alibaba.dashscope.utils.Constants; +import com.alibaba.dashscope.utils.JsonUtils; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import okhttp3.MediaType; +import okhttp3.WebSocket; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +/** + * Verifies the tracking headers that actually reach the wire, which is what the gateway logs. + * Expected shape: {@code x-dashscope-sdk-client: java-sdk//} plus a session id; + * nothing else. + */ +@Execution(ExecutionMode.SAME_THREAD) +@SetEnvironmentVariable(key = "DASHSCOPE_API_KEY", value = "1234") +public class TestSdkClientTrackingHeader { + private static final MediaType MEDIA_TYPE_APPLICATION_JSON = + MediaType.parse("application/json; charset=utf-8"); + private static final String SDK_CLIENT = "x-dashscope-sdk-client"; + private static final String SDK_SESSION_ID = "x-dashscope-sdk-session-id"; + private static final String SDK_VERSION = "x-dashscope-sdk-version"; + private static final String SDK_MODULE = "x-dashscope-sdk-module"; + private static final String CLIENT_PREFIX = "java-sdk/" + Version.version; + + private MockWebServer server; + private String originalHttpUrl; + private String originalWebSocketUrl; + + @BeforeEach + public void before() { + server = new MockWebServer(); + originalHttpUrl = Constants.baseHttpApiUrl; + originalWebSocketUrl = Constants.baseWebsocketApiUrl; + } + + @AfterEach + public void after() throws IOException { + Constants.baseHttpApiUrl = originalHttpUrl; + Constants.baseWebsocketApiUrl = originalWebSocketUrl; + server.close(); + } + + private RecordedRequest takeRequest() throws InterruptedException { + RecordedRequest request = server.takeRequest(10, TimeUnit.SECONDS); + assertNotNull(request, "no request reached the mock server"); + return request; + } + + /** Asserts the full tracking header set every request must carry, then the module segment. */ + private void assertTrackingHeaders(RecordedRequest request, String expectedModule) { + assertEquals(CLIENT_PREFIX + "/" + expectedModule, request.getHeader(SDK_CLIENT)); + assertNotNull(request.getHeader(SDK_SESSION_ID)); + assertNull(request.getHeader(SDK_VERSION)); + assertNull(request.getHeader(SDK_MODULE)); + } + + private void enqueueJsonResponse() { + JsonObject output = new JsonObject(); + output.addProperty("text", "hi"); + output.addProperty("finish_reason", "stop"); + TestResponse rsp = TestResponse.builder().output(output).requestId("probe-request-id").build(); + server.enqueue( + new MockResponse() + .setBody(JsonUtils.toJson(rsp)) + .setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON)); + } + + private QwenParam qwenParam() { + return QwenParam.builder() + .model(Generation.Models.QWEN_TURBO) + .resultFormat(QwenParam.ResultFormat.TEXT) + .prompt("hi") + .build(); + } + + @Test + public void testGenerationSendsTrackingHeadersOnTheWire() throws Exception { + enqueueJsonResponse(); + Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", server.getPort()); + + new Generation().call(qwenParam()); + + RecordedRequest request = takeRequest(); + assertEquals("/services/aigc/text-generation/generation", request.getPath()); + assertTrackingHeaders(request, "aigc"); + } + + @Test + public void testAsyncTaskPollSendsTasksModuleOnTheWire() throws Exception { + enqueueJsonResponse(); + Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", server.getPort()); + AsyncTaskOption option = + AsyncTaskOption.builder() + .protocol(Protocol.HTTP) + .httpMethod(HttpMethod.GET) + .url("/tasks/task-id") + .build(); + AsyncTaskParam param = AsyncTaskParam.builder().taskId("task-id").build(); + + new SynchronizeHalfDuplexApi(option).call(param); + + RecordedRequest request = takeRequest(); + assertEquals("/tasks/task-id", request.getPath()); + assertTrackingHeaders(request, "tasks"); + } + + @Test + public void testWebSocketHandshakeSendsTrackingHeadersOnTheWire() throws Exception { + WebSocketRecorder serverListener = new WebSocketRecorder("server"); + server.enqueue(new MockResponse().withWebSocketUpgrade(serverListener)); + Constants.baseWebsocketApiUrl = + String.format("ws://127.0.0.1:%s/api-ws/v1/inference/", server.getPort()); + ApiServiceOption option = + ApiServiceOption.builder() + .protocol(Protocol.WEBSOCKET) + .streamingMode(StreamingMode.NONE) + .outputMode(OutputMode.ACCUMULATE) + .taskGroup("audio") + .task("tts") + .function("SpeechSynthesizer") + .build(); + HalfDuplexTestParam param = + HalfDuplexTestParam.builder().model("testModel").prompt("prompt").build(); + + new SynchronizeHalfDuplexApi<>(option) + .call( + param, + new ResultCallback() { + @Override + public void onEvent(DashScopeResult message) {} + + @Override + public void onComplete() {} + + @Override + public void onError(Exception e) {} + }); + + WebSocket wsServer = serverListener.assertOpen(); + RecordedRequest request = takeRequest(); + assertTrackingHeaders(request, "audio"); + wsServer.close(1000, "bye"); + } + + @Test + @SetEnvironmentVariable(key = "DASHSCOPE_DISABLE_SDK_HEADERS", value = "1") + public void testDisableEnvVarSuppressesHeadersOnTheWire() throws Exception { + enqueueJsonResponse(); + Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", server.getPort()); + + new Generation().call(qwenParam()); + + RecordedRequest request = takeRequest(); + assertNull(request.getHeader(SDK_CLIENT)); + assertNull(request.getHeader(SDK_SESSION_ID)); + assertNotNull(request.getHeader("Authorization")); + } +} diff --git a/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java b/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java new file mode 100644 index 00000000..c62e53eb --- /dev/null +++ b/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java @@ -0,0 +1,211 @@ +// Copyright (c) Alibaba, Inc. and its affiliates. + +package com.alibaba.dashscope.protocol; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.alibaba.dashscope.HalfDuplexTestParam; +import com.alibaba.dashscope.Version; +import com.alibaba.dashscope.audio.asr.phrase.AsrPhraseFinetuneOption; +import com.alibaba.dashscope.audio.asr.phrase.AsrPhraseOperationType; +import com.alibaba.dashscope.exception.ApiException; +import com.alibaba.dashscope.exception.NoApiKeyException; +import com.alibaba.dashscope.task.AsyncTaskParam; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junitpioneer.jupiter.SetEnvironmentVariable; + +/** + * Locks the SDK tracking header contract. Exactly two tracking headers are emitted: + * + *
+ *   x-dashscope-sdk-client:     java-sdk/<version>[/<module>]
+ *   x-dashscope-sdk-session-id: <process-wide uuid>
+ * 
+ * + * Version and module live inside the client value; {@code x-dashscope-sdk-version} and {@code + * x-dashscope-sdk-module} must not be emitted. + */ +@Execution(ExecutionMode.SAME_THREAD) +public class TestDashScopeHeaders { + private static final String SDK_CLIENT = "x-dashscope-sdk-client"; + private static final String SDK_SESSION_ID = "x-dashscope-sdk-session-id"; + private static final String SDK_VERSION = "x-dashscope-sdk-version"; + private static final String SDK_MODULE = "x-dashscope-sdk-module"; + private static final String CLIENT_PREFIX = "java-sdk/" + Version.version; + private static final String API_KEY = "sk-test"; + + private static Map httpHeaders(String module) throws NoApiKeyException { + return DashScopeHeaders.buildHttpHeaders( + API_KEY, false, Protocol.HTTP, false, false, null, new HashMap<>(), null, module); + } + + private static Map wsHeaders(String module) throws NoApiKeyException { + return DashScopeHeaders.buildWebSocketHeaders( + API_KEY, false, null, new HashMap<>(), null, module); + } + + @Test + public void testSdkClientCarriesModuleSegment() throws NoApiKeyException { + assertEquals(CLIENT_PREFIX + "/aigc", httpHeaders("aigc").get(SDK_CLIENT)); + assertEquals(CLIENT_PREFIX + "/audio", wsHeaders("audio").get(SDK_CLIENT)); + } + + @Test + public void testSdkClientOmitsModuleWhenNullOrBlank() throws NoApiKeyException { + for (String module : new String[] {null, ""}) { + String value = httpHeaders(module).get(SDK_CLIENT); + assertEquals(CLIENT_PREFIX, value); + assertFalse(value.endsWith("/")); + } + } + + @Test + public void testOnlyTwoTrackingHeadersAreEmitted() throws NoApiKeyException { + for (Map headers : new Map[] {httpHeaders("aigc"), wsHeaders("audio")}) { + assertTrue(headers.get(SDK_CLIENT).startsWith(CLIENT_PREFIX)); + assertFalse(headers.containsKey(SDK_VERSION)); + assertFalse(headers.containsKey(SDK_MODULE)); + } + assertFalse( + DashScopeHeaders.buildHttpHeaders( + API_KEY, false, Protocol.HTTP, false, false, null, new HashMap()) + .containsKey(SDK_VERSION)); + } + + @Test + public void testLegacyOverloadsOmitModule() throws NoApiKeyException { + assertEquals( + CLIENT_PREFIX, + DashScopeHeaders.buildHttpHeaders( + API_KEY, false, Protocol.HTTP, false, false, null, new HashMap()) + .get(SDK_CLIENT)); + // the 8th argument of this overload is customUserAgent, not module + assertEquals( + CLIENT_PREFIX, + DashScopeHeaders.buildHttpHeaders( + API_KEY, + false, + Protocol.HTTP, + false, + false, + null, + new HashMap(), + "my-app") + .get(SDK_CLIENT)); + assertEquals( + CLIENT_PREFIX, + DashScopeHeaders.buildWebSocketHeaders(API_KEY, false, null, new HashMap()) + .get(SDK_CLIENT)); + } + + @Test + public void testSessionIdIsProcessWideUuid() throws NoApiKeyException { + String fromHttp = httpHeaders("aigc").get(SDK_SESSION_ID); + String fromWebSocket = wsHeaders("audio").get(SDK_SESSION_ID); + assertEquals(fromHttp, fromWebSocket); + assertEquals(fromHttp, UUID.fromString(fromHttp).toString()); + } + + @Test + public void testCustomHeadersOverrideSdkClient() throws NoApiKeyException { + Map customHeaders = new HashMap<>(); + customHeaders.put(SDK_CLIENT, "my-client/9.9/my-module"); + assertEquals( + "my-client/9.9/my-module", + DashScopeHeaders.buildHttpHeaders( + API_KEY, false, Protocol.HTTP, false, false, null, customHeaders, null, "aigc") + .get(SDK_CLIENT)); + } + + @Test + @SetEnvironmentVariable(key = "DASHSCOPE_DISABLE_SDK_HEADERS", value = "1") + public void testDisableEnvVarSuppressesTrackingHeaders() throws NoApiKeyException { + Map headers = httpHeaders("aigc"); + assertNull(headers.get(SDK_CLIENT)); + assertNull(headers.get(SDK_SESSION_ID)); + // opting out must not drop auth or content negotiation + assertTrue(headers.containsKey("Authorization")); + assertTrue(headers.containsKey("user-agent")); + } + + @Test + @SetEnvironmentVariable(key = "DASHSCOPE_DISABLE_SDK_HEADERS", value = "TRUE") + public void testDisableEnvVarIsCaseInsensitive() throws NoApiKeyException { + assertNull(httpHeaders("aigc").get(SDK_CLIENT)); + assertNull(wsHeaders("audio").get(SDK_CLIENT)); + } + + @Test + public void testApiServiceOptionPrefersTaskGroupThenFunction() { + assertEquals( + "aigc", + ApiServiceOption.builder().taskGroup("aigc").function("generation").build().getModule()); + assertEquals( + "tokenizer", + ApiServiceOption.builder().taskGroup(null).function("tokenizer").build().getModule()); + assertNull(ApiServiceOption.builder().build().getModule()); + } + + @Test + public void testAsyncTaskOptionReportsTasks() { + assertEquals("tasks", AsyncTaskOption.builder().url("/tasks/task-id").build().getModule()); + } + + @Test + public void testGeneralServiceOptionReportsConfiguredModule() { + assertEquals( + "agentstudio", + GeneralServiceOption.builder().path("agents").module("agentstudio").build().getModule()); + assertNull(GeneralServiceOption.builder().path("agents").build().getModule()); + } + + @Test + public void testAsrPhraseFinetuneOptionReportsAudio() { + assertEquals( + "audio", + AsrPhraseFinetuneOption.builder() + .operationType(AsrPhraseOperationType.CREATE) + .build() + .getModule()); + } + + @Test + public void testRequestPropagatesModuleFromServiceOption() + throws NoApiKeyException, ApiException { + ApiServiceOption option = + ApiServiceOption.builder() + .protocol(Protocol.HTTP) + .httpMethod(HttpMethod.GET) + .taskGroup("aigc") + .task("text-generation") + .function("generation") + .build(); + HalfDuplexTestParam param = + HalfDuplexTestParam.builder().apiKey(API_KEY).model("qwen-turbo").prompt("hi").build(); + HalfDuplexRequest request = new HalfDuplexRequest(param, option); + assertEquals("aigc", request.getModule()); + assertEquals(CLIENT_PREFIX + "/aigc", request.getHttpRequest().getHeaders().get(SDK_CLIENT)); + } + + @Test + public void testAsyncTaskPollRequestCarriesTasksModule() throws NoApiKeyException, ApiException { + AsyncTaskOption option = + AsyncTaskOption.builder() + .protocol(Protocol.HTTP) + .httpMethod(HttpMethod.GET) + .url("/tasks/task-id") + .build(); + AsyncTaskParam param = AsyncTaskParam.builder().taskId("task-id").apiKey(API_KEY).build(); + HalfDuplexRequest request = new HalfDuplexRequest(param, option); + assertEquals("tasks", request.getModule()); + assertEquals(CLIENT_PREFIX + "/tasks", request.getHttpRequest().getHeaders().get(SDK_CLIENT)); + } +} From 8763fe2999126500c76986f189e43d2e59abe685 Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Thu, 3 Sep 2026 15:56:32 +0800 Subject: [PATCH 2/2] test(protocol): limit sdk tracking header tests to the two live headers The contract is exactly x-dashscope-sdk-client (client/version[/module]) plus x-dashscope-sdk-session-id. Drop references to the retired x-dashscope-sdk-version/x-dashscope-sdk-module names; absence of any other x-dashscope-sdk-* header is now guarded by prefix scanning, both at the builder level and on the wire. --- .../TestSdkClientTrackingHeader.java | 11 ++++++---- .../protocol/TestDashScopeHeaders.java | 22 +++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java b/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java index abe12a63..304fb8b4 100644 --- a/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java +++ b/src/test/java/com/alibaba/dashscope/TestSdkClientTrackingHeader.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.models.QwenParam; @@ -47,8 +48,7 @@ public class TestSdkClientTrackingHeader { MediaType.parse("application/json; charset=utf-8"); private static final String SDK_CLIENT = "x-dashscope-sdk-client"; private static final String SDK_SESSION_ID = "x-dashscope-sdk-session-id"; - private static final String SDK_VERSION = "x-dashscope-sdk-version"; - private static final String SDK_MODULE = "x-dashscope-sdk-module"; + private static final String SDK_HEADER_PREFIX = "x-dashscope-sdk-"; private static final String CLIENT_PREFIX = "java-sdk/" + Version.version; private MockWebServer server; @@ -79,8 +79,11 @@ private RecordedRequest takeRequest() throws InterruptedException { private void assertTrackingHeaders(RecordedRequest request, String expectedModule) { assertEquals(CLIENT_PREFIX + "/" + expectedModule, request.getHeader(SDK_CLIENT)); assertNotNull(request.getHeader(SDK_SESSION_ID)); - assertNull(request.getHeader(SDK_VERSION)); - assertNull(request.getHeader(SDK_MODULE)); + for (String name : request.getHeaders().names()) { + if (name.startsWith(SDK_HEADER_PREFIX)) { + assertTrue(SDK_CLIENT.equals(name) || SDK_SESSION_ID.equals(name), "unexpected: " + name); + } + } } private void enqueueJsonResponse() { diff --git a/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java b/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java index c62e53eb..7018c5b4 100644 --- a/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java +++ b/src/test/java/com/alibaba/dashscope/protocol/TestDashScopeHeaders.java @@ -30,15 +30,13 @@ * x-dashscope-sdk-session-id: <process-wide uuid> * * - * Version and module live inside the client value; {@code x-dashscope-sdk-version} and {@code - * x-dashscope-sdk-module} must not be emitted. + * No other {@code x-dashscope-sdk-*} header may be emitted. */ @Execution(ExecutionMode.SAME_THREAD) public class TestDashScopeHeaders { private static final String SDK_CLIENT = "x-dashscope-sdk-client"; private static final String SDK_SESSION_ID = "x-dashscope-sdk-session-id"; - private static final String SDK_VERSION = "x-dashscope-sdk-version"; - private static final String SDK_MODULE = "x-dashscope-sdk-module"; + private static final String SDK_HEADER_PREFIX = "x-dashscope-sdk-"; private static final String CLIENT_PREFIX = "java-sdk/" + Version.version; private static final String API_KEY = "sk-test"; @@ -52,6 +50,14 @@ private static Map wsHeaders(String module) throws NoApiKeyExcep API_KEY, false, null, new HashMap<>(), null, module); } + private static void assertOnlySdkTrackingHeaders(Map headers) { + for (String key : headers.keySet()) { + if (key.startsWith(SDK_HEADER_PREFIX)) { + assertTrue(SDK_CLIENT.equals(key) || SDK_SESSION_ID.equals(key), "unexpected: " + key); + } + } + } + @Test public void testSdkClientCarriesModuleSegment() throws NoApiKeyException { assertEquals(CLIENT_PREFIX + "/aigc", httpHeaders("aigc").get(SDK_CLIENT)); @@ -71,13 +77,11 @@ public void testSdkClientOmitsModuleWhenNullOrBlank() throws NoApiKeyException { public void testOnlyTwoTrackingHeadersAreEmitted() throws NoApiKeyException { for (Map headers : new Map[] {httpHeaders("aigc"), wsHeaders("audio")}) { assertTrue(headers.get(SDK_CLIENT).startsWith(CLIENT_PREFIX)); - assertFalse(headers.containsKey(SDK_VERSION)); - assertFalse(headers.containsKey(SDK_MODULE)); + assertOnlySdkTrackingHeaders(headers); } - assertFalse( + assertOnlySdkTrackingHeaders( DashScopeHeaders.buildHttpHeaders( - API_KEY, false, Protocol.HTTP, false, false, null, new HashMap()) - .containsKey(SDK_VERSION)); + API_KEY, false, Protocol.HTTP, false, false, null, new HashMap())); } @Test