mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +08:00
fix(tts): surface provider response diagnostics (#555)
This commit is contained in:
parent
b196dc73fd
commit
7563d2dd19
@ -0,0 +1,31 @@
|
|||||||
|
package vip.mate.tts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compact diagnostics for HTTP-based TTS provider failures.
|
||||||
|
*/
|
||||||
|
public final class TtsResponseDiagnostics {
|
||||||
|
|
||||||
|
static final int MAX_SNIPPET_CHARS = 200;
|
||||||
|
|
||||||
|
private TtsResponseDiagnostics() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String failureMessage(String provider, String endpoint, int status, String body) {
|
||||||
|
return provider + " 失败: endpoint=" + endpoint
|
||||||
|
+ ", status=" + status
|
||||||
|
+ ", body=" + snippet(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String snippet(String body) {
|
||||||
|
if (body == null || body.isBlank()) {
|
||||||
|
return "(空响应体)";
|
||||||
|
}
|
||||||
|
String collapsed = body.trim()
|
||||||
|
.replaceAll("(?i)Bearer\\s+[A-Za-z0-9._~+/=-]+", "Bearer [REDACTED]")
|
||||||
|
.replaceAll("\\s+", " ");
|
||||||
|
if (collapsed.length() <= MAX_SNIPPET_CHARS) {
|
||||||
|
return collapsed;
|
||||||
|
}
|
||||||
|
return collapsed.substring(0, MAX_SNIPPET_CHARS) + "...";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import vip.mate.llm.service.ModelProviderService;
|
|||||||
import vip.mate.system.model.SystemSettingsDTO;
|
import vip.mate.system.model.SystemSettingsDTO;
|
||||||
import vip.mate.tts.TtsProvider;
|
import vip.mate.tts.TtsProvider;
|
||||||
import vip.mate.tts.TtsRequest;
|
import vip.mate.tts.TtsRequest;
|
||||||
|
import vip.mate.tts.TtsResponseDiagnostics;
|
||||||
import vip.mate.tts.TtsResult;
|
import vip.mate.tts.TtsResult;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -101,7 +102,8 @@ public class DashScopeTtsProvider implements TtsProvider {
|
|||||||
body.put("speed", request.getSpeed());
|
body.put("speed", request.getSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse response = HttpRequest.post(BASE_URL + "/audio/speech")
|
String endpoint = BASE_URL + "/audio/speech";
|
||||||
|
HttpResponse response = HttpRequest.post(endpoint)
|
||||||
.header("Authorization", "Bearer " + apiKey)
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.body(body.toString())
|
.body(body.toString())
|
||||||
@ -115,7 +117,8 @@ public class DashScopeTtsProvider implements TtsProvider {
|
|||||||
} else {
|
} else {
|
||||||
String errBody = response.body();
|
String errBody = response.body();
|
||||||
log.warn("[DashScope TTS] Failed: HTTP {} - {}", response.getStatus(), errBody);
|
log.warn("[DashScope TTS] Failed: HTTP {} - {}", response.getStatus(), errBody);
|
||||||
return TtsResult.failure("DashScope TTS 失败: HTTP " + response.getStatus());
|
return TtsResult.failure(TtsResponseDiagnostics.failureMessage(
|
||||||
|
"DashScope TTS", endpoint, response.getStatus(), errBody));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[DashScope TTS] Error: {}", e.getMessage(), e);
|
log.error("[DashScope TTS] Error: {}", e.getMessage(), e);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import vip.mate.llm.service.ModelProviderService;
|
|||||||
import vip.mate.system.model.SystemSettingsDTO;
|
import vip.mate.system.model.SystemSettingsDTO;
|
||||||
import vip.mate.tts.TtsProvider;
|
import vip.mate.tts.TtsProvider;
|
||||||
import vip.mate.tts.TtsRequest;
|
import vip.mate.tts.TtsRequest;
|
||||||
|
import vip.mate.tts.TtsResponseDiagnostics;
|
||||||
import vip.mate.tts.TtsResult;
|
import vip.mate.tts.TtsResult;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -112,7 +113,8 @@ public class OpenAiTtsProvider implements TtsProvider {
|
|||||||
} else {
|
} else {
|
||||||
String errBody = response.body();
|
String errBody = response.body();
|
||||||
log.warn("[OpenAI TTS] Failed: HTTP {} - {}", response.getStatus(), errBody);
|
log.warn("[OpenAI TTS] Failed: HTTP {} - {}", response.getStatus(), errBody);
|
||||||
return TtsResult.failure("OpenAI TTS 失败: HTTP " + response.getStatus());
|
return TtsResult.failure(TtsResponseDiagnostics.failureMessage(
|
||||||
|
"OpenAI TTS", url, response.getStatus(), errBody));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[OpenAI TTS] Error: {}", e.getMessage(), e);
|
log.error("[OpenAI TTS] Error: {}", e.getMessage(), e);
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
package vip.mate.tts;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class TtsResponseDiagnosticsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("failureMessage includes provider, endpoint, status and sanitized body snippet")
|
||||||
|
void failureMessageIncludesActionableDiagnostics() {
|
||||||
|
String message = TtsResponseDiagnostics.failureMessage(
|
||||||
|
"DashScope TTS",
|
||||||
|
"https://dashscope.aliyuncs.com/compatible-mode/v1/audio/speech",
|
||||||
|
400,
|
||||||
|
"""
|
||||||
|
{"code":"InvalidParameter","message":"voice does not exist"}
|
||||||
|
""");
|
||||||
|
|
||||||
|
assertTrue(message.contains("DashScope TTS 失败"));
|
||||||
|
assertTrue(message.contains("endpoint=https://dashscope.aliyuncs.com/compatible-mode/v1/audio/speech"));
|
||||||
|
assertTrue(message.contains("status=400"));
|
||||||
|
assertTrue(message.contains("body={\"code\":\"InvalidParameter\",\"message\":\"voice does not exist\"}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("failureMessage truncates long response bodies")
|
||||||
|
void failureMessageTruncatesLongBodies() {
|
||||||
|
String body = "x".repeat(800);
|
||||||
|
|
||||||
|
String message = TtsResponseDiagnostics.failureMessage(
|
||||||
|
"OpenAI TTS",
|
||||||
|
"https://api.openai.com/v1/audio/speech",
|
||||||
|
500,
|
||||||
|
body);
|
||||||
|
|
||||||
|
assertTrue(message.contains("OpenAI TTS 失败"));
|
||||||
|
assertTrue(message.contains("status=500"));
|
||||||
|
assertTrue(message.contains("body="));
|
||||||
|
assertTrue(message.endsWith("..."));
|
||||||
|
assertTrue(message.length() < 420);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("snippet redacts bearer tokens and collapses whitespace")
|
||||||
|
void snippetRedactsSecretsAndCollapsesWhitespace() {
|
||||||
|
String snippet = TtsResponseDiagnostics.snippet("""
|
||||||
|
Authorization: Bearer sk-abcdef123456
|
||||||
|
gateway failed
|
||||||
|
""");
|
||||||
|
|
||||||
|
assertEquals("Authorization: Bearer [REDACTED] gateway failed", snippet);
|
||||||
|
assertFalse(snippet.contains("sk-abcdef"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user