diff --git a/pom.xml b/pom.xml index 4b1b1274..f672ae5b 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ DashScope Java SDK com.alibaba dashscope-sdk-java - 2.22.26 + 2.22.27 8 diff --git a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java index ad71ae6d..2e58dce7 100644 --- a/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java +++ b/src/main/java/com/alibaba/dashscope/common/DashScopeResult.java @@ -9,6 +9,7 @@ import com.alibaba.dashscope.utils.ApiKeywords; import com.alibaba.dashscope.utils.EncryptionUtils; import com.alibaba.dashscope.utils.JsonUtils; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.nio.ByteBuffer; import java.util.List; @@ -16,7 +17,9 @@ import java.util.stream.Collectors; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Data @EqualsAndHashCode(callSuper = true) public class DashScopeResult extends Result { @@ -34,110 +37,37 @@ protected T fromResponse(Protocol protocol, NetworkResponse r this.setHeaders(changeHeaders(response.getHeaders())); if (protocol == Protocol.WEBSOCKET) { if (response.getBinary() == null) { - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); - if (jsonObject.has(ApiKeywords.HEADER)) { - JsonObject headers = jsonObject.get(ApiKeywords.HEADER).getAsJsonObject(); - if (headers.has(ApiKeywords.TASKID)) { - this.setRequestId(headers.get(ApiKeywords.TASKID).getAsString()); - } - // Extract status_code, code and message from header - if (headers.has(ApiKeywords.STATUS_CODE)) { - this.setStatusCode( - headers.get(ApiKeywords.STATUS_CODE).isJsonNull() - ? null - : headers.get(ApiKeywords.STATUS_CODE).getAsInt()); - } else { - // Set default status code - this.setStatusCode(200); - } - if (headers.has(ApiKeywords.ERROR_CODE)) { - this.setCode( - headers.get(ApiKeywords.ERROR_CODE).isJsonNull() - ? "" - : headers.get(ApiKeywords.ERROR_CODE).getAsString()); - } else { - // Set default empty string for successful responses - this.setCode(""); - } - if (headers.has(ApiKeywords.ERROR_MESSAGE)) { - this.setMessage( - headers.get(ApiKeywords.ERROR_MESSAGE).isJsonNull() - ? "" - : headers.get(ApiKeywords.ERROR_MESSAGE).getAsString()); - } else { - // Set default empty string for successful responses - this.setMessage(""); - } - } - if (jsonObject.has(ApiKeywords.PAYLOAD)) { - JsonObject payload = jsonObject.getAsJsonObject(ApiKeywords.PAYLOAD); - if (payload.has(ApiKeywords.OUTPUT)) { - this.output = - payload.get(ApiKeywords.OUTPUT).isJsonNull() - ? null - : payload.get(ApiKeywords.OUTPUT); - } - if (payload.has(ApiKeywords.USAGE)) { - this.setUsage( - payload.get(ApiKeywords.USAGE).isJsonNull() - ? null - : payload.get(ApiKeywords.USAGE)); - } + String message = response.getMessage(); + if (message == null || message.isEmpty()) { + log.warn( + "WebSocket response message is null or empty, httpStatusCode: {}", + response.getHttpStatusCode()); + setInternalError(); + return (T) this; } + fromWebSocketMessage(message); } else { this.output = response.getBinary(); } } else { - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); - // Set HTTP status code if available + String message = response.getMessage(); + if (message == null || message.isEmpty()) { + log.warn( + "HTTP response message is null or empty, httpStatusCode: {}", + response.getHttpStatusCode()); + setInternalError(); + return (T) this; + } + JsonObject jsonObject = parseJson(message, "Failed to parse HTTP response as JSON: {}"); + if (jsonObject == null) { + return (T) this; + } if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); } - if (jsonObject.has(ApiKeywords.OUTPUT)) { - this.output = - jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.OUTPUT).getAsJsonObject(); - } - if (jsonObject.has(ApiKeywords.USAGE)) { - this.setUsage( - jsonObject.get(ApiKeywords.USAGE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.USAGE).getAsJsonObject()); - } - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - this.setRequestId(jsonObject.get(ApiKeywords.REQUEST_ID).getAsString()); - } - if (jsonObject.has(ApiKeywords.STATUS_CODE)) { - this.setStatusCode( - jsonObject.get(ApiKeywords.STATUS_CODE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.STATUS_CODE).getAsInt()); - } - if (jsonObject.has(ApiKeywords.CODE)) { - this.setCode( - jsonObject.get(ApiKeywords.CODE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.CODE).getAsString()); - } else { - // Set default empty string for successful responses - this.setCode(""); - } - if (jsonObject.has(ApiKeywords.MESSAGE)) { - this.setMessage( - jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.MESSAGE).getAsString()); - } else { - // Set default empty string for successful responses - this.setMessage(""); - } - if (jsonObject.has(ApiKeywords.DATA)) { - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - jsonObject.remove(ApiKeywords.REQUEST_ID); - } - this.output = jsonObject; - } + handleOutputField(jsonObject); + populateFromHttpJson(jsonObject); + handleDataField(jsonObject); } return (T) this; } @@ -148,24 +78,19 @@ public T fromResponse( Protocol protocol, NetworkResponse response, boolean isFlattenResult) throws ApiException { if (!isFlattenResult) { return fromResponse(protocol, response); - } else { - this.setHeaders(changeHeaders(response.getHeaders())); - // flatten not support websocket. - if (protocol == Protocol.WEBSOCKET) { - if (response.getBinary() == null) { - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); - this.output = jsonObject; - // convert to the result - } else { - this.output = response.getBinary(); - } - } else { // HTTP - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); - this.output = jsonObject; - this.event = response.getEvent(); + } + this.setHeaders(changeHeaders(response.getHeaders())); + if (protocol == Protocol.WEBSOCKET) { + if (response.getBinary() == null) { + this.output = parseJson(response.getMessage(), "Failed to parse WebSocket message"); + } else { + this.output = response.getBinary(); } - return (T) this; + } else { + this.output = parseJson(response.getMessage(), "Failed to parse HTTP response message"); + this.event = response.getEvent(); } + return (T) this; } @Override @@ -174,15 +99,25 @@ public T fromResponse( Protocol protocol, NetworkResponse response, boolean isFlattenResult, HalfDuplexRequest req) throws ApiException { this.setHeaders(changeHeaders(response.getHeaders())); - // check it's encrypted output if ((response.getHeaders().containsKey("X-DashScope-OutputEncrypted".toLowerCase()) || req.isEncryptRequest()) && protocol == Protocol.HTTP) { - // Set HTTP status code if available if (response.getHttpStatusCode() != null) { this.setStatusCode(response.getHttpStatusCode()); } - JsonObject jsonObject = JsonUtils.parse(response.getMessage()); + String encryptedMessage = response.getMessage(); + if (encryptedMessage == null || encryptedMessage.isEmpty()) { + log.warn( + "Encrypted HTTP response message is null or empty, httpStatusCode: {}", + response.getHttpStatusCode()); + setInternalError(); + return (T) this; + } + JsonObject jsonObject = + parseJson(encryptedMessage, "Failed to parse encrypted HTTP response message"); + if (jsonObject == null) { + return (T) this; + } String encryptedOutput = jsonObject.get(ApiKeywords.OUTPUT).isJsonNull() ? null @@ -193,53 +128,112 @@ public T fromResponse( encryptedOutput, req.getEncryptionConfig().getAESEncryptKey(), req.getEncryptionConfig().getIv()); - this.output = JsonUtils.parse(plainOutput); + this.output = parseJson(plainOutput, "Failed to parse decrypted output"); } else { this.output = null; } - if (jsonObject.has(ApiKeywords.USAGE)) { - this.setUsage( - jsonObject.get(ApiKeywords.USAGE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.USAGE).getAsJsonObject()); - } - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - this.setRequestId(jsonObject.get(ApiKeywords.REQUEST_ID).getAsString()); - } - if (jsonObject.has(ApiKeywords.STATUS_CODE)) { - this.setStatusCode( - jsonObject.get(ApiKeywords.STATUS_CODE).isJsonNull() - ? null - : jsonObject.get(ApiKeywords.STATUS_CODE).getAsInt()); - } - if (jsonObject.has(ApiKeywords.CODE)) { - this.setCode( - jsonObject.get(ApiKeywords.CODE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.CODE).getAsString()); - } else { - // Set default empty string for successful responses - this.setCode(""); - } - if (jsonObject.has(ApiKeywords.MESSAGE)) { - this.setMessage( - jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() - ? "" - : jsonObject.get(ApiKeywords.MESSAGE).getAsString()); - } else { - // Set default empty string for successful responses - this.setMessage(""); - } - if (jsonObject.has(ApiKeywords.DATA)) { - if (jsonObject.has(ApiKeywords.REQUEST_ID)) { - jsonObject.remove(ApiKeywords.REQUEST_ID); - } - } + populateFromHttpJson(jsonObject); return (T) this; } return fromResponse(protocol, response, isFlattenResult); } + private void fromWebSocketMessage(String message) { + JsonObject jsonObject = parseJson(message, "Failed to parse WebSocket message"); + if (jsonObject == null) { + return; + } + if (jsonObject.has(ApiKeywords.HEADER)) { + JsonObject headers = jsonObject.get(ApiKeywords.HEADER).getAsJsonObject(); + if (headers.has(ApiKeywords.TASKID)) { + this.setRequestId(headers.get(ApiKeywords.TASKID).getAsString()); + } + this.setStatusCode( + headers.has(ApiKeywords.STATUS_CODE) && !headers.get(ApiKeywords.STATUS_CODE).isJsonNull() + ? headers.get(ApiKeywords.STATUS_CODE).getAsInt() + : 200); + this.setCode( + headers.has(ApiKeywords.ERROR_CODE) && !headers.get(ApiKeywords.ERROR_CODE).isJsonNull() + ? headers.get(ApiKeywords.ERROR_CODE).getAsString() + : ""); + this.setMessage( + headers.has(ApiKeywords.ERROR_MESSAGE) + && !headers.get(ApiKeywords.ERROR_MESSAGE).isJsonNull() + ? headers.get(ApiKeywords.ERROR_MESSAGE).getAsString() + : ""); + } + if (jsonObject.has(ApiKeywords.PAYLOAD)) { + JsonObject payload = jsonObject.getAsJsonObject(ApiKeywords.PAYLOAD); + if (payload.has(ApiKeywords.OUTPUT)) { + this.output = + payload.get(ApiKeywords.OUTPUT).isJsonNull() ? null : payload.get(ApiKeywords.OUTPUT); + } + if (payload.has(ApiKeywords.USAGE)) { + this.setUsage( + payload.get(ApiKeywords.USAGE).isJsonNull() ? null : payload.get(ApiKeywords.USAGE)); + } + } + } + + private void handleOutputField(JsonObject jsonObject) { + // Handle OUTPUT field first + if (this.output == null && jsonObject.has(ApiKeywords.OUTPUT)) { + JsonElement outputElement = jsonObject.get(ApiKeywords.OUTPUT); + this.output = (outputElement == null || outputElement.isJsonNull()) ? null : outputElement; + } + } + + private void handleDataField(JsonObject jsonObject) { + // Handle DATA field only if OUTPUT was not present + if (this.output == null && jsonObject.has(ApiKeywords.DATA)) { + jsonObject.remove(ApiKeywords.REQUEST_ID); + this.output = jsonObject; + } + } + + private void populateFromHttpJson(JsonObject jsonObject) { + if (jsonObject.has(ApiKeywords.USAGE)) { + this.setUsage( + jsonObject.get(ApiKeywords.USAGE).isJsonNull() + ? null + : jsonObject.get(ApiKeywords.USAGE).getAsJsonObject()); + } + if (jsonObject.has(ApiKeywords.REQUEST_ID)) { + this.setRequestId(jsonObject.get(ApiKeywords.REQUEST_ID).getAsString()); + } + if (jsonObject.has(ApiKeywords.STATUS_CODE)) { + this.setStatusCode( + jsonObject.get(ApiKeywords.STATUS_CODE).isJsonNull() + ? null + : jsonObject.get(ApiKeywords.STATUS_CODE).getAsInt()); + } + this.setCode( + jsonObject.has(ApiKeywords.CODE) && !jsonObject.get(ApiKeywords.CODE).isJsonNull() + ? jsonObject.get(ApiKeywords.CODE).getAsString() + : ""); + this.setMessage( + jsonObject.has(ApiKeywords.MESSAGE) && !jsonObject.get(ApiKeywords.MESSAGE).isJsonNull() + ? jsonObject.get(ApiKeywords.MESSAGE).getAsString() + : ""); + } + + private JsonObject parseJson(String raw, String logMsg) { + try { + return JsonUtils.parse(raw); + } catch (Exception e) { + log.error(logMsg, e); + setInternalError(); + return null; + } + } + + private void setInternalError() { + this.output = null; + this.setStatusCode(PublicErrorDef.INTERNAL_ERROR.getStatusCode()); + this.setCode(PublicErrorDef.INTERNAL_ERROR.getErrorCode()); + this.setMessage(PublicErrorDef.INTERNAL_ERROR.getErrorMsg()); + } + private Map changeHeaders(Map> headers) { if (headers == null || headers.isEmpty()) { return null; diff --git a/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java b/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java new file mode 100644 index 00000000..e8be4fa5 --- /dev/null +++ b/src/main/java/com/alibaba/dashscope/common/PublicErrorDef.java @@ -0,0 +1,112 @@ +// Copied from com.alibaba.dashscope:error:1.0.0 +package com.alibaba.dashscope.common; + +// Auto-generated by tools/generate.py -- DO NOT EDIT. +import java.util.Map; + +/** Customer-facing error definitions. */ +public enum PublicErrorDef { + INVALID_REQUEST( + 400, + "BadRequestError", + "The request is invalid. Please check the request and try again.", + "invalid_request_error"), + MISSING_PARAMETER( + 400, "BadRequestError", "Missing required parameter: {parameter}.", "invalid_request_error"), + CONTENT_POLICY_VIOLATION( + 400, + "BadRequestError", + "The request was rejected by content policy.", + "invalid_request_error"), + INVALID_URL( + 400, + "BadRequestError", + "The provided URL is invalid or cannot be accessed.", + "invalid_request_error"), + INVALID_FILE(400, "BadRequestError", "The provided file is invalid.", "invalid_request_error"), + AUTH_FAILED( + 401, + "AuthenticationError", + "Authentication failed. Please provide valid authentication credentials.", + "authentication_error"), + INVALID_API_KEY( + 401, "AuthenticationError", "Incorrect API key provided.", "authentication_error"), + PERMISSION_DENIED( + 403, + "PermissionDeniedError", + "You do not have permission to access this resource.", + "permission_error"), + RESOURCE_NOT_FOUND( + 404, "NotFoundError", "The requested resource was not found: {resource}.", "not_found_error"), + REQUEST_TOO_LARGE( + 413, + "RequestTooLargeError", + "The request exceeds the maximum allowed size of {limit}.", + "request_too_large"), + RATE_LIMIT_EXCEEDED( + 429, + "RateLimitError", + "Rate limit exceeded. Please slow down your requests.", + "rate_limit_error"), + CONCURRENCY_LIMIT_EXCEEDED( + 429, + "RateLimitError", + "Too many concurrent requests. Please reduce concurrency and try again.", + "rate_limit_error"), + INSUFFICIENT_QUOTA( + 429, + "RateLimitError", + "You exceeded your current quota. Please check your plan and billing details.", + "billing_error"), + INTERNAL_ERROR( + 500, + "InternalServerError", + "The server encountered an internal error. Please try again later.", + "api_error"), + SERVICE_UNAVAILABLE( + 503, + "ServiceUnavailableError", + "The service is temporarily unavailable. Please try again later.", + "overloaded_error"), + REQUEST_TIMEOUT( + 504, + "GatewayTimeoutError", + "The request timed out. Please try again later.", + "timeout_error"); + + private final int statusCode; + private final String errorCode; + private final String errorMsg; + private final String anthropicErrorCode; + + PublicErrorDef(int statusCode, String errorCode, String errorMsg, String anthropicErrorCode) { + this.statusCode = statusCode; + this.errorCode = errorCode; + this.errorMsg = errorMsg; + this.anthropicErrorCode = anthropicErrorCode; + } + + public int getStatusCode() { + return statusCode; + } + + public String getErrorCode() { + return errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public String getAnthropicErrorCode() { + return anthropicErrorCode; + } + + public String formatMsg(Map vars) { + String msg = errorMsg; + for (Map.Entry entry : vars.entrySet()) { + msg = msg.replace("{" + entry.getKey() + "}", entry.getValue()); + } + return msg; + } +} diff --git a/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java b/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java index 80333e5d..d0bda500 100644 --- a/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java +++ b/src/main/java/com/alibaba/dashscope/threads/runs/DefaultAssistantEventHandler.java @@ -188,7 +188,7 @@ public void onError(String errorMsg) { @Override public void onUnknown(String msg) { - System.out.println(msg); + // Ignored } @Override