Add streaming support and refactor Chat UI
- Add run_stream method to AgentCore for streaming output - Add base_url parameter to LLM clients for OpenRouter support - Add xbot module for new agent implementation - Refactor Chat.vue into composable + components (ChatHeader, ChatMessage, ChatInput, ChatSidebar, ChatAgentSelector) - Add ChatStream handler for SSE streaming in Go server - Add UseXBot field to chat request Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"x-agents/server/internal/repository"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AgentChatRequest Python Agent 对话请求
|
||||
@@ -23,6 +25,7 @@ type AgentChatRequest struct {
|
||||
ModelProvider string `json:"model_provider,omitempty"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
BaseURL string `json:"base_url,omitempty"`
|
||||
UseXBot bool `json:"use_xbot"`
|
||||
}
|
||||
|
||||
// AgentChatResponse Python Agent 对话响应
|
||||
@@ -186,3 +189,93 @@ func (s *AgentService) TeamChat(req TeamChatRequest) (*TeamChatResponse, error)
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ChatStream 流式对话
|
||||
func (s *AgentService) ChatStream(c interface{}, agentID int, message, sessionID, modelID string, userID int) error {
|
||||
// 获取 gin.Context
|
||||
ginCtx, ok := c.(*gin.Context)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid context type")
|
||||
}
|
||||
|
||||
// 初始化请求体
|
||||
reqBody := map[string]interface{}{
|
||||
"agent_id": agentID,
|
||||
"message": message,
|
||||
"user_id": userID,
|
||||
"session_id": sessionID,
|
||||
"use_xbot": false,
|
||||
}
|
||||
|
||||
// 如果传入了 model_id,查询模型配置获取 api_key 和 base_url
|
||||
if modelID != "" && s.modelRepo != nil {
|
||||
model, err := s.modelRepo.FindByID(modelID)
|
||||
if err != nil {
|
||||
log.Printf("[ChatStream] Model not found: %s, error: %v", modelID, err)
|
||||
} else if model != nil {
|
||||
log.Printf("[ChatStream] Using model: provider=%s, model=%s, base_url=%s", model.Provider, model.Model, model.BaseURL)
|
||||
// 将模型配置添加到请求体
|
||||
reqBody["model_provider"] = model.Provider
|
||||
reqBody["model_name"] = model.Model
|
||||
reqBody["api_key"] = model.APIKey
|
||||
reqBody["base_url"] = model.BaseURL
|
||||
}
|
||||
} else {
|
||||
log.Printf("[ChatStream] modelID is empty or modelRepo is nil: modelID=%s, modelRepo=%v", modelID, s.modelRepo != nil)
|
||||
}
|
||||
|
||||
streamURL := fmt.Sprintf("%s/agent/chat/stream", s.pythonURL)
|
||||
|
||||
jsonData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal request: %w", err)
|
||||
}
|
||||
|
||||
// 创建 HTTP 请求,设置不缓冲
|
||||
httpReq, err := http.NewRequest("POST", streamURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("Accept", "text/event-stream")
|
||||
httpReq.Header.Set("Cache-Control", "no-cache")
|
||||
|
||||
// 创建不缓冲的 HTTP 客户端
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to call python agent: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 设置 SSE 响应头
|
||||
ginCtx.Header("Content-Type", "text/event-stream")
|
||||
ginCtx.Header("Cache-Control", "no-cache")
|
||||
ginCtx.Header("Connection", "keep-alive")
|
||||
ginCtx.Header("X-Accel-Buffering", "no")
|
||||
|
||||
// 分块读取并转发,使用小 buffer 减少延迟
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := resp.Body.Read(buf)
|
||||
if n > 0 {
|
||||
_, writeErr := ginCtx.Writer.Write(buf[:n])
|
||||
if writeErr != nil {
|
||||
break
|
||||
}
|
||||
// 强制刷新到客户端
|
||||
if flusher, ok := ginCtx.Writer.(interface{ Flush() }); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user