fix(gemini): 移除空返图占位并暴露失败原因

Gemini 图片响应未包含可提取资源时,不再返回伪造占位图,改为非重试失败并保留安全拦截、候选结束状态和上游错误等结构化诊断。\n\n同步将诊断写入 HTTP 错误、任务结果、attempt 指标和日志,确保任务失败时不结算,并补充单元及 PostgreSQL 验收测试。\n\n验证:gofmt、go test ./... -count=1、go vet ./...、迁移安全检查、pnpm openapi。
This commit is contained in:
2026-07-29 19:50:43 +08:00
parent 012823adff
commit 3886048e0f
9 changed files with 506 additions and 8 deletions
@@ -3,6 +3,7 @@ package runner
import (
"testing"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/clients"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)
@@ -45,3 +46,27 @@ func TestAttemptMetricsIncludesCacheAffinityCoefficient(t *testing.T) {
t.Fatalf("expected cache affinity routing diagnostics in attempt metrics, got %+v", metrics)
}
}
func TestFailureRecordIncludesStructuredClientErrorDetails(t *testing.T) {
cause := &clients.ClientError{
Code: "gemini_image_output_missing",
Message: "Gemini image response contains no extractable image: prompt blocked",
Details: map[string]any{
"provider": "gemini",
"reason": "prompt_blocked",
"promptBlockReason": "SAFETY",
},
}
_, metrics, _, _, _ := failureMetrics(cause, false)
details, _ := metrics["errorDetails"].(map[string]any)
if details["reason"] != "prompt_blocked" || details["promptBlockReason"] != "SAFETY" {
t.Fatalf("failure metrics lost client error details: %+v", metrics)
}
result := buildFailureResult(cause.Code, cause.Message, "", cause)
errorPayload, _ := result["error"].(map[string]any)
resultDetails, _ := errorPayload["details"].(map[string]any)
if resultDetails["provider"] != "gemini" || resultDetails["reason"] != "prompt_blocked" {
t.Fatalf("failure result lost client error details: %+v", result)
}
}