on main help sign in
perf: init tools in background
380d524Carlos Alexandro Becker
internal/app/app.go+1−1
@@ -80,7 +80,7 @@ func New(ctx context.Context, conn *sql.DB, cfg *config.Config) (*App, error) {
80 app.setupEvents()
80 app.setupEvents()
81
81
82 // Initialize LSP clients in the background.
82 // Initialize LSP clients in the background.
83− go app.initLSPClients(ctx)
83+ app.initLSPClients(ctx)
84
84
85 // TODO: remove the concept of agent config, most likely.
85 // TODO: remove the concept of agent config, most likely.
86 if cfg.IsConfigured() {
86 if cfg.IsConfigured() {
internal/cmd/root.go+4
@@ -6,6 +6,7 @@ import (
6 "io"
6 "io"
7 "log/slog"
7 "log/slog"
8 "os"
8 "os"
9+ "time"
9
10
10 tea "github.com/charmbracelet/bubbletea/v2"
11 tea "github.com/charmbracelet/bubbletea/v2"
11 "github.com/charmbracelet/crush/internal/app"
12 "github.com/charmbracelet/crush/internal/app"
@@ -83,12 +84,15 @@ to assist developers in writing, debugging, and understanding code directly from
83 return err
84 return err
84 }
85 }
85
86
87+ slog.Info("Initing...")
88+ now := time.Now()
86 app, err := app.New(ctx, conn, cfg)
89 app, err := app.New(ctx, conn, cfg)
87 if err != nil {
90 if err != nil {
88 slog.Error(fmt.Sprintf("Failed to create app instance: %v", err))
91 slog.Error(fmt.Sprintf("Failed to create app instance: %v", err))
89 return err
92 return err
90 }
93 }
91 defer app.Shutdown()
94 defer app.Shutdown()
95+ slog.Info("Init done", "took", time.Since(now).String())
92
96
93 prompt, err = maybePrependStdin(prompt)
97 prompt, err = maybePrependStdin(prompt)
94 if err != nil {
98 if err != nil {
internal/llm/agent/agent.go+65−49
@@ -8,6 +8,7 @@ import (
8 "slices"
8 "slices"
9 "strings"
9 "strings"
10 "sync"
10 "sync"
11+ "sync/atomic"
11 "time"
12 "time"
12
13
13 "github.com/charmbracelet/crush/internal/config"
14 "github.com/charmbracelet/crush/internal/config"
@@ -67,7 +68,9 @@ type agent struct {
67 sessions session.Service
68 sessions session.Service
68 messages message.Service
69 messages message.Service
69
70
71+ toolsDone atomic.Bool
70− tools []tools.BaseTool
72+ tools []tools.BaseTool
73+
71 provider provider.Provider
74 provider provider.Provider
72 providerID string
75 providerID string
73
76
@@ -94,46 +97,7 @@ func NewAgent(
94) (Service, error) {
97) (Service, error) {
95 ctx := context.Background()
98 ctx := context.Background()
96 cfg := config.Get()
99 cfg := config.Get()
97− otherTools := GetMCPTools(ctx, permissions, cfg)
98− if len(lspClients) > 0 {
99− otherTools = append(otherTools, tools.NewDiagnosticsTool(lspClients))
100− }
101
100
102− cwd := cfg.WorkingDir()
103− allTools := []tools.BaseTool{
104− tools.NewBashTool(permissions, cwd),
105− tools.NewDownloadTool(permissions, cwd),
106− tools.NewEditTool(lspClients, permissions, history, cwd),
107− tools.NewFetchTool(permissions, cwd),
108− tools.NewGlobTool(cwd),
109− tools.NewGrepTool(cwd),
110− tools.NewLsTool(cwd),
111− tools.NewSourcegraphTool(),
112− tools.NewViewTool(lspClients, cwd),
113− tools.NewWriteTool(lspClients, permissions, history, cwd),
114− }
115−
116− if agentCfg.ID == "coder" {
117− taskAgentCfg := config.Get().Agents["task"]
118− if taskAgentCfg.ID == "" {
119− return nil, fmt.Errorf("task agent not found in config")
120− }
121− taskAgent, err := NewAgent(taskAgentCfg, permissions, sessions, messages, history, lspClients)
122− if err != nil {
123− return nil, fmt.Errorf("failed to create task agent: %w", err)
124− }
125−
126− allTools = append(
127− allTools,
128− NewAgentTool(
129− taskAgent,
130− sessions,
131− messages,
132− ),
133− )
134− }
135−
136− allTools = append(allTools, otherTools...)
137 providerCfg := config.Get().GetProviderForModel(agentCfg.Model)
101 providerCfg := config.Get().GetProviderForModel(agentCfg.Model)
138 if providerCfg == nil {
102 if providerCfg == nil {
139 return nil, fmt.Errorf("provider for agent %s not found in config", agentCfg.Name)
103 return nil, fmt.Errorf("provider for agent %s not found in config", agentCfg.Name)
@@ -190,15 +154,22 @@ func NewAgent(
190 return nil, err
154 return nil, err
191 }
155 }
192
156
193− agentTools := []tools.BaseTool{}
157+ var agentTool tools.BaseTool
194− if agentCfg.AllowedTools == nil {
158+ if agentCfg.ID == "coder" {
195− agentTools = allTools
159+ taskAgentCfg := config.Get().Agents["task"]
196− } else {
160+ if taskAgentCfg.ID == "" {
197− for _, tool := range allTools {
161+ return nil, fmt.Errorf("task agent not found in config")
198− if slices.Contains(agentCfg.AllowedTools, tool.Name()) {
199− agentTools = append(agentTools, tool)
200− }
201 }
162 }
163+ taskAgent, err := NewAgent(taskAgentCfg, permissions, sessions, messages, history, lspClients)
164+ if err != nil {
165+ return nil, fmt.Errorf("failed to create task agent: %w", err)
166+ }
167+
168+ agentTool = NewAgentTool(
169+ taskAgent,
170+ sessions,
171+ messages,
172+ )
202 }
173 }
203
174
204 agent := &agent{
175 agent := &agent{
@@ -208,13 +179,55 @@ func NewAgent(
208 providerID: string(providerCfg.ID),
179 providerID: string(providerCfg.ID),
209 messages: messages,
180 messages: messages,
210 sessions: sessions,
181 sessions: sessions,
211− tools: agentTools,
212 titleProvider: titleProvider,
182 titleProvider: titleProvider,
213 summarizeProvider: summarizeProvider,
183 summarizeProvider: summarizeProvider,
214 summarizeProviderID: string(smallModelProviderCfg.ID),
184 summarizeProviderID: string(smallModelProviderCfg.ID),
215 activeRequests: sync.Map{},
185 activeRequests: sync.Map{},
216 }
186 }
217
187
188+ go func() {
189+ slog.Info("Initializing agent tools", "agent", agentCfg.ID)
190+
191+ cwd := cfg.WorkingDir()
192+ allTools := []tools.BaseTool{
193+ tools.NewBashTool(permissions, cwd),
194+ tools.NewDownloadTool(permissions, cwd),
195+ tools.NewEditTool(lspClients, permissions, history, cwd),
196+ tools.NewFetchTool(permissions, cwd),
197+ tools.NewGlobTool(cwd),
198+ tools.NewGrepTool(cwd),
199+ tools.NewLsTool(cwd),
200+ tools.NewSourcegraphTool(),
201+ tools.NewViewTool(lspClients, cwd),
202+ tools.NewWriteTool(lspClients, permissions, history, cwd),
203+ }
204+
205+ mcpTools := GetMCPTools(ctx, permissions, cfg)
206+ if len(lspClients) > 0 {
207+ mcpTools = append(mcpTools, tools.NewDiagnosticsTool(lspClients))
208+ }
209+ allTools = append(allTools, mcpTools...)
210+
211+ if agentTool != nil {
212+ allTools = append(allTools, agentTool)
213+ }
214+
215+ agentTools := []tools.BaseTool{}
216+ if agentCfg.AllowedTools == nil {
217+ agentTools = allTools
218+ } else {
219+ for _, tool := range allTools {
220+ if slices.Contains(agentCfg.AllowedTools, tool.Name()) {
221+ agentTools = append(agentTools, tool)
222+ }
223+ }
224+ }
225+
226+ slog.Info("Initialized agent tools", "agent", agentCfg.ID)
227+ agent.tools = agentTools
228+ agent.toolsDone.Store(true)
229+ }()
230+
218 return agent, nil
231 return agent, nil
219}
232}
220
233
@@ -437,6 +450,9 @@ func (a *agent) createUserMessage(ctx context.Context, sessionID, content string
437
450
438func (a *agent) streamAndHandleEvents(ctx context.Context, sessionID string, msgHistory []message.Message) (message.Message, *message.Message, error) {
451func (a *agent) streamAndHandleEvents(ctx context.Context, sessionID string, msgHistory []message.Message) (message.Message, *message.Message, error) {
439 ctx = context.WithValue(ctx, tools.SessionIDContextKey, sessionID)
452 ctx = context.WithValue(ctx, tools.SessionIDContextKey, sessionID)
453+ if !a.toolsDone.Load() {
454+ return message.Message{}, nil, fmt.Errorf("tools not initialized yet")
455+ }
440 eventChan := a.provider.StreamResponse(ctx, msgHistory, a.tools)
456 eventChan := a.provider.StreamResponse(ctx, msgHistory, a.tools)
441
457
442 assistantMsg, err := a.messages.Create(ctx, sessionID, message.CreateMessageParams{
458 assistantMsg, err := a.messages.Create(ctx, sessionID, message.CreateMessageParams{