fix(aihr): redact model failure details
This commit is contained in:
+26
-6
@@ -230,7 +230,7 @@ public class AihrModelSeedService {
|
||||
modelName,
|
||||
"真实模型调用失败,已回退内置默认兜底:先确认事实、同步时限、再生成工单闭环。",
|
||||
"openai-compatible-failed",
|
||||
e.getMessage(),
|
||||
safeFailureCode(e),
|
||||
List.of("检查 aihr_model_provider.api_host/api_key 和 aihr_model_config.model_name 是否与供应商一致。")
|
||||
);
|
||||
}
|
||||
@@ -291,7 +291,8 @@ public class AihrModelSeedService {
|
||||
try {
|
||||
return Optional.of(callOpenAiCompatible(runtime, runtime.modelName(), userPrompt, systemPrompt, temperature));
|
||||
} catch (Exception e) {
|
||||
log.warn("aihr llm tryChat failed, caller falls back to seed: {}", e.getMessage());
|
||||
log.warn("event=aihr_chat_call_failed providerCode={} modelName={} exception={}",
|
||||
runtime.providerCode(), runtime.modelName(), e.getClass().getSimpleName());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -416,21 +417,26 @@ public class AihrModelSeedService {
|
||||
.send(builder.build(), HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||
throw new IllegalStateException("LLM HTTP " + response.statusCode() + ": " + truncate(response.body(), 240));
|
||||
throw new IllegalStateException(httpFailureCode(response.statusCode()));
|
||||
}
|
||||
|
||||
JsonNode root = objectMapper.readTree(response.body());
|
||||
JsonNode root;
|
||||
try {
|
||||
root = objectMapper.readTree(response.body());
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("LLM_RESPONSE_INVALID_JSON");
|
||||
}
|
||||
return parseChatCallResult(root, modelName);
|
||||
}
|
||||
|
||||
static ChatCallResult parseChatCallResult(JsonNode root, String fallbackModelName) {
|
||||
JsonNode choices = root.path("choices");
|
||||
if (!choices.isArray() || choices.isEmpty()) {
|
||||
throw new IllegalStateException("LLM response missing choices");
|
||||
throw new IllegalStateException("LLM_RESPONSE_CHOICES_MISSING");
|
||||
}
|
||||
String content = choices.get(0).path("message").path("content").asText();
|
||||
if (isBlank(content)) {
|
||||
throw new IllegalStateException("LLM response missing message.content");
|
||||
throw new IllegalStateException("LLM_RESPONSE_CONTENT_MISSING");
|
||||
}
|
||||
String responseModel = root.path("model").asText();
|
||||
String actualModel = isBlank(responseModel) ? fallbackModelName : responseModel;
|
||||
@@ -439,6 +445,20 @@ public class AihrModelSeedService {
|
||||
return new ChatCallResult(content, actualModel, inputTokens, outputTokens);
|
||||
}
|
||||
|
||||
static String httpFailureCode(int statusCode) {
|
||||
return "LLM_HTTP_" + statusCode;
|
||||
}
|
||||
|
||||
static String safeFailureCode(Exception exception) {
|
||||
if (exception instanceof IllegalStateException) {
|
||||
String message = exception.getMessage();
|
||||
if (message != null && message.matches("LLM_(HTTP_[0-9]{3}|RESPONSE_[A-Z_]+)")) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
return "LLM_CALL_FAILED_" + exception.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private RuntimeConfig runtimeConfig(String requestedModel) {
|
||||
Optional<RuntimeConfig> resolved = dbRuntimeConfig(requestedModel);
|
||||
if (resolved.isEmpty() && !isBlank(requestedModel)) {
|
||||
|
||||
+17
@@ -7,6 +7,8 @@ import org.junit.jupiter.api.Tag;
|
||||
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.assertThrows;
|
||||
|
||||
@Tag("dev")
|
||||
class AihrModelSeedServiceTest {
|
||||
@@ -43,4 +45,19 @@ class AihrModelSeedServiceTest {
|
||||
assertEquals(0, result.inputTokens());
|
||||
assertEquals(0, result.outputTokens());
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureCodesNeverExposeProviderBodyOrExceptionMessage() throws Exception {
|
||||
String sensitive = "api_key=secret prompt=业主手机号13800000000 raw-provider-body";
|
||||
|
||||
assertEquals("LLM_HTTP_502", AihrModelSeedService.httpFailureCode(502));
|
||||
String safe = AihrModelSeedService.safeFailureCode(new IllegalStateException(sensitive));
|
||||
assertEquals("LLM_CALL_FAILED_IllegalStateException", safe);
|
||||
assertFalse(safe.contains(sensitive));
|
||||
|
||||
IllegalStateException missing = assertThrows(IllegalStateException.class,
|
||||
() -> AihrModelSeedService.parseChatCallResult(objectMapper.readTree("{}"), "configured-model"));
|
||||
assertEquals("LLM_RESPONSE_CHOICES_MISSING", missing.getMessage());
|
||||
assertFalse(missing.getMessage().contains(sensitive));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user