mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 06:17:41 +00:00
fix(agent): prevent trailing assistant messages for proxy provider compat
Reorder team task reminders to inject before the user message instead of after, ensuring the user's message is always the last in the messages array. Proxy providers (LiteLLM, OpenRouter) reject requests ending with an assistant-role message (unsupported prefill). Also add a safety net in the OpenAI provider to strip any trailing assistant message that might slip through in the future.
This commit is contained in:
1 parent
0415052872
commit
11673dabf1
2 files changed
+22
-1
No files matched your search
+11
-1
@@ -396,6 +396,8 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
|
||||
// 2g. Cross-session task reminder: notify team leads about pending and in-progress tasks.
|
||||
// Stale recovery (expired lock → pending) is handled by the background TaskTicker.
|
||||
// Reminders are injected BEFORE the user message so the user's actual message is always
|
||||
// the last message — prevents trailing assistant messages that proxy providers reject.
|
||||
if l.teamStore != nil && l.agentUUID != uuid.Nil {
|
||||
if team, _ := l.teamStore.GetTeamForAgent(ctx, l.agentUUID); team != nil && team.LeadAgentID == l.agentUUID {
|
||||
if tasks, err := l.teamStore.ListTasks(ctx, team.ID, "newest", "active", req.UserID, "", "", 0, 0); err == nil {
|
||||
@@ -434,16 +436,20 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
}
|
||||
if len(parts) > 0 {
|
||||
reminder := "[System] " + strings.Join(parts, "\n\n")
|
||||
// Pop user message, inject reminder, push user message back
|
||||
userMsg := messages[len(messages)-1]
|
||||
messages = messages[:len(messages)-1]
|
||||
messages = append(messages,
|
||||
providers.Message{Role: "user", Content: reminder},
|
||||
providers.Message{Role: "assistant", Content: "I see the task status. Let me handle accordingly."},
|
||||
userMsg,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2g. Member task reminder: inject task context for members working on dispatched tasks.
|
||||
// 2h. Member task reminder: inject task context for members working on dispatched tasks.
|
||||
// Caches task subject/number for mid-loop progress nudge (avoids extra DB query).
|
||||
var memberTaskSubject string
|
||||
var memberTaskNumber int
|
||||
@@ -457,9 +463,13 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
"Stay focused on this task. Your final response becomes the task result — make it clear and complete. "+
|
||||
"For long tasks, report progress: team_tasks(action=\"progress\", percent=50, text=\"status\").",
|
||||
task.TaskNumber, task.Subject)
|
||||
// Pop user message, inject reminder, push user message back
|
||||
userMsg := messages[len(messages)-1]
|
||||
messages = messages[:len(messages)-1]
|
||||
messages = append(messages,
|
||||
providers.Message{Role: "user", Content: reminder},
|
||||
providers.Message{Role: "assistant", Content: "Understood. I'll focus on this task and report progress."},
|
||||
userMsg,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +340,17 @@ func (p *OpenAIProvider) buildRequestBody(model string, req ChatRequest, stream
|
||||
msgs = append(msgs, msg)
|
||||
}
|
||||
|
||||
// Safety net: strip trailing assistant message to prevent HTTP 400 from
|
||||
// proxy providers (LiteLLM, OpenRouter) that don't support assistant prefill.
|
||||
// This should rarely trigger — the agent loop ensures user message is last.
|
||||
if len(msgs) > 0 {
|
||||
if role, _ := msgs[len(msgs)-1]["role"].(string); role == "assistant" {
|
||||
slog.Warn("openai: stripped trailing assistant message (unsupported prefill)",
|
||||
"provider", p.name, "model", model)
|
||||
msgs = msgs[:len(msgs)-1]
|
||||
}
|
||||
}
|
||||
|
||||
body := map[string]any{
|
||||
"model": model,
|
||||
"messages": msgs,
|
||||
|
||||
Reference in new issue
Block a user