package chat import ( "context" "sync" "trankilou.fr/lassistanoque/backend/internal/domain" ) type ChatSession struct { userID string teamID string chatID string agent *domain.Agent provider *domain.Provider modelID string messages []*domain.Message llmEngine domain.LLMEngine repoChat domain.ChatRepository subscribers []domain.StreamCallback mu sync.Mutex } func (s *ChatSession) run(ctx context.Context) error { params := &domain.LLMParams{} tools := make([]*domain.ToolDefinition, 0) for _, t := range allTools { tools = append(tools, t.Definition(ctx)) } params.Tools = tools s.mu.Lock() if len(s.subscribers) > 0 { params.OnChunk = func(chunk *domain.Chunk) { for _, cb := range s.subscribers { cb(chunk) } } } s.mu.Unlock() systemPrompt := &domain.Message{ Role: string(domain.RoleSystem), Content: s.agent.SystemPrompt, } continue_loop := true // LOOP on TOOLS for continue_loop { msg := s.llmEngine.Stream( ctx, s.provider, s.modelID, params, append([]*domain.Message{systemPrompt}, s.messages...), ) msg.ChatID = s.chatID msg.TeamID = s.teamID msg, err := s.repoChat.CreateChatMessage(s.userID, msg) if err != nil { return err } s.messages = append(s.messages, msg) // TOOL if len(msg.ToolCalls) > 0 { continue_loop = true for _, tc := range msg.ToolCalls { tool := GetTool(tc.Function.Name) output, err := tool.Execute(ctx, []byte(tc.Function.Arguments)) var toolResponse *domain.Message if err != nil { toolResponse = &domain.Message{ ChatID: s.chatID, TeamID: s.teamID, ToolCallID: tc.ID, Role: string(domain.RoleTool), Content: "ERROR: " + err.Error(), } } else { toolResponse = &domain.Message{ ChatID: s.chatID, TeamID: s.teamID, ToolCallID: tc.ID, Role: string(domain.RoleTool), Content: string(output), } } toolResponse, err = s.repoChat.CreateChatMessage(s.userID, toolResponse) if err != nil { return err } s.messages = append(s.messages, toolResponse) } } else { continue_loop = false } // END TOOL } // END LOOP return nil }