From 185fb89d39572ea3a136b91f8387a29476627522 Mon Sep 17 00:00:00 2001 From: mudler Date: Mon, 8 Apr 2024 11:15:36 +0200 Subject: [PATCH] wips --- agent/agent.go | 53 ++++++++++++++++++++++++-------------- example/webui/agentpool.go | 48 ++++++++++++++++++++++++---------- example/webui/create.html | 33 +++++++++++++++++------- example/webui/elements.go | 6 ++--- example/webui/index.html | 9 ++++++- example/webui/main.go | 20 +++++++++++--- external/search.go | 2 +- 7 files changed, 120 insertions(+), 51 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 29b00a1..8bd42a6 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -59,12 +59,6 @@ func New(opts ...Option) (*Agent, error) { context: action.NewContext(ctx, cancel), } - if a.options.randomIdentity { - if err = a.generateIdentity(a.options.randomIdentityGuidance); err != nil { - return a, fmt.Errorf("failed to generate identity: %v", err) - } - } - if a.options.statefile != "" { if _, err := os.Stat(a.options.statefile); err == nil { if err = a.LoadState(a.options.statefile); err != nil { @@ -73,20 +67,6 @@ func New(opts ...Option) (*Agent, error) { } } - if a.options.characterfile != "" { - if _, err := os.Stat(a.options.characterfile); err == nil { - // if there is a file, load the character back - if err = a.LoadCharacter(a.options.characterfile); err != nil { - return a, fmt.Errorf("failed to load character: %v", err) - } - } else { - // otherwise save it for next time - if err = a.SaveCharacter(a.options.characterfile); err != nil { - return a, fmt.Errorf("failed to save character: %v", err) - } - } - } - if a.options.debugMode { fmt.Println("=== Agent in Debug mode ===") fmt.Println(a.Character.String()) @@ -520,11 +500,44 @@ func (a *Agent) periodicallyRun() { // a.ResetConversation() } +func (a *Agent) prepareIdentity() error { + + if a.options.characterfile != "" { + if _, err := os.Stat(a.options.characterfile); err == nil { + // if there is a file, load the character back + if err = a.LoadCharacter(a.options.characterfile); err != nil { + return fmt.Errorf("failed to load character: %v", err) + } + } else { + if a.options.randomIdentity { + if err = a.generateIdentity(a.options.randomIdentityGuidance); err != nil { + return fmt.Errorf("failed to generate identity: %v", err) + } + } + + // otherwise save it for next time + if err = a.SaveCharacter(a.options.characterfile); err != nil { + return fmt.Errorf("failed to save character: %v", err) + } + } + } else { + if err := a.generateIdentity(a.options.randomIdentityGuidance); err != nil { + return fmt.Errorf("failed to generate identity: %v", err) + } + } + + return nil +} + func (a *Agent) Run() error { // The agent run does two things: // picks up requests from a queue // and generates a response/perform actions + if err := a.prepareIdentity(); err != nil { + return fmt.Errorf("failed to prepare identity: %v", err) + } + // It is also preemptive. // That is, it can interrupt the current action // if another one comes in. diff --git a/example/webui/agentpool.go b/example/webui/agentpool.go index 8bb0a6f..90bc788 100644 --- a/example/webui/agentpool.go +++ b/example/webui/agentpool.go @@ -19,17 +19,17 @@ type ConnectorConfig struct { type ActionsConfig string type AgentConfig struct { - Connector []ConnectorConfig `json:"connector"` - Actions []ActionsConfig `json:"actions"` + Connector []ConnectorConfig `json:"connector" form:"connector" ` + Actions []ActionsConfig `json:"actions" form:"actions"` // This is what needs to be part of ActionsConfig - Model string `json:"model"` - Name string `json:"name"` - HUD bool `json:"hud"` - Debug bool `json:"debug"` - StandaloneJob bool `json:"standalone_job"` - RandomIdentity bool `json:"random_identity"` - IdentityGuidance string `json:"identity_guidance"` - PeriodicRuns string `json:"periodic_runs"` + Model string `json:"model" form:"model"` + Name string `json:"name" form:"name"` + HUD bool `json:"hud" form:"hud"` + Debug bool `json:"debug" form:"debug"` + StandaloneJob bool `json:"standalone_job" form:"standalone_job"` + RandomIdentity bool `json:"random_identity" form:"random_identity"` + IdentityGuidance string `json:"identity_guidance" form:"identity_guidance"` + PeriodicRuns string `json:"periodic_runs" form:"periodic_runs"` } type AgentPool struct { @@ -104,13 +104,17 @@ func (a *AgentPool) List() []string { return agents } +var AvailableActions = []string{"search"} + func (a *AgentConfig) availableActions() []Action { actions := []Action{} + if len(a.Actions) == 0 { // Return search as default return []Action{external.NewSearch(3)} } for _, action := range a.Actions { + fmt.Println("Set Action", action) switch action { case "search": actions = append(actions, external.NewSearch(3)) @@ -134,6 +138,10 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error fmt.Println("API URL", a.apiURL) actions := config.availableActions() + + stateFile, characterFile := a.stateFiles(name) + + fmt.Println("Actions", actions) opts := []Option{ WithModel(model), WithLLMAPIURL(a.apiURL), @@ -141,8 +149,8 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error WithActions( actions..., ), - WithStateFile(filepath.Join(a.pooldir, fmt.Sprintf("%s.state.json", name))), - WithCharacterFile(filepath.Join(a.pooldir, fmt.Sprintf("%s.character.json", name))), + WithStateFile(stateFile), + WithCharacterFile(characterFile), WithAgentReasoningCallback(func(state ActionCurrentState) bool { fmt.Println("Reasoning", state.Reasoning) manager.Send( @@ -201,7 +209,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error go func() { if err := agent.Run(); err != nil { - panic(err) + fmt.Println("Agent stop: ", err.Error()) } }() @@ -253,7 +261,21 @@ func (a *AgentPool) Start(name string) error { return fmt.Errorf("agent %s not found", name) } +func (a *AgentPool) stateFiles(name string) (string, string) { + stateFile := filepath.Join(a.pooldir, fmt.Sprintf("%s.state.json", name)) + characterFile := filepath.Join(a.pooldir, fmt.Sprintf("%s.character.json", name)) + + return stateFile, characterFile +} + func (a *AgentPool) Remove(name string) error { + + // Cleanup character and state + stateFile, characterFile := a.stateFiles(name) + + os.Remove(stateFile) + os.Remove(characterFile) + a.Stop(name) delete(a.agents, name) delete(a.pool, name) diff --git a/example/webui/create.html b/example/webui/create.html index 1e1fef8..fed8dba 100644 --- a/example/webui/create.html +++ b/example/webui/create.html @@ -4,25 +4,38 @@ Create New Agent + + +

Create New Agent

-
- - -
-
- - -
- - +
+ + +
+ + +
+ + + +
diff --git a/example/webui/elements.go b/example/webui/elements.go index 0a82bba..a0cfbd6 100644 --- a/example/webui/elements.go +++ b/example/webui/elements.go @@ -12,10 +12,10 @@ func loader() string { return `
` } -func inputMessageDisabled(disabled bool) string { +func disabledElement(id string, disabled bool) string { if disabled { - return `` + return `` } - return `` + return `` } diff --git a/example/webui/index.html b/example/webui/index.html index 936ef3b..a1ee34d 100644 --- a/example/webui/index.html +++ b/example/webui/index.html @@ -4,6 +4,7 @@ Agent List +
@@ -24,7 +25,10 @@ Status - Edit + Talk + + + Delete @@ -37,6 +41,9 @@ Talk + + Delete + {{ end }} diff --git a/example/webui/main.go b/example/webui/main.go index f41fef5..5465623 100644 --- a/example/webui/main.go +++ b/example/webui/main.go @@ -88,7 +88,8 @@ func main() { webapp.Get("/create", func(c *fiber.Ctx) error { return c.Render("create.html", fiber.Map{ - "Title": "Hello, World!", + "Title": "Hello, World!", + "Actions": AvailableActions, }) }) @@ -107,6 +108,7 @@ func main() { webapp.Get("/notify/:name", app.Notify(pool)) webapp.Post("/chat/:name", app.Chat(pool)) webapp.Post("/create", app.Create(pool)) + webapp.Get("/delete/:name", app.Delete(pool)) webapp.Get("/talk/:name", func(c *fiber.Ctx) error { return c.Render("chat.html", fiber.Map{ @@ -166,6 +168,16 @@ func (a *App) Notify(pool *AgentPool) func(c *fiber.Ctx) error { } } +func (a *App) Delete(pool *AgentPool) func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + if err := pool.Remove(c.Params("name")); err != nil { + fmt.Println("Error removing agent", err) + return c.Status(http.StatusInternalServerError).SendString(err.Error()) + } + return c.Redirect("/") + } +} + func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { config := AgentConfig{} @@ -173,6 +185,8 @@ func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error { return err } + fmt.Printf("Agent configuration: %+v\n", config) + if config.Name == "" { c.Status(http.StatusBadRequest).SendString("Name is required") return nil @@ -223,7 +237,7 @@ func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error { ).WithEvent("messages")) manager.Send( NewMessage( - inputMessageDisabled(false), // show again the input + disabledElement("inputMessage", false), // show again the input ).WithEvent("message_status")) //result := `done` @@ -232,7 +246,7 @@ func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error { manager.Send( NewMessage( - loader() + inputMessageDisabled(true), + loader() + disabledElement("inputMessage", true), ).WithEvent("message_status")) return nil diff --git a/external/search.go b/external/search.go index 865c470..6335e0d 100644 --- a/external/search.go +++ b/external/search.go @@ -30,7 +30,7 @@ func (a *SearchAction) Run(params action.ActionParams) (string, error) { ddg := client.NewDuckDuckGoSearchClient() res, err := ddg.SearchLimited(result.Query, a.results) if err != nil { - msg := fmt.Sprintf("error: %v", err) + msg := fmt.Sprintf("error duckduckgo: %v", err) fmt.Printf(msg) return msg, err }