Files
GoLoom/internal/service/chat.go

111 lines
3.6 KiB
Go

package service
import (
"fmt"
"sort"
"ai-agent-scaffold-go/internal/model"
"ai-agent-scaffold-go/pkg/types"
)
// ChatService 聊天服务
type ChatService struct {
registry model.AgentRegistry
sessions model.SessionStore
}
// NewChatService 创建聊天服务
func NewChatService(registry model.AgentRegistry, sessions model.SessionStore) *ChatService {
return &ChatService{registry: registry, sessions: sessions}
}
// QueryAgentConfigList 查询已注册的 Agent 列表
func (s *ChatService) QueryAgentConfigList() []model.AgentSummary {
registered := s.registry.List()
sort.Slice(registered, func(i, j int) bool {
return registered[i].AgentID < registered[j].AgentID
})
agents := make([]model.AgentSummary, 0, len(registered))
for _, agent := range registered {
agents = append(agents, model.AgentSummary{
AgentID: agent.AgentID,
AgentName: agent.AgentName,
AgentDesc: agent.AgentDesc,
})
}
return agents
}
// CreateSession 为指定 Agent 和用户创建会话
func (s *ChatService) CreateSession(agentID, userID string) (string, error) {
if sessionID, ok := s.sessions.Get(userID, agentID); ok {
return sessionID, nil
}
registered, ok := s.registry.Get(agentID)
if !ok || registered.Runner == nil {
return "", types.NewAppError(types.CodeAgentNotFound, types.InfoAgentNotFound)
}
sessionID, err := registered.Runner.CreateSession(userID)
if err != nil {
return "", err
}
if err := s.sessions.Set(userID, agentID, sessionID); err != nil {
return "", err
}
return sessionID, nil
}
// HandleMessage 处理同步聊天消息
func (s *ChatService) HandleMessage(agentID, userID, sessionID, message string) ([]string, error) {
content := model.ChatContent{Texts: []model.TextPart{{Message: message}}}
return s.handleCommand(agentID, userID, sessionID, message, content)
}
// HandleMessageStream 处理流式聊天消息
func (s *ChatService) HandleMessageStream(agentID, userID, sessionID, message string) (<-chan string, <-chan error) {
content := model.ChatContent{Texts: []model.TextPart{{Message: message}}}
return s.handleCommandStream(agentID, userID, sessionID, message, content)
}
func (s *ChatService) handleCommand(agentID, userID, sessionID, message string, content model.ChatContent) ([]string, error) {
registered, sessionID, err := s.resolveRunnerSession(agentID, userID, sessionID, message, content)
if err != nil {
return nil, err
}
return registered.Runner.Run(userID, sessionID, content)
}
func (s *ChatService) handleCommandStream(agentID, userID, sessionID, message string, content model.ChatContent) (<-chan string, <-chan error) {
registered, sessionID, err := s.resolveRunnerSession(agentID, userID, sessionID, message, content)
if err != nil {
outputs := make(chan string)
errs := make(chan error, 1)
errs <- err
close(outputs)
close(errs)
return outputs, errs
}
return registered.Runner.Stream(userID, sessionID, content)
}
func (s *ChatService) resolveRunnerSession(agentID, userID, sessionID, message string, content model.ChatContent) (model.RegisteredAgent, string, error) {
registered, ok := s.registry.Get(agentID)
if !ok || registered.Runner == nil {
return model.RegisteredAgent{}, "", types.NewAppError(types.CodeAgentNotFound, types.InfoAgentNotFound)
}
if sessionID == "" {
var err error
sessionID, err = s.CreateSession(agentID, userID)
if err != nil {
return model.RegisteredAgent{}, "", err
}
}
if len(content.Texts) == 0 && message != "" {
content.Texts = []model.TextPart{{Message: message}}
}
if len(content.Texts) == 0 {
return model.RegisteredAgent{}, "", fmt.Errorf("chat content is required")
}
return registered, sessionID, nil
}