mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-09-08 10:14:35 +08:00
fix: agent chat completions can not use (#16570)
### Summary As title <img width="2370" height="2039" alt="image" src="https://github.com/user-attachments/assets/4cccf543-3908-49ee-8101-c5068fbf53ec" />
This commit is contained in:
@@ -154,30 +154,22 @@ func WriteDoneFrame(w http.ResponseWriter) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteChatbotRunEvent translates one canvas.RunEvent into the
|
||||
// unified python-shaped chat-completion envelope (same shape as
|
||||
// WriteChatbotFrame). This unifies the SSE format across:
|
||||
// WriteChatbotRunEvent translates one canvas.RunEvent into the flat
|
||||
// Python agent-canvas SSE envelope:
|
||||
//
|
||||
// - /api/v1/agents/chat/completions (was: writeChatCompletionSSE)
|
||||
// - /api/v1/agentbots/<id>/completions (was: WriteChatbotFrame per-event)
|
||||
// data: {"event":"message","message_id":"...","task_id":"...",
|
||||
// "session_id":"...","created_at":123,"data":{"content":"..."}}\n\n
|
||||
//
|
||||
// This is intentionally different from WriteChatbotFrame's legacy
|
||||
// chatbot `{code,data:{answer:"..."}}` shape. The agent React page's
|
||||
// use-send-message.ts parser appends each parsed object directly to
|
||||
// answerList and expects top-level `event` / `message_id`, plus a
|
||||
// typed `data` payload. If RunEvent frames are double-wrapped in
|
||||
// data.answer, the browser receives bytes but cannot render the
|
||||
// assistant message or correlate the current Log panel.
|
||||
//
|
||||
// The "done" event type emits `data: [DONE]\n\n` (no envelope),
|
||||
// matching the OpenAI-style terminator and the existing
|
||||
// AgentbotCompletion wire.
|
||||
//
|
||||
// For non-done events, ev.Data is placed verbatim into the `answer`
|
||||
// field — callers pass canvas-runner output that is itself a JSON
|
||||
// string (e.g. `{"answer":"hi back","reference":[]}`); the iframe
|
||||
// SDK then JSON.parse()s the `answer` string to extract the inner
|
||||
// fields. This matches the existing AgentbotCompletion behaviour.
|
||||
//
|
||||
// The event type is forwarded as the `event` field of the envelope
|
||||
// (PR #14589) so the front-end can distinguish interactive
|
||||
// `user_inputs` / `workflow_finished` events from plain `message`
|
||||
// streams and render the UserFillUp form vs the assistant text.
|
||||
// Without this field the form UI never appears because the
|
||||
// iframe SDK has no way to know the canvas paused for human
|
||||
// input.
|
||||
// matching the Python agent API terminator.
|
||||
//
|
||||
// Returns the write error so callers can short-circuit; both nil
|
||||
// and io.ErrClosedPipe are tolerated because the client may have
|
||||
@@ -193,13 +185,65 @@ func WriteChatbotRunEvent(w http.ResponseWriter, ev canvas.RunEvent) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
f := ChatbotSSEFrame{
|
||||
Event: ev.Type,
|
||||
Data: ev.Data,
|
||||
Reference: map[string]any{},
|
||||
SessionID: ev.SessionID,
|
||||
|
||||
var data any = map[string]any{}
|
||||
if ev.Data != "" {
|
||||
if err := json.Unmarshal([]byte(ev.Data), &data); err != nil {
|
||||
data = ev.Data
|
||||
}
|
||||
}
|
||||
return WriteChatbotFrame(w, f)
|
||||
if ev.Type == "error" {
|
||||
msg := "an internal error occurred"
|
||||
if m, ok := data.(map[string]any); ok {
|
||||
if s, _ := m["message"].(string); s != "" {
|
||||
msg = s
|
||||
}
|
||||
}
|
||||
payload := map[string]any{
|
||||
"code": 500,
|
||||
"message": msg,
|
||||
"data": false,
|
||||
}
|
||||
return writeSSEJSON(w, payload)
|
||||
}
|
||||
|
||||
payload := map[string]any{
|
||||
"data": data,
|
||||
"created_at": ev.CreatedAt,
|
||||
}
|
||||
if ev.Type != "" {
|
||||
payload["event"] = ev.Type
|
||||
}
|
||||
if ev.MessageID != "" {
|
||||
payload["message_id"] = ev.MessageID
|
||||
}
|
||||
if ev.TaskID != "" {
|
||||
payload["task_id"] = ev.TaskID
|
||||
}
|
||||
if ev.SessionID != "" {
|
||||
payload["session_id"] = ev.SessionID
|
||||
}
|
||||
return writeSSEJSON(w, payload)
|
||||
}
|
||||
|
||||
func writeSSEJSON(w http.ResponseWriter, payload map[string]any) error {
|
||||
b, err := runtime.SafeJSONMarshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("data:")); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("\n\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AgentbotSSEFrame mirrors ChatbotSSEFrame for the agentbot
|
||||
|
||||
Reference in New Issue
Block a user