fix: propagate LLM API errors instead of showing misleading "Looks good to me"

When all subtask LLM calls fail (e.g. invalid API key), dispatchSubtasks
now returns an error instead of empty comments with nil error. Partial
failures are recorded as subtask_error warnings so output functions can
distinguish "no issues found" from "review failed".
This commit is contained in:
kite 2026-05-22 15:28:26 +08:00
parent 6c213909d4
commit f054bb799e
2 changed files with 40 additions and 3 deletions

View file

@ -21,8 +21,27 @@ func outputText(comments []model.LlmComment) {
}
}
func hasSubtaskErrors(warnings []agent.AgentWarning) bool {
for _, w := range warnings {
if w.Type == "subtask_error" {
return true
}
}
return false
}
func outputTextWithWarnings(comments []model.LlmComment, warnings []agent.AgentWarning) {
outputText(comments)
if len(comments) == 0 {
if hasSubtaskErrors(warnings) {
fmt.Println("Some files could not be reviewed due to errors (see warnings below).")
} else {
fmt.Println("No comments generated. Looks good to me.")
}
} else {
for _, c := range comments {
renderComment(c)
}
}
if len(warnings) > 0 {
for _, w := range warnings {
fmt.Fprintf(os.Stderr, "[ocr] WARNING [%s] %s: %s\n", w.Type, w.File, w.Message)
@ -168,11 +187,19 @@ func outputJSONWithWarnings(comments []model.LlmComment, warnings []agent.AgentW
Comments: comments,
}
if len(comments) == 0 {
out.Message = "No comments generated. Looks good to me."
if hasSubtaskErrors(warnings) {
out.Message = "Some files could not be reviewed due to errors."
} else {
out.Message = "No comments generated. Looks good to me."
}
}
if len(warnings) > 0 {
out.Warnings = warnings
out.Status = "completed_with_warnings"
if hasSubtaskErrors(warnings) {
out.Status = "completed_with_errors"
} else {
out.Status = "completed_with_warnings"
}
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")

View file

@ -137,6 +137,7 @@ type Agent struct {
totalTokensUsed int64 // accumulated total tokens from all LLM calls, accessed atomically
totalInputTokens int64 // accumulated input/prompt tokens, accessed atomically
totalOutputTokens int64 // accumulated completion tokens, accessed atomically
subtaskFailed int64 // count of failed subtasks, accessed atomically
warningsMu sync.Mutex
warnings []AgentWarning
compressionMu sync.Mutex
@ -386,9 +387,11 @@ func (a *Agent) dispatchSubtasks(ctx context.Context) ([]model.LlmComment, error
}
if err := a.executeSubtask(fileCtx, d); err != nil {
atomic.AddInt64(&a.subtaskFailed, 1)
fmt.Fprintf(stdout.Writer(), "[ocr] Subtask error for %s: %v\n", d.NewPath, err)
telemetry.ErrorEvent(fileCtx, "subtask.error", err,
telemetry.AnyToAttr("file.path", d.NewPath))
a.recordWarning("subtask_error", d.NewPath, err.Error())
}
}(a.diffs[i])
}
@ -399,6 +402,13 @@ func (a *Agent) dispatchSubtasks(ctx context.Context) ([]model.LlmComment, error
if a.args.CommentWorkerPool != nil {
a.args.CommentWorkerPool.Await()
}
failed := atomic.LoadInt64(&a.subtaskFailed)
total := int64(len(a.diffs))
if failed > 0 && failed == total {
return nil, fmt.Errorf("all %d file review(s) failed — check your LLM configuration and API key", total)
}
return a.args.CommentCollector.Comments(), nil
}