diff --git a/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java b/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java index 75810bc..d9ac8fb 100644 --- a/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java +++ b/src/main/java/cn/van/business/util/ds/SocialMediaLlmClient.java @@ -180,8 +180,37 @@ public class SocialMediaLlmClient { return t.isEmpty() ? null : t; } + /** + * 火山方舟「Coding / OpenClaw」文档里的基址常为 {@code .../api/coding/v3},与 OpenAI 兼容对话接口 + * {@code .../api/v3/chat/completions} 不同,误填会导致 404。此处自动纠正常见误填。 + * + * @see 方舟对话 API + */ + private static String normalizeOpenAiCompatibleUrl(String chatCompletionsUrl) { + if (StrUtil.isBlank(chatCompletionsUrl)) { + return chatCompletionsUrl; + } + String u = chatCompletionsUrl.trim().replaceAll("/+$", ""); + if (!u.contains("volces.com") || u.contains("chat/completions")) { + return u; + } + if (u.contains("/api/coding/")) { + String fixed = u.replace("/api/coding/v3", "/api/v3/chat/completions"); + if (!fixed.equals(u)) { + return fixed; + } + } + return u; + } + private String callOpenAiCompatible(String chatCompletionsUrl, String apiKey, String model, String userText) throws IOException { + String endpoint = normalizeOpenAiCompatibleUrl(chatCompletionsUrl); + String originalTrim = chatCompletionsUrl == null ? "" : chatCompletionsUrl.trim().replaceAll("/+$", ""); + if (!endpoint.equals(originalTrim)) { + log.info("OpenAI 兼容 URL 已规范化: {} -> {}", originalTrim, endpoint); + } + Map requestBody = new HashMap<>(); requestBody.put("model", model); requestBody.put("messages", new Map[]{ @@ -191,7 +220,7 @@ public class SocialMediaLlmClient { String jsonBody = objectMapper.writeValueAsString(requestBody); - HttpRequest request = HttpRequest.of(chatCompletionsUrl.trim()) + HttpRequest request = HttpRequest.of(endpoint) .method(Method.POST) .header("Content-Type", "application/json") .header("Accept", "application/json") @@ -202,12 +231,21 @@ public class SocialMediaLlmClient { request.header("Authorization", "Bearer " + apiKey.trim()); } - log.info("请求 OpenAI 兼容 API: URL={}, model={}", chatCompletionsUrl, model); + log.info("请求 OpenAI 兼容 API: URL={}, model={}", endpoint, model); HttpResponse response = request.execute(); if (response.getStatus() != 200) { - log.error("OpenAI 兼容 API 失败: status={}, body={}", response.getStatus(), response.body()); - throw new IOException("API 调用失败,HTTP 状态码: " + response.getStatus()); + String body = response.body(); + log.error("OpenAI 兼容 API 失败: status={}, body={}", response.getStatus(), body); + String hint = ""; + if (response.getStatus() == 404) { + hint = "(若为火山方舟:请使用 .../api/v3/chat/completions ,不要用 Coding 文档中的 .../api/coding/v3 )"; + } + String snippet = ""; + if (StrUtil.isNotBlank(body)) { + snippet = " — " + (body.length() > 400 ? body.substring(0, 400) + "..." : body); + } + throw new IOException("API 调用失败,HTTP 状态码: " + response.getStatus() + hint + snippet); } JsonNode root = objectMapper.readTree(response.body());