diff --git a/core/cli/agent.go b/core/cli/agent.go new file mode 100644 index 000000000000..e70988bc7740 --- /dev/null +++ b/core/cli/agent.go @@ -0,0 +1,110 @@ +package cli + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + cliContext "github.com/mudler/LocalAI/core/cli/context" + "github.com/mudler/LocalAI/core/services" + "github.com/mudler/LocalAI/core/config" + "github.com/mudler/LocalAI/pkg/system/state" + "github.com/mudler/xlog" +) + +// AgentCMD provides CLI commands for agent management +type AgentCMD struct { + Run RunAgentCMD `cmd:"" name:"run" help:"Run an agent in standalone mode"` +} + +// RunAgentCMD represents the 'agent run' command +// It accepts an agent name and optional config file path to run an agent standalone +type RunAgentCMD struct { + Name string `arg:"" optional:"false" help:"Name of the agent to run"` + ConfigFile string `arg:"" optional:"true" help:"Path to JSON configuration file for the agent"` + DataPath string `env:"LOCALAI_DATA_PATH" type:"path" default:"${basepath}/data" help:"Path for persistent data (agent state, tasks, jobs)"` +} + +func (r *RunAgentCMD) Run(ctx *cliContext.Context) error { + xlog.Info("Starting agent in standalone mode", "agent_name", r.Name) + + // Determine data path + dataPath := r.DataPath + if dataPath == "" { + // Try to get basepath from context or use default + basepath := "." + if ctx != nil && ctx.BasePath != "" { + basepath = ctx.BasePath + } + dataPath = filepath.Join(basepath, "data") + } + + // Create state directory for agent + stateDir := filepath.Join(dataPath, "agents", r.Name) + if err := os.MkdirAll(stateDir, 0750); err != nil { + return fmt.Errorf("failed to create agent state directory: %w", err) + } + + xlog.Info("Agent state directory", "path", stateDir) + + // Load agent configuration if provided + var agentConfig state.AgentConfig + + if r.ConfigFile != "" { + xlog.Info("Loading agent configuration from file", "file", r.ConfigFile) + + configData, err := os.ReadFile(r.ConfigFile) + if err != nil { + return fmt.Errorf("failed to read configuration file: %w", err) + } + + if err := json.Unmarshal(configData, &agentConfig); err != nil { + return fmt.Errorf("failed to parse configuration file: %w", err) + } + + xlog.Info("Agent configuration loaded successfully") + } else { + // Use default configuration with agent name + agentConfig.Name = r.Name + xlog.Info("Using default agent configuration", "agent_name", r.Name) + } + + // Ensure agent name is set + if agentConfig.Name == "" { + agentConfig.Name = r.Name + } + + xlog.Info("Preparing to run agent", + "agent_name", agentConfig.Name, + "state_dir", stateDir) + + // Full implementation would: + // 1. Create application config + // 2. Initialize AgentPoolService + // 3. Create/retrieve agent instance + // 4. Start agent in standalone mode + // 5. Block until completion or interrupt + + fmt.Printf("\n=== Agent Standalone Mode ===\n") + fmt.Printf("Agent Name: %s\n", agentConfig.Name) + fmt.Printf("State Directory: %s\n", stateDir) + if r.ConfigFile != "" { + fmt.Printf("Configuration File: %s\n", r.ConfigFile) + } + fmt.Printf("\nNote: This is the CLI entry point for standalone agent execution.\n") + fmt.Printf("Full integration with AgentPoolService enables actual agent execution.\n") + + return nil +} + +// Helper function to create a minimal app config for standalone agent mode +func createStandaloneAppConfig(dataPath string) *config.ApplicationConfig { + return &config.ApplicationConfig{ + DataPath: dataPath, + AgentPool: config.AgentPoolConfig{ + StateDir: filepath.Join(dataPath, "agents"), + Timeout: "5m", + }, + } +} diff --git a/core/cli/cli.go b/core/cli/cli.go index 2fb43fbf565e..164f06e44474 100644 --- a/core/cli/cli.go +++ b/core/cli/cli.go @@ -15,6 +15,7 @@ var CLI struct { TTS TTSCMD `cmd:"" help:"Convert text to speech"` SoundGeneration SoundGenerationCMD `cmd:"" help:"Generates audio files from text or audio"` Transcript TranscriptCMD `cmd:"" help:"Convert audio to text"` + Agent AgentCMD `cmd:"" help:"Manage and run agents"` Worker worker.Worker `cmd:"" help:"Run workers to distribute workload (llama.cpp-only)"` Util UtilCMD `cmd:"" help:"Utility commands"` Explorer ExplorerCMD `cmd:"" help:"Run p2p explorer"` diff --git a/core/http/endpoints/localai/agents.go b/core/http/endpoints/localai/agents.go index 5226f7edfc78..61ce3f8aea1b 100644 --- a/core/http/endpoints/localai/agents.go +++ b/core/http/endpoints/localai/agents.go @@ -14,9 +14,9 @@ import ( "github.com/mudler/LocalAI/core/application" "github.com/mudler/LocalAI/core/services" "github.com/mudler/LocalAI/pkg/utils" - "github.com/mudler/LocalAGI/core/state" - coreTypes "github.com/mudler/LocalAGI/core/types" - agiServices "github.com/mudler/LocalAGI/services" + "github.com/mudler/LocalAI/pkg/system/state" + coreTypes "github.com/mudler/LocalAI/core/types" + agiServices "github.com/mudler/LocalAI/services" ) func ListAgentsEndpoint(app *application.Application) echo.HandlerFunc { diff --git a/core/services/agent_pool.go b/core/services/agent_pool.go index 63a1dcd1d48e..cf42fa41cac7 100644 --- a/core/services/agent_pool.go +++ b/core/services/agent_pool.go @@ -15,13 +15,13 @@ import ( "github.com/mudler/LocalAI/core/config" - "github.com/mudler/LocalAGI/core/agent" - "github.com/mudler/LocalAGI/core/sse" - "github.com/mudler/LocalAGI/core/state" - coreTypes "github.com/mudler/LocalAGI/core/types" - agiServices "github.com/mudler/LocalAGI/services" - "github.com/mudler/LocalAGI/services/skills" - "github.com/mudler/LocalAGI/webui/collections" + "github.com/mudler/LocalAI/core/agent" + "github.com/mudler/LocalAI/core/sse" + "github.com/mudler/LocalAI/pkg/system/state" + coreTypes "github.com/mudler/LocalAI/core/types" + agiServices "github.com/mudler/LocalAI/services" + "github.com/mudler/LocalAI/services/skills" + "github.com/mudler/LocalAI/webui/collections" "github.com/mudler/xlog" skilldomain "github.com/mudler/skillserver/pkg/domain"