mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-06 11:28:38 +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:
@@ -1094,19 +1094,14 @@ func (h *AgentHandler) AgentChatCompletions(c *gin.Context) {
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||
c.Writer.Header().Set("Connection", "keep-alive")
|
||||
// SSE wire format is the unified python envelope used by both
|
||||
// /api/v1/agents/chat/completions and /api/v1/agentbots/<id>/completions.
|
||||
// One frame per canvas event, all routed through
|
||||
// service.WriteChatbotRunEvent so the two paths share one writer
|
||||
// and one shape — see internal/service/bot_completion.go for the
|
||||
// frame definition. The same unified envelope is used by the
|
||||
// /api/v1/agents/{canvas_id}/run and /api/v1/agentbots/<id>/completions
|
||||
// endpoints, all going through service.WriteChatbotRunEvent. The
|
||||
// channel close is signalled by `data: [DONE]\n\n`. We do NOT emit
|
||||
// an SSE `event:` line — the front-end's `use-send-message.ts`
|
||||
// SSE wire format is the flat Python agent-canvas envelope:
|
||||
// {event,message_id,task_id,session_id,created_at,data}. One
|
||||
// frame is emitted per canvas event through service.WriteChatbotRunEvent.
|
||||
// The channel close is signalled by `data: [DONE]\n\n`. We do NOT
|
||||
// emit an SSE `event:` line — the front-end's use-send-message.ts
|
||||
// parser feeds each `data:` line directly into JSON.parse and
|
||||
// breaks on the `e` of `event:` (browser console: "SyntaxError:
|
||||
// Unexpected token 'e', \"event: mes\"…").
|
||||
// expects the event type in the JSON object's top-level `event`
|
||||
// field.
|
||||
emitted := false
|
||||
for ev := range events {
|
||||
emitted = true
|
||||
|
||||
@@ -730,10 +730,9 @@ func (s *stubChatRunner) RunAgent(_ context.Context, _, _, _, _ string, _ any) (
|
||||
// TestAgentChatCompletions_StreamSetsContentType covers the SSE
|
||||
// path: the handler streams canvas.RunEvent frames as
|
||||
// `data: {...}\n\n` with a trailing `data: [DONE]\n\n` terminator.
|
||||
// The frame shape is the unified python envelope
|
||||
// {code:0, message:"", data:{answer, reference, audio_binary, id,
|
||||
// session_id}} — the same shape /api/v1/agentbots/<id>/completions
|
||||
// emits. See service.WriteChatbotRunEvent and WriteChatbotFrame.
|
||||
// The frame shape is the Python agent-canvas envelope
|
||||
// {event,message_id,task_id,session_id,data:{content}}. See
|
||||
// service.WriteChatbotRunEvent.
|
||||
//
|
||||
// The stubChatRunner emits one `message` frame and one `done` frame
|
||||
// so the test verifies the body contains both the framed event and
|
||||
@@ -749,7 +748,7 @@ func TestAgentChatCompletions_StreamSetsContentType(t *testing.T) {
|
||||
c.Set("user_id", "u1")
|
||||
|
||||
runner := &stubChatRunner{events: []canvas.RunEvent{
|
||||
{Type: "message", Data: `{"answer":"hi back","reference":[]}`},
|
||||
{Type: "message", MessageID: "msg-1", TaskID: "task-1", SessionID: "sess-1", Data: `{"content":"hi back","reference":[]}`},
|
||||
{Type: "done", Data: ""},
|
||||
}}
|
||||
h := &AgentHandler{chatRunner: runner}
|
||||
@@ -759,12 +758,12 @@ func TestAgentChatCompletions_StreamSetsContentType(t *testing.T) {
|
||||
t.Errorf("Content-Type = %q, want text/event-stream", got)
|
||||
}
|
||||
body := w.Body.String()
|
||||
// Body must contain the unified python envelope (`code/data.answer`)
|
||||
// and the [DONE] terminator. The iframe SDK JSON.parse()s `answer`
|
||||
// to extract the inner fields, so the embedded JSON is double-encoded
|
||||
// (escaped quotes inside the outer `"answer"` string).
|
||||
if !strings.Contains(body, "\"code\":0") || !strings.Contains(body, `"answer":"{\"answer\":\"hi back\",\"reference\":[]}"`) {
|
||||
t.Errorf("body should contain unified python envelope with answer, got %q", body)
|
||||
if !strings.Contains(body, `"event":"message"`) ||
|
||||
!strings.Contains(body, `"message_id":"msg-1"`) ||
|
||||
!strings.Contains(body, `"task_id":"task-1"`) ||
|
||||
!strings.Contains(body, `"session_id":"sess-1"`) ||
|
||||
!strings.Contains(body, `"content":"hi back"`) {
|
||||
t.Errorf("body should contain flat agent event with content, got %q", body)
|
||||
}
|
||||
if !strings.HasSuffix(body, "data: [DONE]\n\n") {
|
||||
t.Errorf("body should end with [DONE] terminator, got %q", body)
|
||||
@@ -775,8 +774,7 @@ func TestAgentChatCompletions_StreamSetsContentType(t *testing.T) {
|
||||
// scenario the user actually hit: `openai-compatible: false` with no
|
||||
// `stream` field on the body. The handler must still invoke the
|
||||
// canvas runner and stream the result as SSE — the SSE envelope is
|
||||
// the unified python shape shared with
|
||||
// /api/v1/agentbots/<id>/completions regardless of the stream flag.
|
||||
// the flat Python agent-canvas shape regardless of the stream flag.
|
||||
func TestAgentChatCompletions_DefaultBranchStreamsSSE(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
w := httptest.NewRecorder()
|
||||
@@ -788,7 +786,7 @@ func TestAgentChatCompletions_DefaultBranchStreamsSSE(t *testing.T) {
|
||||
c.Set("user_id", "u1")
|
||||
|
||||
runner := &stubChatRunner{events: []canvas.RunEvent{
|
||||
{Type: "message", Data: `{"answer":"hello back","reference":[]}`},
|
||||
{Type: "message", MessageID: "msg-2", TaskID: "task-2", SessionID: "sess-2", Data: `{"content":"hello back","reference":[]}`},
|
||||
{Type: "done", Data: ""},
|
||||
}}
|
||||
h := &AgentHandler{chatRunner: runner}
|
||||
@@ -798,8 +796,10 @@ func TestAgentChatCompletions_DefaultBranchStreamsSSE(t *testing.T) {
|
||||
t.Errorf("Content-Type = %q, want text/event-stream (default branch must stream)", got)
|
||||
}
|
||||
body := w.Body.String()
|
||||
if !strings.Contains(body, "\"code\":0") || !strings.Contains(body, `"answer":"{\"answer\":\"hello back\",\"reference\":[]}"`) {
|
||||
t.Errorf("body should contain unified python envelope with answer, got %q", body)
|
||||
if !strings.Contains(body, `"event":"message"`) ||
|
||||
!strings.Contains(body, `"message_id":"msg-2"`) ||
|
||||
!strings.Contains(body, `"content":"hello back"`) {
|
||||
t.Errorf("body should contain flat agent event with content, got %q", body)
|
||||
}
|
||||
if !strings.HasSuffix(body, "data: [DONE]\n\n") {
|
||||
t.Errorf("body should end with [DONE] terminator, got %q", body)
|
||||
|
||||
Reference in New Issue
Block a user