diff --git a/backend/internal/app/task_media_recovery.go b/backend/internal/app/task_media_recovery.go index b81540e0b..e6746e586 100644 --- a/backend/internal/app/task_media_recovery.go +++ b/backend/internal/app/task_media_recovery.go @@ -110,9 +110,6 @@ func recoverProtocolMedia(ctx context.Context, config providerConfig, mode strin if len(references) == 0 || len(references) > 32 { return nil, true, &mediaRecoveryError{stage: "checkpoint", cause: errors.New("生成结果数量超过恢复限制")} } - if mode != "image" { - references = references[:1] - } if task.MediaRecoveryJSON != "" { result, err := s.resumeTaskMedia(ctx, task) return result, true, err @@ -246,6 +243,10 @@ func (s *Service) materializeTaskMedia(ctx context.Context, task *model.Task, co result = map[string]interface{}{"mode": checkpoint.Mode} if checkpoint.Mode == "image" { result["images"] = items + } else if checkpoint.Mode == "audio" { + result["audios"] = items + // Keep the legacy singular field for clients that render one audio result. + result["audio"] = items[0] } else { result[checkpoint.Mode] = items[0] } diff --git a/backend/internal/app/task_media_recovery_test.go b/backend/internal/app/task_media_recovery_test.go index d3ba3e95d..0d9c80927 100644 --- a/backend/internal/app/task_media_recovery_test.go +++ b/backend/internal/app/task_media_recovery_test.go @@ -409,3 +409,37 @@ func TestMediaRecoveryExpiredLeaseAndStagingLimits(t *testing.T) { t.Fatal("staging budget exceeded") } } + +func TestMediaRecoveryAudioKeepsAllReturnedResults(t *testing.T) { + t.Setenv("CANVAS_ALLOWED_PRIVATE_UPSTREAM_HOSTS", "127.0.0.1") + s, db := newMediaRecoveryTestService(t) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "audio/mpeg") + _, _ = w.Write([]byte("audio-result")) + })) + defer server.Close() + expires := time.Now().Add(time.Hour) + input, err := json.Marshal(canvasGenerationInput{Mode: "audio", Config: providerConfig{BaseURL: server.URL}, Prompt: "test"}) + if err != nil { + t.Fatal(err) + } + task := &model.Task{ID: newID(), UserID: "media-user", Type: "canvas_audio", Status: model.TaskStatusRunning, InputJSON: string(input), LeaseOwner: "worker-a", LeaseExpiresAt: &expires, RouteRun: 1, CreatedAt: time.Now()} + if err := db.Create(task).Error; err != nil { + t.Fatal(err) + } + ctx := context.WithValue(withProviderAnalytics(context.Background(), s, *task), mediaExecutionTaskKey{}, *task) + result, handled, recoveryErr := recoverProtocolMedia(ctx, providerConfig{BaseURL: server.URL}, "audio", []protocol.MediaReference{ + {URL: server.URL + "/voice.wav", MIMEType: "audio/mpeg"}, + {URL: server.URL + "/emotion.wav", MIMEType: "audio/mpeg"}, + }) + if !handled || recoveryErr != nil { + t.Fatalf("handled=%v err=%v", handled, recoveryErr) + } + audios, ok := result["audios"].([]interface{}) + if !ok || len(audios) != 2 { + t.Fatalf("audios=%#v result=%#v", result["audios"], result) + } + if _, ok := result["audio"]; !ok { + t.Fatal("legacy singular audio result missing") + } +}