diff --git a/.dockerignore b/.dockerignore index e9be8812d..1a70682c2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,5 +4,4 @@ !./**/*.template !./**/*.go !./**/*.txt -!./**/*.txt !/pkg/config/default-agent.yaml \ No newline at end of file diff --git a/cmd/root/build.go b/cmd/root/build.go index 2c65e5e92..7fe049914 100644 --- a/cmd/root/build.go +++ b/cmd/root/build.go @@ -5,7 +5,6 @@ import ( "github.com/docker/cagent/pkg/build" "github.com/docker/cagent/pkg/cli" - "github.com/docker/cagent/pkg/filesystem" "github.com/docker/cagent/pkg/telemetry" ) @@ -44,5 +43,5 @@ func (f *buildFlags) runBuildCommand(cmd *cobra.Command, args []string) error { dockerImageName = args[1] } - return build.DockerImage(ctx, out, agentFilename, filesystem.AllowAll, dockerImageName, f.opts) + return build.DockerImage(ctx, out, agentFilename, dockerImageName, f.opts) } diff --git a/cmd/root/feedback.go b/cmd/root/feedback.go index ff104d3ce..debb0419c 100644 --- a/cmd/root/feedback.go +++ b/cmd/root/feedback.go @@ -22,6 +22,6 @@ func newFeedbackCmd() *cobra.Command { func runFeedbackCommand(cmd *cobra.Command, args []string) error { telemetry.TrackCommand("feedback", args) - fmt.Fprintln(cmd.OutOrStdout(), "Feel free to give feedback:\n", feedback.FeedbackLink) + fmt.Fprintln(cmd.OutOrStdout(), "Feel free to give feedback:\n", feedback.Link) return nil } diff --git a/cmd/root/root.go b/cmd/root/root.go index c4ecf5538..b52674c3d 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -119,7 +119,7 @@ For any feedback, please visit: %s We collect anonymous usage data to help improve cagent. To disable: - Set environment variable: TELEMETRY_ENABLED=false -`, feedback.FeedbackLink) +`, feedback.Link) fmt.Fprint(stderr, startupMsg) } diff --git a/docs/PROVIDERS.md b/docs/PROVIDERS.md index eb36be744..2e5123753 100644 --- a/docs/PROVIDERS.md +++ b/docs/PROVIDERS.md @@ -2,10 +2,10 @@ ## Add provider alias -Add a new `Alias` to `ProviderAliases` [`pkg/model/provider/provider.go`](https://github.com/docker/cagent/blob/main/pkg/model/provider/provider.go) +Add a new `Alias` to `Aliases` [`pkg/model/provider/provider.go`](https://github.com/docker/cagent/blob/main/pkg/model/provider/provider.go) ```go -var ProviderAliases = map[string]Alias{ +var Aliases = map[string]Alias{ "requesty": { APIType: "openai", BaseURL: "https://router.requesty.ai/v1", diff --git a/pkg/a2a/executor_wrapper_test.go b/pkg/a2a/executor_wrapper_test.go index 024024620..6342aa3d2 100644 --- a/pkg/a2a/executor_wrapper_test.go +++ b/pkg/a2a/executor_wrapper_test.go @@ -15,12 +15,12 @@ type mockQueue struct { events []a2a.Event } -func (m *mockQueue) Write(ctx context.Context, event a2a.Event) error { +func (m *mockQueue) Write(_ context.Context, event a2a.Event) error { m.events = append(m.events, event) return nil } -func (m *mockQueue) Read(ctx context.Context) (a2a.Event, error) { +func (m *mockQueue) Read(context.Context) (a2a.Event, error) { return nil, nil } diff --git a/pkg/acp/agent.go b/pkg/acp/agent.go index 6c747dedd..7d8d05d4e 100644 --- a/pkg/acp/agent.go +++ b/pkg/acp/agent.go @@ -89,7 +89,7 @@ func (a *Agent) Initialize(ctx context.Context, params acp.InitializeRequest) (a } // NewSession implements [acp.Agent] -func (a *Agent) NewSession(ctx context.Context, params acp.NewSessionRequest) (acp.NewSessionResponse, error) { +func (a *Agent) NewSession(context.Context, acp.NewSessionRequest) (acp.NewSessionResponse, error) { sid := uuid.New().String() slog.Debug("ACP NewSession called", "session_id", sid) @@ -110,19 +110,19 @@ func (a *Agent) NewSession(ctx context.Context, params acp.NewSessionRequest) (a } // Authenticate implements [acp.Agent] -func (a *Agent) Authenticate(ctx context.Context, params acp.AuthenticateRequest) (acp.AuthenticateResponse, error) { +func (a *Agent) Authenticate(context.Context, acp.AuthenticateRequest) (acp.AuthenticateResponse, error) { slog.Debug("ACP Authenticate called") return acp.AuthenticateResponse{}, nil } // LoadSession implements [acp.Agent] (optional, not supported) -func (a *Agent) LoadSession(ctx context.Context, params acp.LoadSessionRequest) (acp.LoadSessionResponse, error) { +func (a *Agent) LoadSession(context.Context, acp.LoadSessionRequest) (acp.LoadSessionResponse, error) { slog.Debug("ACP LoadSession called (not supported)") return acp.LoadSessionResponse{}, fmt.Errorf("load session not supported") } // Cancel implements [acp.Agent] -func (a *Agent) Cancel(ctx context.Context, params acp.CancelNotification) error { +func (a *Agent) Cancel(_ context.Context, params acp.CancelNotification) error { sid := string(params.SessionId) slog.Debug("ACP Cancel called", "session_id", sid) @@ -138,7 +138,7 @@ func (a *Agent) Cancel(ctx context.Context, params acp.CancelNotification) error } // Prompt implements [acp.Agent] -func (a *Agent) Prompt(ctx context.Context, params acp.PromptRequest) (acp.PromptResponse, error) { +func (a *Agent) Prompt(_ context.Context, params acp.PromptRequest) (acp.PromptResponse, error) { sid := string(params.SessionId) slog.Debug("ACP Prompt called", "session_id", sid) @@ -201,7 +201,7 @@ func (a *Agent) Prompt(ctx context.Context, params acp.PromptRequest) (acp.Promp } // SetSessionMode implements acp.Agent (optional) -func (a *Agent) SetSessionMode(ctx context.Context, params acp.SetSessionModeRequest) (acp.SetSessionModeResponse, error) { +func (a *Agent) SetSessionMode(context.Context, acp.SetSessionModeRequest) (acp.SetSessionModeResponse, error) { // We don't implement session modes, cagent agents have only one mode (for now? ;) ). return acp.SetSessionModeResponse{}, nil } @@ -325,7 +325,7 @@ func (a *Agent) handleMaxIterationsReached(ctx context.Context, acpSess *Session permResp, err := a.conn.RequestPermission(ctx, acp.RequestPermissionRequest{ SessionId: acp.SessionId(acpSess.id), ToolCall: acp.RequestPermissionToolCall{ - ToolCallId: acp.ToolCallId("max_iterations"), + ToolCallId: "max_iterations", Title: acp.Ptr(fmt.Sprintf("Maximum iterations (%d) reached", e.MaxIterations)), Kind: acp.Ptr(acp.ToolKindExecute), Status: acp.Ptr(acp.ToolCallStatusPending), @@ -334,12 +334,12 @@ func (a *Agent) handleMaxIterationsReached(ctx context.Context, acpSess *Session { Kind: acp.PermissionOptionKindAllowOnce, Name: "Continue", - OptionId: acp.PermissionOptionId("continue"), + OptionId: "continue", }, { Kind: acp.PermissionOptionKindRejectOnce, Name: "Stop", - OptionId: acp.PermissionOptionId("stop"), + OptionId: "stop", }, }, }) diff --git a/pkg/api/pagination_test.go b/pkg/api/pagination_test.go index 565762284..3ee39b293 100644 --- a/pkg/api/pagination_test.go +++ b/pkg/api/pagination_test.go @@ -136,7 +136,7 @@ func TestPaginateMessages_MaxLimit(t *testing.T) { } func TestPaginateMessages_EmptyMessages(t *testing.T) { - messages := []session.Message{} + var messages []session.Message params := PaginationParams{ Limit: 10, diff --git a/pkg/build/build.go b/pkg/build/build.go index da0a2966a..564e4fdc3 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -14,7 +14,6 @@ import ( "github.com/goccy/go-yaml" "github.com/docker/cagent/pkg/config" - "github.com/docker/cagent/pkg/filesystem" ) //go:embed Dockerfile.template @@ -31,7 +30,7 @@ type Printer interface { Println(a ...any) } -func DockerImage(ctx context.Context, out Printer, agentFilename string, fs filesystem.FS, dockerImageName string, opts Options) error { +func DockerImage(ctx context.Context, out Printer, agentFilename, dockerImageName string, opts Options) error { agentSource, err := config.Resolve(agentFilename) if err != nil { return err diff --git a/pkg/config/auto_test.go b/pkg/config/auto_test.go index 17fb31f74..416116d59 100644 --- a/pkg/config/auto_test.go +++ b/pkg/config/auto_test.go @@ -11,7 +11,7 @@ type mockEnvProvider struct { envVars map[string]string } -func (m *mockEnvProvider) Get(ctx context.Context, name string) string { +func (m *mockEnvProvider) Get(_ context.Context, name string) string { return m.envVars[name] } diff --git a/pkg/config/gather.go b/pkg/config/gather.go index 0a337ae9a..e55ce25e9 100644 --- a/pkg/config/gather.go +++ b/pkg/config/gather.go @@ -56,7 +56,7 @@ func GatherEnvVarsForModels(cfg *latest.Config) []string { model := cfg.Models[modelName] // Use the token environment variable from the alias if available - if alias, exists := provider.ProviderAliases[model.Provider]; exists { + if alias, exists := provider.Aliases[model.Provider]; exists { if alias.TokenEnvVar != "" { requiredEnv[alias.TokenEnvVar] = true } diff --git a/pkg/config/resolve.go b/pkg/config/resolve.go index a232133dd..4219a5539 100644 --- a/pkg/config/resolve.go +++ b/pkg/config/resolve.go @@ -17,7 +17,7 @@ import ( //go:embed default-agent.yaml var defaultAgent []byte -// ResolveSource resolves an agent file reference (local file or OCI image) to a local file path +// ResolveSources resolves an agent file reference (local file or OCI image) to a local file path // For OCI references, always checks remote for updates but falls back to local cache if offline func ResolveSources(agentsPath string) (Sources, error) { resolvedPath, err := resolve(agentsPath) diff --git a/pkg/config/resolve_test.go b/pkg/config/resolve_test.go index 83e77a93c..f3c7e4d97 100644 --- a/pkg/config/resolve_test.go +++ b/pkg/config/resolve_test.go @@ -203,40 +203,6 @@ agents: assert.Equal(t, absPath, resolved) } -func TestResolveAgentFile_RelativeDirectory(t *testing.T) { - t.Parallel() - - tmpDir := t.TempDir() - agentContent := `version: "1" -agents: - root: - model: openai/gpt-4o - description: Test agent - instruction: You are a test agent -` - agentFile := filepath.Join(tmpDir, "agent.yaml") - require.NoError(t, os.WriteFile(agentFile, []byte(agentContent), 0o644)) - - // Change to temp directory and use relative path - originalWd, err := os.Getwd() - require.NoError(t, err) - defer func() { - if err := os.Chdir(originalWd); err != nil { - t.Logf("Failed to restore working directory: %v", err) - } - }() - - require.NoError(t, os.Chdir(tmpDir)) - - // Resolve relative path "." - resolved, err := resolve(".") - require.NoError(t, err) - - absPath, err := filepath.Abs(".") - require.NoError(t, err) - assert.Equal(t, absPath, resolved) -} - func TestResolveAgentFile_NonExistentDirectory(t *testing.T) { t.Parallel() diff --git a/pkg/creator/agent.go b/pkg/creator/agent.go index 9ad43538c..94fbf345c 100644 --- a/pkg/creator/agent.go +++ b/pkg/creator/agent.go @@ -5,18 +5,10 @@ import ( _ "embed" "encoding/json" "fmt" - "log/slog" - "os" "strings" - "github.com/docker/cagent/pkg/agent" "github.com/docker/cagent/pkg/config" "github.com/docker/cagent/pkg/config/latest" - "github.com/docker/cagent/pkg/environment" - "github.com/docker/cagent/pkg/model/provider/anthropic" - "github.com/docker/cagent/pkg/model/provider/options" - "github.com/docker/cagent/pkg/runtime" - "github.com/docker/cagent/pkg/session" "github.com/docker/cagent/pkg/team" "github.com/docker/cagent/pkg/teamloader" "github.com/docker/cagent/pkg/tools" @@ -76,53 +68,6 @@ func (f *fsToolset) customWriteFileHandler(ctx context.Context, toolCall tools.T return f.originalWriteFileHandler(ctx, toolCall) } -func CreateAgent(ctx context.Context, baseDir, prompt string, runConfig *config.RuntimeConfig) (out, path string, err error) { - llm, err := anthropic.NewClient( - ctx, - &latest.ModelConfig{ - Provider: "anthropic", - Model: "claude-sonnet-4-0", - MaxTokens: 64000, - }, - environment.NewDefaultProvider(), - options.WithGateway(runConfig.ModelsGateway), - ) - if err != nil { - return "", "", fmt.Errorf("failed to create LLM client: %w", err) - } - - slog.Info("Generating agent configuration....") - - fsToolset := fsToolset{inner: builtin.NewFilesystemTool([]string{baseDir})} - newTeam := team.New( - team.WithAgents( - agent.New( - "root", - agentBuilderInstructions, - agent.WithModel(llm), - agent.WithToolSets( - builtin.NewShellTool(os.Environ(), runConfig), - &fsToolset, - ), - ))) - rt, err := runtime.New(newTeam) - if err != nil { - return "", "", fmt.Errorf("failed to create runtime: %w", err) - } - - sess := session.New( - session.WithUserMessage(prompt), - session.WithToolsApproved(true), - ) - - messages, err := rt.Run(ctx, sess) - if err != nil { - return "", "", fmt.Errorf("failed to run session: %w", err) - } - - return messages[len(messages)-1].Message.Content, fsToolset.path, nil -} - func Agent(ctx context.Context, runConfig *config.RuntimeConfig, modelNameOverride string) (*team.Team, error) { usableProviders := config.AvailableProviders(ctx, runConfig.ModelsGateway, runConfig.EnvProvider()) diff --git a/pkg/feedback/feedback.go b/pkg/feedback/feedback.go index e07b74b3b..9f863abc9 100644 --- a/pkg/feedback/feedback.go +++ b/pkg/feedback/feedback.go @@ -1,3 +1,3 @@ package feedback -var FeedbackLink = "https://docker.qualtrics.com/jfe/form/SV_cNsCIg92nQemlfw" +var Link = "https://docker.qualtrics.com/jfe/form/SV_cNsCIg92nQemlfw" diff --git a/pkg/filesystem/config.go b/pkg/filesystem/config.go deleted file mode 100644 index 43242fa5d..000000000 --- a/pkg/filesystem/config.go +++ /dev/null @@ -1,17 +0,0 @@ -package filesystem - -import ( - "os" -) - -type FS interface { - ReadFile(name string) ([]byte, error) -} - -var AllowAll FS = &allowAll{} - -type allowAll struct{} - -func (r *allowAll) ReadFile(name string) ([]byte, error) { - return os.ReadFile(name) -} diff --git a/pkg/js/expand.go b/pkg/js/expand.go index e113c8038..8fc08be38 100644 --- a/pkg/js/expand.go +++ b/pkg/js/expand.go @@ -84,7 +84,7 @@ func (exp *Expander) Expand(ctx context.Context, text string) string { return fmt.Sprintf("%v", result.Export()) } -func ExpandString(ctx context.Context, str string, values map[string]string) (string, error) { +func ExpandString(_ context.Context, str string, values map[string]string) (string, error) { vm := goja.New() for k, v := range values { diff --git a/pkg/model/provider/anthropic/beta_client_test.go b/pkg/model/provider/anthropic/beta_client_test.go index 66622a188..ddae53f47 100644 --- a/pkg/model/provider/anthropic/beta_client_test.go +++ b/pkg/model/provider/anthropic/beta_client_test.go @@ -59,8 +59,8 @@ func TestCountAnthropicTokensBeta_Success(t *testing.T) { // TestCountAnthropicTokensBeta_NoAPIKey tests error when API key is missing func TestCountAnthropicTokensBeta_NoAPIKey(t *testing.T) { - messages := []anthropic.BetaMessageParam{} - system := []anthropic.BetaTextBlockParam{} + var messages []anthropic.BetaMessageParam + var system []anthropic.BetaTextBlockParam client := anthropic.NewClient( option.WithAPIKey("test-key"), @@ -80,8 +80,8 @@ func TestCountAnthropicTokensBeta_ServerError(t *testing.T) { })) defer server.Close() - messages := []anthropic.BetaMessageParam{} - system := []anthropic.BetaTextBlockParam{} + var messages []anthropic.BetaMessageParam + var system []anthropic.BetaTextBlockParam client := anthropic.NewClient( option.WithAPIKey("test-key"), @@ -111,8 +111,8 @@ func TestCountAnthropicTokensBeta_WithTools(t *testing.T) { })) defer server.Close() - messages := []anthropic.BetaMessageParam{} - system := []anthropic.BetaTextBlockParam{} + var messages []anthropic.BetaMessageParam + var system []anthropic.BetaTextBlockParam tools := []anthropic.BetaToolUnionParam{ {OfTool: &anthropic.BetaToolParam{ Name: "test_tool", diff --git a/pkg/model/provider/anthropic/client_test.go b/pkg/model/provider/anthropic/client_test.go index cef764475..1a2c528d0 100644 --- a/pkg/model/provider/anthropic/client_test.go +++ b/pkg/model/provider/anthropic/client_test.go @@ -321,8 +321,8 @@ func TestCountAnthropicTokens_Success(t *testing.T) { // TestCountAnthropicTokens_NoAPIKey tests error when API key is missing func TestCountAnthropicTokens_NoAPIKey(t *testing.T) { - messages := []anthropic.MessageParam{} - system := []anthropic.TextBlockParam{} + var messages []anthropic.MessageParam + var system []anthropic.TextBlockParam client := anthropic.NewClient( option.WithAPIKey("test-key"), @@ -342,8 +342,8 @@ func TestCountAnthropicTokens_ServerError(t *testing.T) { })) defer server.Close() - messages := []anthropic.MessageParam{} - system := []anthropic.TextBlockParam{} + var messages []anthropic.MessageParam + var system []anthropic.TextBlockParam client := anthropic.NewClient( option.WithAPIKey("test-key"), @@ -374,8 +374,8 @@ func TestCountAnthropicTokens_WithTools(t *testing.T) { })) defer server.Close() - messages := []anthropic.MessageParam{} - system := []anthropic.TextBlockParam{} + var messages []anthropic.MessageParam + var system []anthropic.TextBlockParam aiTools := []anthropic.ToolUnionParam{ {OfTool: &anthropic.ToolParam{ Name: "test_tool", diff --git a/pkg/model/provider/dmr/client.go b/pkg/model/provider/dmr/client.go index 26f9f011b..3c0e27fa4 100644 --- a/pkg/model/provider/dmr/client.go +++ b/pkg/model/provider/dmr/client.go @@ -571,7 +571,7 @@ func (c *Client) CreateChatCompletionStream(ctx context.Context, messages []chat toolsParam[i] = openai.ChatCompletionFunctionTool(shared.FunctionDefinitionParam{ Name: tool.Name, Description: openai.String(desc), - Parameters: shared.FunctionParameters(paramsMap), + Parameters: paramsMap, }) } params.Tools = toolsParam @@ -631,9 +631,7 @@ func (c *Client) CreateEmbedding(ctx context.Context, text string) (*base.Embedd // Convert []float32 to []float64 embedding32 := response.Data[0].Embedding embedding := make([]float64, len(embedding32)) - for i, v := range embedding32 { - embedding[i] = float64(v) - } + copy(embedding, embedding32) // Extract usage information inputTokens := int(response.Usage.PromptTokens) @@ -690,9 +688,7 @@ func (c *Client) CreateBatchEmbedding(ctx context.Context, texts []string) (*bas for i, data := range response.Data { embedding32 := data.Embedding embedding := make([]float64, len(embedding32)) - for j, v := range embedding32 { - embedding[j] = float64(v) - } + copy(embedding, embedding32) embeddings[i] = embedding } diff --git a/pkg/model/provider/openai/client.go b/pkg/model/provider/openai/client.go index 7a0d76553..300b3c209 100644 --- a/pkg/model/provider/openai/client.go +++ b/pkg/model/provider/openai/client.go @@ -478,7 +478,7 @@ func (c *Client) CreateResponseStream( params.Text.Format.OfJSONSchema = &responses.ResponseFormatTextJSONSchemaConfigParam{ Name: structuredOutput.Name, Description: param.NewOpt(structuredOutput.Description), - Schema: jsonSchema(structuredOutput.Schema), + Schema: structuredOutput.Schema, Strict: param.NewOpt(structuredOutput.Strict), } } @@ -699,9 +699,7 @@ func (c *Client) CreateBatchEmbedding(ctx context.Context, texts []string) (*bas for i, data := range response.Data { embedding32 := data.Embedding embedding := make([]float64, len(embedding32)) - for j, v := range embedding32 { - embedding[j] = float64(v) - } + copy(embedding, embedding32) embeddings[i] = embedding } diff --git a/pkg/model/provider/provider.go b/pkg/model/provider/provider.go index f69c1b10b..963cf6aed 100644 --- a/pkg/model/provider/provider.go +++ b/pkg/model/provider/provider.go @@ -25,8 +25,8 @@ type Alias struct { TokenEnvVar string // Environment variable name for the API token } -// ProviderAliases maps provider names to their corresponding configurations -var ProviderAliases = map[string]Alias{ +// Aliases maps provider names to their corresponding configurations +var Aliases = map[string]Alias{ "requesty": { APIType: "openai", BaseURL: "https://router.requesty.ai/v1", @@ -101,7 +101,7 @@ func New(ctx context.Context, cfg *latest.ModelConfig, env environment.Provider, // Apply provider alias defaults to the config enhancedCfg := applyProviderDefaults(cfg) apiType := "" - if alias, exists := ProviderAliases[cfg.Provider]; exists { + if alias, exists := Aliases[cfg.Provider]; exists { apiType = alias.APIType } @@ -134,7 +134,7 @@ func applyProviderDefaults(cfg *latest.ModelConfig) *latest.ModelConfig { enhancedCfg := *cfg // Check if provider has alias configuration - if alias, exists := ProviderAliases[cfg.Provider]; exists { + if alias, exists := Aliases[cfg.Provider]; exists { // Set default base URL if not already specified if enhancedCfg.BaseURL == "" && alias.BaseURL != "" { enhancedCfg.BaseURL = alias.BaseURL @@ -157,7 +157,7 @@ func resolveProviderType(provider, apiType string) string { } // Check if provider has an alias mapping - if resolved, exists := ProviderAliases[provider]; exists { + if resolved, exists := Aliases[provider]; exists { return resolved.APIType } diff --git a/pkg/rag/prompts/rerank.go b/pkg/rag/prompts/rerank.go index b780db3f8..51f12d41c 100644 --- a/pkg/rag/prompts/rerank.go +++ b/pkg/rag/prompts/rerank.go @@ -21,7 +21,7 @@ func BuildRerankDocumentsPrompt(query string, documents []types.Document) string // Add metadata if present if doc.SourcePath != "" || len(doc.Metadata) > 0 { fmt.Fprintf(&b, " (") - parts := []string{} + var parts []string if doc.SourcePath != "" { parts = append(parts, fmt.Sprintf("source: %s", doc.SourcePath)) diff --git a/pkg/rag/strategy/bm25.go b/pkg/rag/strategy/bm25.go index 906d005d3..2a395b24d 100644 --- a/pkg/rag/strategy/bm25.go +++ b/pkg/rag/strategy/bm25.go @@ -20,7 +20,7 @@ import ( ) // NewBM25FromConfig creates a BM25 strategy from configuration -func NewBM25FromConfig(ctx context.Context, cfg latest.RAGStrategyConfig, buildCtx BuildContext, events chan<- types.Event) (*Config, error) { +func NewBM25FromConfig(_ context.Context, cfg latest.RAGStrategyConfig, buildCtx BuildContext, events chan<- types.Event) (*Config, error) { // Get optional parameters with defaults k1 := GetParam(cfg.Params, "k1", 1.5) bParam := GetParam(cfg.Params, "b", 0.75) diff --git a/pkg/rag/strategy/vector_store.go b/pkg/rag/strategy/vector_store.go index 4ba2bca2c..0e7c1a491 100644 --- a/pkg/rag/strategy/vector_store.go +++ b/pkg/rag/strategy/vector_store.go @@ -522,14 +522,9 @@ func (s *VectorStore) indexFile(ctx context.Context, filePath string, chunkSize, return fmt.Errorf("failed to delete old documents: %w", err) } - var chunks []chunk.Chunk - - if chunks == nil { - var err error - chunks, err = s.processor.ProcessFile(filePath, chunkSize, chunkOverlap, respectWordBoundaries) - if err != nil { - return fmt.Errorf("failed to process file: %w", err) - } + chunks, err := s.processor.ProcessFile(filePath, chunkSize, chunkOverlap, respectWordBoundaries) + if err != nil { + return fmt.Errorf("failed to process file: %w", err) } // Filter out empty chunks diff --git a/pkg/runtime/event.go b/pkg/runtime/event.go index fcae8f42a..15409ff3f 100644 --- a/pkg/runtime/event.go +++ b/pkg/runtime/event.go @@ -328,7 +328,7 @@ func (e *MaxIterationsReachedEvent) GetAgentName() string { return e.AgentName } -// MCP initialization lifecycle events +// MCPInitStartedEvent is for MCP initialization lifecycle events type MCPInitStartedEvent struct { Type string `json:"type"` AgentContext @@ -427,7 +427,7 @@ func ToolsetInfo(availableTools int, agentName string) Event { } } -// RAG lifecycle events +// RAGIndexingStartedEvent is for RAG lifecycle events type RAGIndexingStartedEvent struct { Type string `json:"type"` RAGName string `json:"rag_name"` diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index c8123fed9..988a1c445 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -341,7 +341,7 @@ func (r *LocalRuntime) CurrentAgentCommands(context.Context) map[string]string { return r.CurrentAgent().Commands() } -func (r *LocalRuntime) CurrentWelcomeMessage(ctx context.Context) string { +func (r *LocalRuntime) CurrentWelcomeMessage(context.Context) string { return r.CurrentAgent().WelcomeMessage() } @@ -1363,7 +1363,7 @@ func (r *LocalRuntime) handleTaskTransfer(ctx context.Context, sess *session.Ses }, nil } -func (r *LocalRuntime) handleHandoff(ctx context.Context, sess *session.Session, toolCall tools.ToolCall, evts chan Event) (*tools.ToolCallResult, error) { +func (r *LocalRuntime) handleHandoff(_ context.Context, _ *session.Session, toolCall tools.ToolCall, _ chan Event) (*tools.ToolCallResult, error) { var params builtin.HandoffArgs if err := json.Unmarshal([]byte(toolCall.Function.Arguments), ¶ms); err != nil { return nil, fmt.Errorf("invalid arguments: %w", err) diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go index 06843a6e6..726f17c2c 100644 --- a/pkg/runtime/runtime_test.go +++ b/pkg/runtime/runtime_test.go @@ -407,19 +407,19 @@ func TestContextCancellation(t *testing.T) { // stubRAGStrategy is a minimal implementation of strategy.Strategy for testing RAG initialization. type stubRAGStrategy struct{} -func (s *stubRAGStrategy) Initialize(ctx context.Context, docPaths []string, chunkSize, chunkOverlap int, respectWordBoundaries bool) error { +func (s *stubRAGStrategy) Initialize(context.Context, []string, int, int, bool) error { return nil } -func (s *stubRAGStrategy) Query(ctx context.Context, query string, numResults int, threshold float64) ([]database.SearchResult, error) { +func (s *stubRAGStrategy) Query(context.Context, string, int, float64) ([]database.SearchResult, error) { return nil, nil } -func (s *stubRAGStrategy) CheckAndReindexChangedFiles(ctx context.Context, docPaths []string, chunkSize, chunkOverlap int, respectWordBoundaries bool) error { +func (s *stubRAGStrategy) CheckAndReindexChangedFiles(context.Context, []string, int, int, bool) error { return nil } -func (s *stubRAGStrategy) StartFileWatcher(ctx context.Context, docPaths []string, chunkSize, chunkOverlap int, respectWordBoundaries bool) error { +func (s *stubRAGStrategy) StartFileWatcher(context.Context, []string, int, int, bool) error { return nil } diff --git a/pkg/server/server.go b/pkg/server/server.go index 07098380b..51fd4be16 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -38,7 +38,7 @@ type Server struct { type Opt func(*Server) error -func New(sessionStore session.Store, runConfig *config.RuntimeConfig, agentSources config.Sources, opts ...Opt) (*Server, error) { +func New(sessionStore session.Store, runConfig *config.RuntimeConfig, agentSources config.Sources) (*Server, error) { e := echo.New() e.Use(middleware.CORS()) e.Use(middleware.Logger()) diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index 570c092bb..e123d004b 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -21,7 +21,6 @@ import ( ) func TestServer_ListAgents(t *testing.T) { - // t.Parallel() t.Setenv("OPENAI_API_KEY", "dummy") t.Setenv("ANTHROPIC_API_KEY", "dummy") @@ -48,6 +47,14 @@ func TestServer_ListAgents(t *testing.T) { assert.False(t, agents[2].Multi) } +func TestServer_EmptyList(t *testing.T) { + ctx := t.Context() + lnPath := startServer(t, ctx, prepareAgentsDir(t)) + + buf := httpGET(t, ctx, lnPath, "/api/agents") + assert.Equal(t, "[]\n", string(buf)) // We don't want null, but an empty array +} + func TestServer_ListSessions(t *testing.T) { t.Parallel() diff --git a/pkg/teamloader/registry.go b/pkg/teamloader/registry.go index 03f9968ea..7439c0009 100644 --- a/pkg/teamloader/registry.go +++ b/pkg/teamloader/registry.go @@ -69,14 +69,14 @@ func NewDefaultToolsetRegistry() *ToolsetRegistry { return r } -func createTodoTool(ctx context.Context, toolset latest.Toolset, _ string, _ *config.RuntimeConfig) (tools.ToolSet, error) { +func createTodoTool(_ context.Context, toolset latest.Toolset, _ string, _ *config.RuntimeConfig) (tools.ToolSet, error) { if toolset.Shared { return builtin.NewSharedTodoTool(), nil } return builtin.NewTodoTool(), nil } -func createMemoryTool(ctx context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { +func createMemoryTool(_ context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { var memoryPath string if filepath.IsAbs(toolset.Path) { memoryPath = "" @@ -106,7 +106,7 @@ func createThinkTool(_ context.Context, _ latest.Toolset, _ string, _ *config.Ru return builtin.NewThinkTool(), nil } -func createShellTool(ctx context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { +func createShellTool(ctx context.Context, toolset latest.Toolset, _ string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { env, err := environment.ExpandAll(ctx, environment.ToValues(toolset.Env), runConfig.EnvProvider()) if err != nil { return nil, fmt.Errorf("failed to expand the tool's environment variables: %w", err) @@ -115,7 +115,7 @@ func createShellTool(ctx context.Context, toolset latest.Toolset, parentDir stri return builtin.NewShellTool(env, runConfig), nil } -func createScriptTool(ctx context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { +func createScriptTool(ctx context.Context, toolset latest.Toolset, _ string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { if len(toolset.Shell) == 0 { return nil, fmt.Errorf("shell is required for script toolset") } @@ -128,7 +128,7 @@ func createScriptTool(ctx context.Context, toolset latest.Toolset, parentDir str return builtin.NewScriptShellTool(toolset.Shell, env) } -func createFilesystemTool(ctx context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { +func createFilesystemTool(_ context.Context, toolset latest.Toolset, _ string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { wd := runConfig.WorkingDir if wd == "" { var err error @@ -162,7 +162,7 @@ func createFilesystemTool(ctx context.Context, toolset latest.Toolset, parentDir return builtin.NewFilesystemTool([]string{wd}, opts...), nil } -func createAPITool(ctx context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { +func createAPITool(ctx context.Context, toolset latest.Toolset, _ string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { if toolset.APIConfig.Endpoint == "" { return nil, fmt.Errorf("api tool requires an endpoint in api_config") } @@ -182,7 +182,7 @@ func createFetchTool(_ context.Context, toolset latest.Toolset, _ string, _ *con return builtin.NewFetchTool(opts...), nil } -func createMCPTool(ctx context.Context, toolset latest.Toolset, parentDir string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { +func createMCPTool(ctx context.Context, toolset latest.Toolset, _ string, runConfig *config.RuntimeConfig) (tools.ToolSet, error) { // MCP tool has three different modes: ref, command, and remote if toolset.Ref != "" { mcpServerName := gateway.ParseServerRef(toolset.Ref) @@ -193,7 +193,7 @@ func createMCPTool(ctx context.Context, toolset latest.Toolset, parentDir string // TODO(dga): until the MCP Gateway supports oauth with cagent, we fetch the remote url and directly connect to it. if serverSpec.Type == "remote" { - return mcp.NewRemoteToolset(serverSpec.Remote.URL, serverSpec.Remote.TransportType, nil, runConfig.WorkingDir), nil + return mcp.NewRemoteToolset(serverSpec.Remote.URL, serverSpec.Remote.TransportType, nil), nil } return mcp.NewGatewayToolset(ctx, mcpServerName, toolset.Config, runConfig.EnvProvider(), runConfig.WorkingDir) @@ -219,7 +219,7 @@ func createMCPTool(ctx context.Context, toolset latest.Toolset, parentDir string headers[k] = expanded } - return mcp.NewRemoteToolset(toolset.Remote.URL, toolset.Remote.TransportType, headers, runConfig.WorkingDir), nil + return mcp.NewRemoteToolset(toolset.Remote.URL, toolset.Remote.TransportType, headers), nil } return nil, fmt.Errorf("mcp toolset requires either ref, command, or remote configuration") diff --git a/pkg/teamloader/teamloader.go b/pkg/teamloader/teamloader.go index 38f003047..70e6e32f8 100644 --- a/pkg/teamloader/teamloader.go +++ b/pkg/teamloader/teamloader.go @@ -53,12 +53,6 @@ func Load(ctx context.Context, agentSource config.Source, runConfig *config.Runt } } - env := runConfig.EnvProvider() - parentDir := agentSource.ParentDir() - if parentDir == "" { - parentDir = runConfig.WorkingDir - } - // Load the agent's configuration cfg, err := config.Load(ctx, agentSource) if err != nil { @@ -71,11 +65,16 @@ func Load(ctx context.Context, agentSource config.Source, runConfig *config.Runt } // Early check for required env vars before loading models and tools. + env := runConfig.EnvProvider() if err := config.CheckRequiredEnvVars(ctx, cfg, runConfig.ModelsGateway, env); err != nil { return nil, err } // Create RAG managers + parentDir := agentSource.ParentDir() + if parentDir == "" { + parentDir = runConfig.WorkingDir + } ragManagers, err := rag.NewManagers(ctx, cfg, rag.ManagersBuildConfig{ ParentDir: parentDir, ModelsGateway: runConfig.ModelsGateway, diff --git a/pkg/tools/builtin/deferred.go b/pkg/tools/builtin/deferred.go index 70549fe1a..9b97e1d67 100644 --- a/pkg/tools/builtin/deferred.go +++ b/pkg/tools/builtin/deferred.go @@ -87,7 +87,7 @@ type AddToolArgs struct { Name string `json:"name" jsonschema:"The name of the tool to activate"` } -func (d *DeferredToolset) handleSearchTool(ctx context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) { +func (d *DeferredToolset) handleSearchTool(_ context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) { var args SearchToolArgs if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil { return nil, fmt.Errorf("failed to parse arguments: %w", err) @@ -127,7 +127,7 @@ func (d *DeferredToolset) handleSearchTool(ctx context.Context, toolCall tools.T }, nil } -func (d *DeferredToolset) handleAddTool(ctx context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) { +func (d *DeferredToolset) handleAddTool(_ context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) { var args AddToolArgs if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil { return nil, fmt.Errorf("failed to parse arguments: %w", err) @@ -157,7 +157,7 @@ func (d *DeferredToolset) handleAddTool(ctx context.Context, toolCall tools.Tool }, nil } -func (d *DeferredToolset) Tools(ctx context.Context) ([]tools.Tool, error) { +func (d *DeferredToolset) Tools(context.Context) ([]tools.Tool, error) { d.mu.RLock() defer d.mu.RUnlock() @@ -223,6 +223,6 @@ func (d *DeferredToolset) Start(ctx context.Context) error { return nil } -func (d *DeferredToolset) Stop(ctx context.Context) error { +func (d *DeferredToolset) Stop(context.Context) error { return nil } diff --git a/pkg/tools/builtin/shell.go b/pkg/tools/builtin/shell.go index cbfed2373..2b5284860 100644 --- a/pkg/tools/builtin/shell.go +++ b/pkg/tools/builtin/shell.go @@ -218,7 +218,7 @@ func (h *shellHandler) RunShell(ctx context.Context, toolCall tools.ToolCall) (* } } -func (h *shellHandler) RunShellBackground(ctx context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) { +func (h *shellHandler) RunShellBackground(_ context.Context, toolCall tools.ToolCall) (*tools.ToolCallResult, error) { var params RunShellBackgroundArgs if err := json.Unmarshal([]byte(toolCall.Function.Arguments), ¶ms); err != nil { return nil, fmt.Errorf("invalid arguments: %w", err) diff --git a/pkg/tools/codemode/codemode_test.go b/pkg/tools/codemode/codemode_test.go index beccd5d2b..a4b6ba03d 100644 --- a/pkg/tools/codemode/codemode_test.go +++ b/pkg/tools/codemode/codemode_test.go @@ -170,7 +170,7 @@ type testToolSet struct { stop int } -func (t *testToolSet) Tools(ctx context.Context) ([]tools.Tool, error) { +func (t *testToolSet) Tools(context.Context) ([]tools.Tool, error) { return t.tools, nil } diff --git a/pkg/tools/mcp/mcp.go b/pkg/tools/mcp/mcp.go index 774694158..ea21a4ed6 100644 --- a/pkg/tools/mcp/mcp.go +++ b/pkg/tools/mcp/mcp.go @@ -49,7 +49,7 @@ func NewToolsetCommand(command string, args, env []string, cwd string) *Toolset } // NewRemoteToolset creates a new MCP toolset from a remote MCP Server. -func NewRemoteToolset(url, transport string, headers map[string]string, cwd string) *Toolset { +func NewRemoteToolset(url, transport string, headers map[string]string) *Toolset { slog.Debug("Creating Remote MCP toolset", "url", url, "transport", transport, "headers", headers) return &Toolset{ diff --git a/pkg/tui/commands/commands.go b/pkg/tui/commands/commands.go index 60aa3275e..a71a55093 100644 --- a/pkg/tui/commands/commands.go +++ b/pkg/tui/commands/commands.go @@ -23,13 +23,13 @@ type ( OpenURLMsg = messages.OpenURLMsg ) -// CommandCategory represents a category of commands +// Category represents a category of commands type Category struct { Name string Commands []Item } -// Command represents a single command in the palette +// Item represents a single command in the palette type Item struct { ID string Label string @@ -111,7 +111,7 @@ func builtInFeedbackCommands() []Item { Description: "Provide feedback about cagent", Category: "Feedback", Execute: func() tea.Cmd { - return core.CmdHandler(OpenURLMsg{URL: feedback.FeedbackLink}) + return core.CmdHandler(OpenURLMsg{URL: feedback.Link}) }, }, } diff --git a/pkg/tui/components/messages/messages.go b/pkg/tui/components/messages/messages.go index c6c51700b..9d85734fe 100644 --- a/pkg/tui/components/messages/messages.go +++ b/pkg/tui/components/messages/messages.go @@ -15,7 +15,6 @@ import ( "github.com/docker/cagent/pkg/app" "github.com/docker/cagent/pkg/runtime" "github.com/docker/cagent/pkg/tools" - "github.com/docker/cagent/pkg/tui/components/markdown" "github.com/docker/cagent/pkg/tui/components/message" "github.com/docker/cagent/pkg/tui/components/notification" "github.com/docker/cagent/pkg/tui/components/tool" @@ -705,9 +704,7 @@ func (m *model) ScrollToBottom() tea.Cmd { } func (m *model) createToolCallView(msg *types.Message) layout.Model { - // TODO: -3 because of the padding in the tool calls, we should really make - // our own layout system and stop messing around with these magic numbers - view := tool.New(msg, markdown.NewRenderer(m.width-3), m.sessionState) + view := tool.New(msg, m.sessionState) view.SetSize(m.width, 0) return view } diff --git a/pkg/tui/components/tool/factory.go b/pkg/tui/components/tool/factory.go index d835cbfc5..2333b9dcf 100644 --- a/pkg/tui/components/tool/factory.go +++ b/pkg/tui/components/tool/factory.go @@ -1,8 +1,6 @@ package tool import ( - "github.com/charmbracelet/glamour/v2" - "github.com/docker/cagent/pkg/tools/builtin" "github.com/docker/cagent/pkg/tui/components/tool/defaulttool" "github.com/docker/cagent/pkg/tui/components/tool/editfile" @@ -29,11 +27,7 @@ func NewFactory(registry *Registry) *Factory { } } -func (f *Factory) Create( - msg *types.Message, - renderer *glamour.TermRenderer, - sessionState *service.SessionState, -) layout.Model { +func (f *Factory) Create(msg *types.Message, sessionState *service.SessionState) layout.Model { toolName := msg.ToolCall.Function.Name if builder, ok := f.registry.Get(toolName); ok { @@ -64,10 +58,6 @@ func newDefaultRegistry() *Registry { return registry } -func New( - msg *types.Message, - renderer *glamour.TermRenderer, - sessionState *service.SessionState, -) layout.Model { - return defaultFactory.Create(msg, renderer, sessionState) +func New(msg *types.Message, sessionState *service.SessionState) layout.Model { + return defaultFactory.Create(msg, sessionState) } diff --git a/pkg/tui/components/tool/transfertask/transfertask.go b/pkg/tui/components/tool/transfertask/transfertask.go index 98a9f39ec..d529ec44b 100644 --- a/pkg/tui/components/tool/transfertask/transfertask.go +++ b/pkg/tui/components/tool/transfertask/transfertask.go @@ -26,7 +26,7 @@ func New( } } -func (c *Component) SetSize(width, height int) tea.Cmd { +func (c *Component) SetSize(int, int) tea.Cmd { return nil } diff --git a/pkg/tui/messages/messages.go b/pkg/tui/messages/messages.go index bbc3d7959..16cd3e5ec 100644 --- a/pkg/tui/messages/messages.go +++ b/pkg/tui/messages/messages.go @@ -9,23 +9,22 @@ type ( ToggleYoloMsg struct{} ) -// Agent command message +// AgentCommandMsg command message type AgentCommandMsg struct { Command string } -// MCP Prompt command message +// MCPPromptMsg command message type MCPPromptMsg struct { PromptName string Arguments map[string]string } -// URL opening message +// OpenURLMsg is a url for opening message type OpenURLMsg struct { URL string } -// Dialog messages type ShowMCPPromptInputMsg struct { PromptName string PromptInfo any // mcptools.PromptInfo but avoiding import cycles