fix(runtime): 上游错误体统一走 trace.UpstreamError,正文不进 error

M41 的「LLM / embedding / 视觉 上游错误体」一族(M38 #5 #6 #7 #8 #9 #14):
非 2xx 时把响应体截断后拼进 error,而 error 会被 chat run / loop step /
retrieve_fallback 打日志,等于把她的提问、记忆正文、要念的台词写进日志。

- model:openai/anthropic 两处 upstreamError 收口到 upstream.go 的
  providerUpstreamError;anthropic 流内 error 事件的 evt.Error.Message
  (原本无上限)整段丢弃,只保留令牌形状的 type
- embedding:readErrorBody 删除,改走 helper(input 就是她的记忆正文)
- vision:错误路径不再沿用取图的 50 MiB 上限,改为 4 KiB 有界读且不拼原文
- voice:classifyDashscopeStatus 只解 code/request_id,正文只留在内存里做
  分类判据;错误体读取同样改为 4 KiB 有界
- 测试:五个上游各一条「日志/error 里搜不到哨兵正文」用例,且能变异打红
This commit is contained in:
AskaEth committed 2026-10-26 13:44:32 +08:00
1 parent 46a6da13e0
commit 66dda084cd
16 files changed
+570 -183

No files matched your search

@@ -0,0 +1,111 @@
package voice
// DashScope 的线格式类型(请求体 / 响应 envelope)。它们只描述 JSON 形状,
// 不含任何逻辑,因此与适配器行为分开成文件。
// dashscopeSTTResponse 是 DashScope ASR 响应 envelope。
type dashscopeSTTResponse struct {
Output struct {
Output struct {
Sentence struct {
Text string `json:"text"`
} `json:"sentence"`
Text string `json:"text"`
} `json:"output"`
Text string `json:"text"`
Choices []struct {
Message struct {
Content []struct {
Text string `json:"text"`
} `json:"content"`
} `json:"message"`
} `json:"choices"`
} `json:"output"`
Text string `json:"text"`
}
// dashscopeTTSResponse 是 DashScope TTS 响应 envelope。
type dashscopeTTSResponse struct {
Output struct {
Audio struct {
URL string `json:"url"`
Data string `json:"data"`
} `json:"audio"`
} `json:"output"`
}
// dashscopeSTTRequest 是 ASR 请求体。
type dashscopeSTTRequest struct {
Model string `json:"model"`
Input dashscopeSTTInput `json:"input"`
Parameters dashscopeSTTParameters `json:"parameters"`
}
// dashscopeSTTInput 承载多模态消息列表。
type dashscopeSTTInput struct {
Messages []dashscopeSTTMessage `json:"messages"`
}
// dashscopeSTTMessage 是一个 ASR 用户回合。
type dashscopeSTTMessage struct {
Role string `json:"role"`
Content []dashscopeSTTContent `json:"content"`
}
// dashscopeSTTContent 是一个音频内容块。
type dashscopeSTTContent struct {
Type string `json:"type"`
InputAudio dashscopeInputAudio `json:"input_audio"`
}
// dashscopeInputAudio 承载 base64 data: URI 音频。
type dashscopeInputAudio struct {
Data string `json:"data"`
}
// dashscopeSTTParameters 调整 ASR 请求。
type dashscopeSTTParameters struct {
Format string `json:"format"`
SampleRate int `json:"sample_rate"`
LanguageHints []string `json:"language_hints,omitempty"`
}
// dashscopeTTSRequest 是 TTS 请求体。
type dashscopeTTSRequest struct {
Model string `json:"model"`
Input dashscopeTTSInput `json:"input"`
}
// dashscopeTTSInput 承载文字 + 音色 + 输出格式。
type dashscopeTTSInput struct {
Text string `json:"text"`
Voice string `json:"voice"`
Format string `json:"format"`
SampleRate int `json:"sample_rate"`
// Instruction 是 CosyVoice 的情绪/语气 instruction;omitempty 使
// 系统音色(必须使用固定 instruction)不携带自由形式的 instruction。
Instruction string `json:"instruction,omitempty"`
}
// dashscopeCloneRequest 是音色注册 create_voice 请求体。
type dashscopeCloneRequest struct {
Model string `json:"model"`
Input dashscopeCloneInput `json:"input"`
}
// dashscopeCloneInput 承载 create_voice 动作参数。
type dashscopeCloneInput struct {
Action string `json:"action"`
TargetModel string `json:"target_model"`
Prefix string `json:"prefix"`
URL string `json:"url"`
}
// dashscopeCloneResponse 是音色注册响应 envelope。顶层
// message 表示模型级错误——即使 HTTP 200 也会暴露。
type dashscopeCloneResponse struct {
Output struct {
VoiceID string `json:"voice_id"`
} `json:"output"`
Message string `json:"message"`
}