package chat import ( "context" _ "embed" "log/slog" "sync" "trankilou.fr/lassistanoque/backend/internal/domain" "trankilou.fr/lassistanoque/backend/internal/service/tool" ) //go:embed system_prompt.md var systemSystemPrompt string type ChatSession struct { userID string teamID string chatID string agent *domain.Agent provider *domain.Provider modelID string messages []*domain.Message contextMaxTokens int maxResponseTokens int llmEngine domain.LLMEngine toolService *tool.Service 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 s.toolService.GetAllToolImpl() { 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: systemSystemPrompt + "\n\n## Customization\n\n" + s.agent.SystemPrompt, } continue_loop := true // LOOP on TOOLS for continue_loop { // compaction du contexte : il doit rester assez de place pour la réponse if s.shouldCompact(systemPrompt.Content) { s.compactContext(ctx) } 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 { toolImpl := s.toolService.GetToolImpl(tc.Function.Name) slog.Info("Execute tool", "name", tc.Function.Name, "impl", toolImpl) toolRecord, err := s.toolService.GetToolByName(s.userID, s.teamID, toolImpl.Name()) if err != nil { slog.Info("Execute tool", "error", err) toolResponse := &domain.Message{ ChatID: s.chatID, TeamID: s.teamID, ToolCallID: tc.ID, Role: string(domain.RoleTool), Content: "ERROR: " + err.Error(), } s.repoChat.CreateChatMessage(s.userID, toolResponse) } output, err := toolImpl.Execute(ctx, []byte(tc.Function.Arguments), toolRecord.Configuration) 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 }