优化错误检查

This commit is contained in:
2026-07-06 10:37:46 +08:00
parent e517c56225
commit e05db36f63
3 changed files with 103 additions and 11 deletions
+1 -1
View File
@@ -943,7 +943,7 @@ func (c *Client) pollForImage(ctx context.Context, session tlsclient.HttpClient,
// Fail fast on a content-audit refusal: the assistant turn carries the
// rejection text and no image will ever land, so polling to timeout only
// wastes the whole budget. Only bail while we have no asset yet.
if len(fileIDs) == 0 && len(sedimentIDs) == 0 && conversationRejected(conv) {
if len(fileIDs) == 0 && len(sedimentIDs) == 0 && (conversationRejected(conv) || conversationEndedWithoutImage(conv)) {
return nil, nil, ErrContentPolicy
}
if len(fileIDs) > 0 || len(sedimentIDs) > 0 {
+56
View File
@@ -129,6 +129,62 @@ func conversationRejected(conversation map[string]any) bool {
return false
}
// conversationEndedWithoutImage reports whether the model finished its turn
// with a plain-text answer and never engaged the async image pipeline (no tool
// turn, no image_gen task). ChatGPT localizes audit refusals, so this catches
// rejections in ANY language structurally: a closed text-only turn can never
// produce an image, and polling further only burns the budget.
func conversationEndedWithoutImage(conversation map[string]any) bool {
mapping, _ := conversation["mapping"].(map[string]any)
if len(mapping) == 0 {
return false
}
for _, rawNode := range mapping {
node, _ := rawNode.(map[string]any)
message, _ := node["message"].(map[string]any)
if message == nil {
continue
}
if metadata, _ := message["metadata"].(map[string]any); metadata != nil {
if stringValue(metadata["image_gen_task_id"]) != "" || metadata["image_gen_async"] == true {
return false
}
}
author, _ := message["author"].(map[string]any)
role := strings.ToLower(strings.TrimSpace(stringValue(author["role"])))
if role == "tool" {
return false
}
if role == "assistant" {
if content, _ := message["content"].(map[string]any); content != nil {
if ct := stringValue(content["content_type"]); ct != "" && ct != "text" {
return false // code/multimodal turn — generation underway
}
}
}
}
for _, rawNode := range mapping {
node, _ := rawNode.(map[string]any)
message, _ := node["message"].(map[string]any)
if message == nil {
continue
}
author, _ := message["author"].(map[string]any)
if strings.ToLower(strings.TrimSpace(stringValue(author["role"]))) != "assistant" {
continue
}
if message["end_turn"] != true || stringValue(message["status"]) != "finished_successfully" {
continue
}
var sb strings.Builder
collectText(message["content"], &sb)
if strings.TrimSpace(sb.String()) != "" {
return true
}
}
return false
}
func stringValue(v any) string {
switch x := v.(type) {
case string: