diff --git a/example/webui/app.go b/example/webui/app.go
new file mode 100644
index 0000000..7ee03ec
--- /dev/null
+++ b/example/webui/app.go
@@ -0,0 +1,155 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+
+ . "github.com/mudler/local-agent-framework/agent"
+
+ "github.com/donseba/go-htmx"
+ fiber "github.com/gofiber/fiber/v2"
+)
+
+type (
+ App struct {
+ htmx *htmx.HTMX
+ pool *AgentPool
+ }
+)
+
+func (a *App) KnowledgeBase(db *InMemoryDatabase) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ payload := struct {
+ URL string `form:"url"`
+ ChunkSize int `form:"chunk_size"`
+ }{}
+
+ if err := c.BodyParser(&payload); err != nil {
+ return err
+ }
+
+ website := payload.URL
+ if website == "" {
+ return fmt.Errorf("please enter a URL")
+ }
+ chunkSize := defaultChunkSize
+ if payload.ChunkSize > 0 {
+ chunkSize = payload.ChunkSize
+ }
+
+ go WebsiteToKB(website, chunkSize, db)
+
+ return c.Redirect("/knowledgebase")
+ }
+}
+
+func (a *App) Notify(pool *AgentPool) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ payload := struct {
+ Message string `form:"message"`
+ }{}
+
+ if err := c.BodyParser(&payload); err != nil {
+ return err
+ }
+
+ query := payload.Message
+ if query == "" {
+ _, _ = c.Write([]byte("Please enter a message."))
+ return nil
+ }
+
+ agent := pool.GetAgent(c.Params("name"))
+ agent.Ask(
+ WithText(query),
+ )
+ _, _ = c.Write([]byte("Message sent"))
+
+ return nil
+ }
+}
+
+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("/agents")
+ }
+}
+
+func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ config := AgentConfig{}
+ if err := c.BodyParser(&config); err != nil {
+ return err
+ }
+
+ fmt.Printf("Agent configuration: %+v\n", config)
+
+ if config.Name == "" {
+ c.Status(http.StatusBadRequest).SendString("Name is required")
+ return nil
+ }
+ if err := pool.CreateAgent(config.Name, &config); err != nil {
+ c.Status(http.StatusInternalServerError).SendString(err.Error())
+ return nil
+ }
+ return c.Redirect("/agents")
+ }
+}
+
+func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ payload := struct {
+ Message string `json:"message"`
+ }{}
+
+ if err := c.BodyParser(&payload); err != nil {
+ return err
+ }
+ agentName := c.Params("name")
+ manager := pool.GetManager(agentName)
+
+ query := payload.Message
+ if query == "" {
+ _, _ = c.Write([]byte("Please enter a message."))
+ return nil
+ }
+ manager.Send(
+ NewMessage(
+ chatDiv(query, "gray"),
+ ).WithEvent("messages"))
+
+ go func() {
+ agent := pool.GetAgent(agentName)
+ if agent == nil {
+ fmt.Println("Agent not found in pool", c.Params("name"))
+ return
+ }
+ res := agent.Ask(
+ WithText(query),
+ )
+ fmt.Println("response is", res.Response)
+ manager.Send(
+ NewMessage(
+ chatDiv(res.Response, "blue"),
+ ).WithEvent("messages"))
+ manager.Send(
+ NewMessage(
+ disabledElement("inputMessage", false), // show again the input
+ ).WithEvent("message_status"))
+
+ //result := `done`
+ // _, _ = w.Write([]byte(result))
+ }()
+
+ manager.Send(
+ NewMessage(
+ loader() + disabledElement("inputMessage", true),
+ ).WithEvent("message_status"))
+
+ return nil
+ }
+}
diff --git a/example/webui/elements.go b/example/webui/elements.go
index a0cfbd6..7bc363f 100644
--- a/example/webui/elements.go
+++ b/example/webui/elements.go
@@ -1,6 +1,9 @@
package main
-import "fmt"
+import (
+ "fmt"
+ "strings"
+)
// TODO: switch to https://github.com/chasefleming/elem-go
@@ -19,3 +22,9 @@ func disabledElement(id string, disabled bool) string {
return ``
}
+
+func htmlIfy(s string) string {
+ s = strings.TrimSpace(s)
+ s = strings.ReplaceAll(s, "\n", "
")
+ return s
+}
diff --git a/example/webui/main.go b/example/webui/main.go
index efdf2aa..3848735 100644
--- a/example/webui/main.go
+++ b/example/webui/main.go
@@ -4,10 +4,8 @@ import (
"embed"
"fmt"
"log"
- "math/rand"
"net/http"
"os"
- "strings"
"github.com/donseba/go-htmx"
fiber "github.com/gofiber/fiber/v2"
@@ -18,13 +16,6 @@ import (
"github.com/mudler/local-agent-framework/llm/rag"
)
-type (
- App struct {
- htmx *htmx.HTMX
- pool *AgentPool
- }
-)
-
var testModel = os.Getenv("TEST_MODEL")
var apiURL = os.Getenv("API_URL")
var apiKey = os.Getenv("API_KEY")
@@ -42,12 +33,6 @@ func init() {
}
}
-func htmlIfy(s string) string {
- s = strings.TrimSpace(s)
- s = strings.ReplaceAll(s, "\n", "
")
- return s
-}
-
//go:embed views/*
var viewsfs embed.FS
@@ -108,207 +93,7 @@ func main() {
Views: engine,
})
- // Serve static files
- webapp.Static("/", "./public")
-
- webapp.Get("/", func(c *fiber.Ctx) error {
- return c.Render("views/index", fiber.Map{
- "Agents": pool.List(),
- })
- })
-
- webapp.Get("/agents", func(c *fiber.Ctx) error {
- return c.Render("views/agents", fiber.Map{
- "Agents": pool.List(),
- })
- })
-
- webapp.Get("/create", func(c *fiber.Ctx) error {
- return c.Render("views/create", fiber.Map{
- "Title": "Hello, World!",
- "Actions": AvailableActions,
- "Connectors": AvailableConnectors,
- })
- })
-
- webapp.Get("/knowledgebase", func(c *fiber.Ctx) error {
- return c.Render("views/knowledgebase", fiber.Map{
- "Title": "Hello, World!",
- "KnowledgebaseItemsCount": len(db.Database),
- })
- })
-
- // Define a route for the GET method on the root path '/'
- webapp.Get("/sse/:name", func(c *fiber.Ctx) error {
-
- m := pool.GetManager(c.Params("name"))
- if m == nil {
- return c.SendStatus(404)
- }
-
- m.Handle(c, NewClient(randStringRunes(10)))
- return nil
- })
-
- 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.Post("/knowledgebase", app.KnowledgeBase(db))
-
- webapp.Get("/talk/:name", func(c *fiber.Ctx) error {
- return c.Render("chat.html", fiber.Map{
- // "Character": agent.Character,
- "Name": c.Params("name"),
- })
- })
+ RegisterRoutes(webapp, pool, db, app)
log.Fatal(webapp.Listen(":3000"))
}
-
-func (a *App) KnowledgeBase(db *InMemoryDatabase) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- payload := struct {
- URL string `form:"url"`
- ChunkSize int `form:"chunk_size"`
- }{}
-
- if err := c.BodyParser(&payload); err != nil {
- return err
- }
-
- website := payload.URL
- if website == "" {
- return fmt.Errorf("please enter a URL")
- }
- chunkSize := defaultChunkSize
- if payload.ChunkSize > 0 {
- chunkSize = payload.ChunkSize
- }
-
- go WebsiteToKB(website, chunkSize, db)
-
- return c.Redirect("/knowledgebase")
- }
-}
-
-func (a *App) Notify(pool *AgentPool) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- payload := struct {
- Message string `form:"message"`
- }{}
-
- if err := c.BodyParser(&payload); err != nil {
- return err
- }
-
- query := payload.Message
- if query == "" {
- _, _ = c.Write([]byte("Please enter a message."))
- return nil
- }
-
- agent := pool.GetAgent(c.Params("name"))
- agent.Ask(
- WithText(query),
- )
- _, _ = c.Write([]byte("Message sent"))
-
- return nil
- }
-}
-
-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("/agents")
- }
-}
-
-func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- config := AgentConfig{}
- if err := c.BodyParser(&config); err != nil {
- return err
- }
-
- fmt.Printf("Agent configuration: %+v\n", config)
-
- if config.Name == "" {
- c.Status(http.StatusBadRequest).SendString("Name is required")
- return nil
- }
- if err := pool.CreateAgent(config.Name, &config); err != nil {
- c.Status(http.StatusInternalServerError).SendString(err.Error())
- return nil
- }
- return c.Redirect("/agents")
- }
-}
-
-func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- payload := struct {
- Message string `json:"message"`
- }{}
-
- if err := c.BodyParser(&payload); err != nil {
- return err
- }
- agentName := c.Params("name")
- manager := pool.GetManager(agentName)
-
- query := payload.Message
- if query == "" {
- _, _ = c.Write([]byte("Please enter a message."))
- return nil
- }
- manager.Send(
- NewMessage(
- chatDiv(query, "gray"),
- ).WithEvent("messages"))
-
- go func() {
- agent := pool.GetAgent(agentName)
- if agent == nil {
- fmt.Println("Agent not found in pool", c.Params("name"))
- return
- }
- res := agent.Ask(
- WithText(query),
- )
- fmt.Println("response is", res.Response)
- manager.Send(
- NewMessage(
- chatDiv(res.Response, "blue"),
- ).WithEvent("messages"))
- manager.Send(
- NewMessage(
- disabledElement("inputMessage", false), // show again the input
- ).WithEvent("message_status"))
-
- //result := `done`
- // _, _ = w.Write([]byte(result))
- }()
-
- manager.Send(
- NewMessage(
- loader() + disabledElement("inputMessage", true),
- ).WithEvent("message_status"))
-
- return nil
- }
-}
-
-var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
-
-func randStringRunes(n int) string {
- b := make([]rune, n)
- for i := range b {
- b[i] = letterRunes[rand.Intn(len(letterRunes))]
- }
- return string(b)
-}
diff --git a/example/webui/routes.go b/example/webui/routes.go
new file mode 100644
index 0000000..0dda837
--- /dev/null
+++ b/example/webui/routes.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "math/rand"
+
+ fiber "github.com/gofiber/fiber/v2"
+)
+
+func RegisterRoutes(webapp *fiber.App, pool *AgentPool, db *InMemoryDatabase, app *App) {
+
+ // Serve static files
+ webapp.Static("/", "./public")
+
+ webapp.Get("/", func(c *fiber.Ctx) error {
+ return c.Render("views/index", fiber.Map{
+ "Agents": pool.List(),
+ })
+ })
+
+ webapp.Get("/agents", func(c *fiber.Ctx) error {
+ return c.Render("views/agents", fiber.Map{
+ "Agents": pool.List(),
+ })
+ })
+
+ webapp.Get("/create", func(c *fiber.Ctx) error {
+ return c.Render("views/create", fiber.Map{
+ "Title": "Hello, World!",
+ "Actions": AvailableActions,
+ "Connectors": AvailableConnectors,
+ })
+ })
+
+ webapp.Get("/knowledgebase", func(c *fiber.Ctx) error {
+ return c.Render("views/knowledgebase", fiber.Map{
+ "Title": "Hello, World!",
+ "KnowledgebaseItemsCount": len(db.Database),
+ })
+ })
+
+ // Define a route for the GET method on the root path '/'
+ webapp.Get("/sse/:name", func(c *fiber.Ctx) error {
+
+ m := pool.GetManager(c.Params("name"))
+ if m == nil {
+ return c.SendStatus(404)
+ }
+
+ m.Handle(c, NewClient(randStringRunes(10)))
+ return nil
+ })
+
+ 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.Post("/knowledgebase", app.KnowledgeBase(db))
+
+ webapp.Get("/talk/:name", func(c *fiber.Ctx) error {
+ return c.Render("chat.html", fiber.Map{
+ // "Character": agent.Character,
+ "Name": c.Params("name"),
+ })
+ })
+
+}
+
+var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+func randStringRunes(n int) string {
+ b := make([]rune, n)
+ for i := range b {
+ b[i] = letterRunes[rand.Intn(len(letterRunes))]
+ }
+ return string(b)
+}