fix(audio): 兼容百炼 OpenAI 音频输入

为阿里云百炼 OpenAI-compatible Chat 统一保留标准 input_audio,并将旧版 audio_url 转换为 input_audio,同时根据 MIME 或扩展名补齐音频格式。

补充请求资产水合测试,确认 input_audio.data 会转换为裸 Base64 且保留 format。

验证:在 apps/api 执行 env -u AI_GATEWAY_TEST_DATABASE_URL go test ./... -count=1 全部通过。
This commit is contained in:
2026-07-27 23:50:17 +08:00
parent 95776c84f6
commit 046c16fc69
4 changed files with 297 additions and 0 deletions
@@ -45,6 +45,56 @@ func TestHydrateProviderRequestAssetsConvertsStrictBase64Field(t *testing.T) {
}
}
func TestHydrateProviderRequestAssetsConvertsInputAudioDataToRawBase64(t *testing.T) {
storageDir := t.TempDir()
fileName := "gateway-request-asset-audio.mp3"
payload := []byte("audio bytes")
if err := os.WriteFile(filepath.Join(storageDir, fileName), payload, 0o644); err != nil {
t.Fatalf("write request asset: %v", err)
}
service := &Service{cfg: config.Config{LocalUploadedStorageDir: storageDir}}
body := map[string]any{
"messages": []any{
map[string]any{
"role": "user",
"content": []any{
map[string]any{
"type": "input_audio",
"input_audio": map[string]any{
"data": map[string]any{
"assetRef": map[string]any{
"sha256": "sha-input-audio",
"contentType": "audio/mpeg",
"url": "/static/uploaded/" + fileName,
"storageProvider": "local_static",
},
"url": "/static/uploaded/" + fileName,
},
"format": "mp3",
},
},
},
},
},
}
hydrated, err := service.hydrateProviderRequestAssets(context.Background(), body, store.RuntimeModelCandidate{})
if err != nil {
t.Fatalf("hydrate request assets: %v", err)
}
messages := hydrated["messages"].([]any)
message := messages[0].(map[string]any)
content := message["content"].([]any)
audioPart := content[0].(map[string]any)
inputAudio := audioPart["input_audio"].(map[string]any)
if got, want := stringFromAny(inputAudio["data"]), base64.StdEncoding.EncodeToString(payload); got != want {
t.Fatalf("unexpected hydrated input audio: got %q want %q", got, want)
}
if inputAudio["format"] != "mp3" {
t.Fatalf("input audio format was not preserved: %+v", inputAudio)
}
}
func TestHydrateProviderRequestAssetsConvertsBase64ArrayField(t *testing.T) {
storageDir := t.TempDir()
fileName := "gateway-request-asset-array.png"