diff --git a/core/state/pool.go b/core/state/pool.go index 4ea38f6..9adfd6e 100644 --- a/core/state/pool.go +++ b/core/state/pool.go @@ -21,18 +21,18 @@ import ( type AgentPool struct { sync.Mutex - file string - pooldir string - pool AgentPoolData - agents map[string]*Agent - managers map[string]sse.Manager - agentStatus map[string]*Status - apiURL, model, multimodalModel, localRAGAPI, apiKey string - availableActions func(*AgentConfig) func(ctx context.Context) []Action - connectors func(*AgentConfig) []Connector - promptBlocks func(*AgentConfig) []PromptBlock - timeout string - conversationLogs string + file string + pooldir string + pool AgentPoolData + agents map[string]*Agent + managers map[string]sse.Manager + agentStatus map[string]*Status + apiURL, defaultModel, defaultMultimodalModel, localRAGAPI, apiKey string + availableActions func(*AgentConfig) func(ctx context.Context) []Action + connectors func(*AgentConfig) []Connector + promptBlocks func(*AgentConfig) []PromptBlock + timeout string + conversationLogs string } type Status struct { @@ -66,7 +66,7 @@ func loadPoolFromFile(path string) (*AgentPoolData, error) { } func NewAgentPool( - model, multimodalModel, apiURL, apiKey, directory string, + defaultModel, defaultMultimodalModel, apiURL, apiKey, directory string, LocalRAGAPI string, availableActions func(*AgentConfig) func(ctx context.Context) []agent.Action, connectors func(*AgentConfig) []Connector, @@ -87,22 +87,22 @@ func NewAgentPool( if _, err := os.Stat(poolfile); err != nil { // file does not exist, create a new pool return &AgentPool{ - file: poolfile, - pooldir: directory, - apiURL: apiURL, - model: model, - multimodalModel: multimodalModel, - localRAGAPI: LocalRAGAPI, - apiKey: apiKey, - agents: make(map[string]*Agent), - pool: make(map[string]AgentConfig), - agentStatus: make(map[string]*Status), - managers: make(map[string]sse.Manager), - connectors: connectors, - availableActions: availableActions, - promptBlocks: promptBlocks, - timeout: timeout, - conversationLogs: conversationPath, + file: poolfile, + pooldir: directory, + apiURL: apiURL, + defaultModel: defaultModel, + defaultMultimodalModel: defaultMultimodalModel, + localRAGAPI: LocalRAGAPI, + apiKey: apiKey, + agents: make(map[string]*Agent), + pool: make(map[string]AgentConfig), + agentStatus: make(map[string]*Status), + managers: make(map[string]sse.Manager), + connectors: connectors, + availableActions: availableActions, + promptBlocks: promptBlocks, + timeout: timeout, + conversationLogs: conversationPath, }, nil } @@ -111,22 +111,22 @@ func NewAgentPool( return nil, err } return &AgentPool{ - file: poolfile, - apiURL: apiURL, - pooldir: directory, - model: model, - multimodalModel: multimodalModel, - apiKey: apiKey, - agents: make(map[string]*Agent), - managers: make(map[string]sse.Manager), - agentStatus: map[string]*Status{}, - pool: *poolData, - connectors: connectors, - localRAGAPI: LocalRAGAPI, - promptBlocks: promptBlocks, - availableActions: availableActions, - timeout: timeout, - conversationLogs: conversationPath, + file: poolfile, + apiURL: apiURL, + pooldir: directory, + defaultModel: defaultModel, + defaultMultimodalModel: defaultMultimodalModel, + apiKey: apiKey, + agents: make(map[string]*Agent), + managers: make(map[string]sse.Manager), + agentStatus: map[string]*Status{}, + pool: *poolData, + connectors: connectors, + localRAGAPI: LocalRAGAPI, + promptBlocks: promptBlocks, + availableActions: availableActions, + timeout: timeout, + conversationLogs: conversationPath, }, nil } @@ -166,8 +166,8 @@ func (a *AgentPool) GetStatusHistory(name string) *Status { func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error { manager := sse.NewManager(5) ctx := context.Background() - model := a.model - multimodalModel := a.multimodalModel + model := a.defaultModel + multimodalModel := a.defaultMultimodalModel if config.MultimodalModel != "" { multimodalModel = config.MultimodalModel } @@ -340,7 +340,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error go func() { if err := agent.Run(); err != nil { - xlog.Error("Agent stopped", "error", err.Error()) + xlog.Error("Agent stopped", "error", err.Error(), "name", name) panic(err) } }() diff --git a/services/connectors/irc.go b/services/connectors/irc.go index 139b3d8..4eec661 100644 --- a/services/connectors/irc.go +++ b/services/connectors/irc.go @@ -8,7 +8,7 @@ import ( "github.com/mudler/LocalAgent/core/agent" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/services/actions" - "github.com/thoj/go-ircevent" + irc "github.com/thoj/go-ircevent" ) type IRC struct { @@ -53,21 +53,25 @@ func cleanUpMessage(message string, nickname string) string { // isMentioned checks if the bot is mentioned in the message func isMentioned(message string, nickname string) bool { - return strings.Contains(message, nickname+":") || - strings.Contains(message, nickname+",") || - strings.HasPrefix(message, nickname) + return strings.Contains(message, nickname+":") || + strings.Contains(message, nickname+",") || + strings.HasPrefix(message, nickname) } // Start connects to the IRC server and starts listening for messages func (i *IRC) Start(a *agent.Agent) { i.conn = irc.IRC(i.nickname, i.nickname) - i.conn.UseTLS = false + if i.conn == nil { + xlog.Error("Failed to create IRC client") + return + } + i.conn.UseTLS = false i.conn.AddCallback("001", func(e *irc.Event) { xlog.Info("Connected to IRC server", "server", i.server) i.conn.Join(i.channel) xlog.Info("Joined channel", "channel", i.channel) }) - + i.conn.AddCallback("JOIN", func(e *irc.Event) { if e.Nick == i.nickname { xlog.Info("Bot joined channel", "channel", e.Arguments[0]) @@ -128,10 +132,10 @@ func (i *IRC) Start(a *agent.Agent) { chunk = line line = "" } - + // Send the message to the channel i.conn.Privmsg(channel, chunk) - + // Small delay to prevent flooding time.Sleep(500 * time.Millisecond) } diff --git a/webui/app.go b/webui/app.go index aabe00e..3b2fa65 100644 --- a/webui/app.go +++ b/webui/app.go @@ -136,6 +136,56 @@ func (a *App) Create(pool *state.AgentPool) func(c *fiber.Ctx) error { } } +// NEW FUNCTION: Get agent configuration +func (a *App) GetAgentConfig(pool *state.AgentPool) func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + config := pool.GetConfig(c.Params("name")) + if config == nil { + return errorJSONMessage(c, "Agent not found") + } + return c.JSON(config) + } +} + +// UpdateAgentConfig handles updating an agent's configuration +func (a *App) UpdateAgentConfig(pool *state.AgentPool) func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + agentName := c.Params("name") + + // First check if agent exists + oldConfig := pool.GetConfig(agentName) + if oldConfig == nil { + return errorJSONMessage(c, "Agent not found") + } + + // Parse the new configuration using the same approach as Create + newConfig := state.AgentConfig{} + if err := c.BodyParser(&newConfig); err != nil { + xlog.Error("Error parsing agent config", "error", err) + return errorJSONMessage(c, err.Error()) + } + + // Ensure the name doesn't change + newConfig.Name = agentName + + // Remove the agent first + if err := pool.Remove(agentName); err != nil { + return errorJSONMessage(c, "Error removing agent: "+err.Error()) + } + + // Create agent with new config + if err := pool.CreateAgent(agentName, &newConfig); err != nil { + // Try to restore the old configuration if update fails + if restoreErr := pool.CreateAgent(agentName, oldConfig); restoreErr != nil { + return errorJSONMessage(c, fmt.Sprintf("Failed to update agent and restore failed: %v, %v", err, restoreErr)) + } + return errorJSONMessage(c, "Error updating agent: "+err.Error()) + } + + return statusJSONMessage(c, "ok") + } +} + func (a *App) ExportAgent(pool *state.AgentPool) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { agent := pool.GetConfig(c.Params("name")) diff --git a/webui/routes.go b/webui/routes.go index b5b5306..cff70b1 100644 --- a/webui/routes.go +++ b/webui/routes.go @@ -102,11 +102,18 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) { } return c.Render("views/settings", fiber.Map{ - // "Character": agent.Character, - "Name": c.Params("name"), - "Status": status, + "Name": c.Params("name"), + "Status": status, + "Actions": services.AvailableActions, + "Connectors": services.AvailableConnectors, + "PromptBlocks": services.AvailableBlockPrompts, }) }) + + // New API endpoints for getting and updating agent configuration + webapp.Get("/api/agent/:name/config", app.GetAgentConfig(pool)) + webapp.Put("/api/agent/:name/config", app.UpdateAgentConfig(pool)) + webapp.Post("/settings/import", app.ImportAgent(pool)) webapp.Get("/settings/export/:name", app.ExportAgent(pool)) } diff --git a/webui/views/create.html b/webui/views/create.html index 76d5b73..c9d8ae3 100644 --- a/webui/views/create.html +++ b/webui/views/create.html @@ -78,7 +78,7 @@
Export your agent configuration for backup or transfer.
@@ -66,7 +233,15 @@