文案优化
This commit is contained in:
@@ -74,7 +74,7 @@ func (p ProviderProcessor) Advance(ctx context.Context, job Job) (Job, error) {
|
||||
ErrorClass: "unknown_outcome",
|
||||
})
|
||||
failed := StatusFailed
|
||||
failure := &JobError{Message: "provider submission outcome is unknown; refusing duplicate submission", Retryable: false}
|
||||
failure := &JobError{Message: unknownProviderSubmissionMessage, Retryable: false}
|
||||
if p.Store == nil {
|
||||
job.Status, job.Error = failed, failure
|
||||
return job, nil
|
||||
@@ -103,7 +103,7 @@ func (p ProviderProcessor) Advance(ctx context.Context, job Job) (Job, error) {
|
||||
}
|
||||
if err != nil && job.ProviderTaskID == "" && p.Store != nil {
|
||||
failed := StatusFailed
|
||||
failure := &JobError{Message: "provider submission outcome is unknown; refusing duplicate submission", Retryable: false}
|
||||
failure := providerSubmissionFailure(err)
|
||||
return p.Store.UpdateJob(ctx, job.ID, workerPatch(job, Patch{Status: &failed, Error: failure}))
|
||||
}
|
||||
if err != nil {
|
||||
@@ -152,6 +152,20 @@ func (p ProviderProcessor) Advance(ctx context.Context, job Job) (Job, error) {
|
||||
return job, nil
|
||||
}
|
||||
|
||||
const (
|
||||
unknownProviderSubmissionMessage = "provider submission outcome is unknown; refusing duplicate submission"
|
||||
outputImageSafetyMessage = "生成结果触发内容安全审核,请更换素材或调整内容后重试。"
|
||||
)
|
||||
|
||||
func providerSubmissionFailure(err error) *JobError {
|
||||
message := unknownProviderSubmissionMessage
|
||||
var providerError *providers.ProviderError
|
||||
if errors.As(err, &providerError) && (providerError.Code == "OutputImageSensitiveContentDetected" || strings.HasPrefix(providerError.Code, "OutputImageSensitiveContentDetected.")) {
|
||||
message = outputImageSafetyMessage
|
||||
}
|
||||
return &JobError{Message: message, Retryable: false}
|
||||
}
|
||||
|
||||
type providerFailureDiagnostic struct {
|
||||
JobID string
|
||||
Provider string
|
||||
|
||||
@@ -415,7 +415,7 @@ func TestProviderProcessorLogsSafeJobCorrelationWhenSubmitFails(t *testing.T) {
|
||||
processor := ProviderProcessor{Providers: ProviderRegistry{"volcengine-visual": adapter}, Store: store}
|
||||
|
||||
got, err := processor.Advance(context.Background(), job)
|
||||
if err != nil || got.Status != StatusFailed {
|
||||
if err != nil || got.Status != StatusFailed || got.Error == nil || got.Error.Message != unknownProviderSubmissionMessage {
|
||||
t.Fatalf("got=%#v err=%v", got, err)
|
||||
}
|
||||
logged := output.String()
|
||||
@@ -431,6 +431,52 @@ func TestProviderProcessorLogsSafeJobCorrelationWhenSubmitFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderProcessorPersistsFriendlySeedreamOutputSafetyMessage(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
|
||||
if request.URL.Path != "/api/v3/images/generations" {
|
||||
t.Fatalf("request path = %q", request.URL.Path)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(`{
|
||||
"error": {
|
||||
"code": "OutputImageSensitiveContentDetected.PolicyViolation",
|
||||
"type": "BadRequest",
|
||||
"message": "private upstream moderation detail"
|
||||
}
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
store := newMemoryJobStore()
|
||||
job := Job{
|
||||
ID: "job-seedream-output-safety", OwnerID: "owner", Provider: "seedream", ReqKey: providers.Seedream50ProModel,
|
||||
Capability: "image.generate", Status: StatusRunning, LockedBy: "worker",
|
||||
RequestPayload: json.RawMessage(`{
|
||||
"capability":"image.generate",
|
||||
"model":"doubao-seedream-5-0-pro-260628",
|
||||
"prompt":"拆分元素",
|
||||
"inputUrls":["https://assets.test/source.jpg"],
|
||||
"settings":{"layerDecomposition":true,"size":"2K","outputFormat":"png"}
|
||||
}`),
|
||||
}
|
||||
store.jobs[job.ID] = job
|
||||
adapter := providers.NewSeedream(providers.Config{
|
||||
BaseURL: server.URL + "/api/v3", APIKey: "private-api-key", Model: providers.Seedream50ProModel,
|
||||
}, server.Client())
|
||||
processor := ProviderProcessor{Providers: ProviderRegistry{"seedream": adapter}, Store: store}
|
||||
|
||||
got, err := processor.Advance(context.Background(), job)
|
||||
const want = "生成结果触发内容安全审核,请更换素材或调整内容后重试。"
|
||||
if err != nil || got.Status != StatusFailed || got.Error == nil || got.Error.Message != want || got.Error.Retryable {
|
||||
t.Fatalf("got=%#v err=%v", got, err)
|
||||
}
|
||||
persisted := store.jobs[job.ID]
|
||||
if persisted.Error == nil || persisted.Error.Message != want {
|
||||
t.Fatalf("persisted error = %#v", persisted.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderProcessorClearsTransientErrorAfterSuccessfulPoll(t *testing.T) {
|
||||
store := newMemoryJobStore()
|
||||
job := Job{ID: "job-recovered", OwnerID: "owner", Provider: "fixture", ReqKey: "model-a", Capability: "image.generate", Status: StatusQueued, LockedBy: "worker", ProviderTaskID: "provider-task", Error: &JobError{Message: "temporary timeout", Retryable: true}, RequestPayload: json.RawMessage(`{"capability":"image.generate","model":"model-a","prompt":"hello"}`)}
|
||||
|
||||
@@ -111,6 +111,7 @@ type Config struct {
|
||||
type ProviderError struct {
|
||||
Operation string
|
||||
Status int
|
||||
Code string
|
||||
Cause error
|
||||
}
|
||||
|
||||
@@ -215,7 +216,7 @@ func (a *httpAdapter) call(ctx context.Context, method, path string, body []byte
|
||||
ErrorClass: "service",
|
||||
ElapsedMS: elapsedMilliseconds(startedAt),
|
||||
})
|
||||
return Result{}, &ProviderError{Operation: a.name + " " + operation, Status: resp.StatusCode}
|
||||
return Result{}, &ProviderError{Operation: a.name + " " + operation, Status: resp.StatusCode, Code: code}
|
||||
}
|
||||
if !json.Valid(raw) {
|
||||
logHTTPProviderFailure(httpProviderDiagnostic{
|
||||
|
||||
@@ -169,7 +169,7 @@ func (v *Volcengine) call(ctx context.Context, action, version string, payload a
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
diagnostic.ErrorClass = "service"
|
||||
logVolcengineFailure(diagnostic)
|
||||
return Result{}, &ProviderError{Operation: "volcengine request", Status: resp.StatusCode}
|
||||
return Result{}, &ProviderError{Operation: "volcengine request", Status: resp.StatusCode, Code: diagnostic.Code}
|
||||
}
|
||||
if !validJSON {
|
||||
diagnostic.ErrorClass = "invalid_json"
|
||||
@@ -179,7 +179,7 @@ func (v *Volcengine) call(ctx context.Context, action, version string, payload a
|
||||
if response.code != nil && !volcengineRequestSucceeded(response.code) {
|
||||
diagnostic.ErrorClass = "service"
|
||||
logVolcengineFailure(diagnostic)
|
||||
return Result{}, &ProviderError{Operation: "volcengine request"}
|
||||
return Result{}, &ProviderError{Operation: "volcengine request", Code: diagnostic.Code}
|
||||
}
|
||||
d := object(first(response.business["data"], response.business["Data"]))
|
||||
out := []string{}
|
||||
|
||||
Reference in New Issue
Block a user