diff options
| author | Grail Finder <wohilas@gmail.com> | 2026-03-15 08:05:12 +0300 |
|---|---|---|
| committer | Grail Finder <wohilas@gmail.com> | 2026-03-15 08:05:12 +0300 |
| commit | 1396b3eb05c32d868a3e07b8f60813f28d2042f8 (patch) | |
| tree | 85dede977e3a9adcaecefcf19397a9b416dfbc4c /bot.go | |
| parent | 619b19cb46061c89aa4a837ed1e6bfea76644bd8 (diff) | |
Refactor: moving tool related code into tools package
Diffstat (limited to 'bot.go')
| -rw-r--r-- | bot.go | 102 |
1 files changed, 70 insertions, 32 deletions
@@ -11,6 +11,7 @@ import ( "gf-lt/models" "gf-lt/rag" "gf-lt/storage" + "gf-lt/tools" "html" "io" "log/slog" @@ -27,26 +28,38 @@ import ( ) var ( - httpClient = &http.Client{} - cfg *config.Config - logger *slog.Logger - logLevel = new(slog.LevelVar) - ctx, cancel = context.WithCancel(context.Background()) - activeChatName string - chatRoundChan = make(chan *models.ChatRoundReq, 1) - chunkChan = make(chan string, 10) - openAIToolChan = make(chan string, 10) - streamDone = make(chan bool, 1) - chatBody *models.ChatBody - store storage.FullRepo - defaultFirstMsg = "Hello! What can I do for you?" - defaultStarter = []models.RoleMsg{} - interruptResp atomic.Bool - ragger *rag.RAG - chunkParser ChunkParser - lastToolCall *models.FuncCall - lastRespStats *models.ResponseStats + httpClient = &http.Client{} + cfg *config.Config + logger *slog.Logger + logLevel = new(slog.LevelVar) + ctx, cancel = context.WithCancel(context.Background()) + activeChatName string + chatRoundChan = make(chan *models.ChatRoundReq, 1) + chunkChan = make(chan string, 10) + openAIToolChan = make(chan string, 10) + streamDone = make(chan bool, 1) + chatBody *models.ChatBody + store storage.FullRepo + defaultStarter = []models.RoleMsg{} + interruptResp atomic.Bool + ragger *rag.RAG + chunkParser ChunkParser + lastToolCall *models.FuncCall + lastRespStats *models.ResponseStats //nolint:unused // TTS_ENABLED conditionally uses this + basicCard = &models.CharCard{ + ID: models.ComputeCardID("assistant", "basic_sys"), + SysPrompt: models.BasicSysMsg, + FirstMsg: models.DefaultFirstMsg, + Role: "assistant", + FilePath: "basic_sys", + } + sysMap = map[string]*models.CharCard{} + roleToID = map[string]string{} + modelHasVision bool + windowToolsAvailable bool + tooler *tools.Tools + // orator Orator asr STT localModelsMu sync.RWMutex @@ -458,6 +471,29 @@ func ModelHasVision(api, modelID string) bool { } } +func UpdateToolCapabilities() { + if !cfg.ToolUse { + return + } + modelHasVision = false + if cfg == nil || cfg.CurrentAPI == "" { + logger.Warn("cannot determine model capabilities: cfg or CurrentAPI is nil") + tooler.RegisterWindowTools(modelHasVision) + return + } + prevHasVision := modelHasVision + modelHasVision = ModelHasVision(cfg.CurrentAPI, cfg.CurrentModel) + if modelHasVision { + logger.Info("model has vision support", "model", cfg.CurrentModel, "api", cfg.CurrentAPI) + } else { + logger.Info("model does not have vision support", "model", cfg.CurrentModel, "api", cfg.CurrentAPI) + if windowToolsAvailable && !prevHasVision && !modelHasVision { + showToast("window tools", "Window capture-and-view unavailable: model lacks vision support") + } + } + tooler.RegisterWindowTools(modelHasVision) +} + // monitorModelLoad starts a goroutine that periodically checks if the specified model is loaded. func monitorModelLoad(modelID string) { go func() { @@ -1102,7 +1138,7 @@ func findCall(msg, toolCall string) bool { // The ID should come from the streaming response (chunk.ToolID) set earlier. // Some tools like todo_create have "id" in their arguments which is NOT the tool call ID. } else { - jsStr := toolCallRE.FindString(msg) + jsStr := tools.ToolCallRE.FindString(msg) if jsStr == "" { // no tool call case return false } @@ -1170,7 +1206,7 @@ func findCall(msg, toolCall string) bool { Args: mapToString(lastToolCall.Args), } // call a func - _, ok := fnMap[fc.Name] + _, ok := tools.FnMap[fc.Name] if !ok { m := fc.Name + " is not implemented" // Create tool response message with the proper tool_call_id @@ -1195,7 +1231,7 @@ func findCall(msg, toolCall string) bool { // Show tool call progress indicator before execution fmt.Fprintf(textView, "\n[yellow::i][tool: %s...][-:-:-]", fc.Name) toolRunningMode.Store(true) - resp := callToolWithAgent(fc.Name, fc.Args) + resp := tools.CallToolWithAgent(fc.Name, fc.Args) toolRunningMode.Store(false) toolMsg := string(resp) logger.Info("llm used a tool call", "tool_name", fc.Name, "too_args", fc.Args, "id", fc.ID, "tool_resp", toolMsg) @@ -1312,7 +1348,7 @@ func chatToText(messages []models.RoleMsg, showSys bool) string { text := strings.Join(s, "\n") // Collapse thinking blocks if enabled if thinkingCollapsed { - text = thinkRE.ReplaceAllStringFunc(text, func(match string) string { + text = tools.ThinkRE.ReplaceAllStringFunc(text, func(match string) string { // Extract content between <think> and </think> start := len("<think>") end := len(match) - len("</think>") @@ -1409,7 +1445,7 @@ func updateModelLists() { chatBody.Model = m cachedModelColor.Store("green") updateStatusLine() - updateToolCapabilities() + UpdateToolCapabilities() app.Draw() return } @@ -1441,7 +1477,7 @@ func summarizeAndStartNewChat() { } showToast("info", "Summarizing chat history...") // Call the summarize_chat tool via agent - summaryBytes := callToolWithAgent("summarize_chat", map[string]string{}) + summaryBytes := tools.CallToolWithAgent("summarize_chat", map[string]string{}) summary := string(summaryBytes) if summary == "" { showToast("error", "Failed to generate summary") @@ -1477,8 +1513,8 @@ func init() { return } defaultStarter = []models.RoleMsg{ - {Role: "system", Content: basicSysMsg}, - {Role: cfg.AssistantRole, Content: defaultFirstMsg}, + {Role: "system", Content: models.BasicSysMsg}, + {Role: cfg.AssistantRole, Content: models.DefaultFirstMsg}, } logfile, err := os.OpenFile(cfg.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) @@ -1489,6 +1525,8 @@ func init() { return } // load cards + sysMap[basicCard.ID] = basicCard + roleToID["assistant"] = basicCard.ID basicCard.Role = cfg.AssistantRole logLevel.Set(slog.LevelInfo) logger = slog.New(slog.NewTextHandler(logfile, &slog.HandlerOptions{Level: logLevel})) @@ -1530,15 +1568,14 @@ func init() { } if cfg.PlaywrightEnabled { go func() { - if err := checkPlaywright(); err != nil { - // slow, need a faster check if playwright install - if err := installPW(); err != nil { + if err := tools.CheckPlaywright(); err != nil { + if err := tools.InstallPW(); err != nil { logger.Error("failed to install playwright", "error", err) cancel() os.Exit(1) return } - if err := checkPlaywright(); err != nil { + if err := tools.CheckPlaywright(); err != nil { logger.Error("failed to run playwright", "error", err) cancel() os.Exit(1) @@ -1551,5 +1588,6 @@ func init() { cachedModelColor.Store("orange") go chatWatcher(ctx) initTUI() - initTools() + tooler = tools.InitTools(cfg, logger, store) + tooler.RegisterWindowTools(modelHasVision) } |
