package main import ( "fmt" "html/template" "log" "math/rand" "net/http" "os" "strings" "time" "github.com/donseba/go-htmx" "github.com/donseba/go-htmx/sse" external "github.com/mudler/local-agent-framework/external" . "github.com/mudler/local-agent-framework/agent" ) type ( App struct { htmx *htmx.HTMX } ) var ( sseManager sse.Manager ) var testModel = os.Getenv("TEST_MODEL") var apiModel = os.Getenv("API_MODEL") func init() { if testModel == "" { testModel = "hermes-2-pro-mistral" } if apiModel == "" { apiModel = "http://192.168.68.113:8080" } } func htmlIfy(s string) string { s = strings.TrimSpace(s) s = strings.ReplaceAll(s, "\n", "
") return s } var agentInstance *Agent func main() { app := &App{ htmx: htmx.New(), } agent, err := New( WithLLMAPIURL(apiModel), WithModel(testModel), EnableHUD, DebugMode, EnableStandaloneJob, WithAgentReasoningCallback(func(state ActionCurrentState) bool { sseManager.Send( sse.NewMessage( fmt.Sprintf(`Thinking: %s`, htmlIfy(state.Reasoning)), ).WithEvent("status"), ) return true }), WithActions(external.NewSearch(3)), WithAgentResultCallback(func(state ActionState) { text := fmt.Sprintf(`Reasoning: %s Action taken: %+v Result: %s`, state.Reasoning, state.ActionCurrentState.Action.Definition().Name, state.Result) sseManager.Send( sse.NewMessage( htmlIfy( text, ), ).WithEvent("status"), ) }), WithRandomIdentity(), WithPeriodicRuns("10m"), //WithPermanentGoal("get the weather of all the cities in italy and store the results"), ) if err != nil { panic(err) } go agent.Run() defer agent.Stop() agentInstance = agent sseManager = sse.NewManager(5) go func() { for { clientsStr := "" clients := sseManager.Clients() for _, c := range clients { clientsStr += c + ", " } time.Sleep(1 * time.Second) // Send a message every seconds sseManager.Send(sse.NewMessage(fmt.Sprintf("connected clients: %v", clientsStr)).WithEvent("clients")) } }() go func() { for { time.Sleep(1 * time.Second) // Send a message every seconds sseManager.Send(sse.NewMessage( htmlIfy(agent.State().String()), ).WithEvent("hud")) } }() mux := http.NewServeMux() mux.Handle("GET /", http.HandlerFunc(app.Home)) // External notifications (e.g. webhook) mux.Handle("POST /notify", http.HandlerFunc(app.Notify)) // User chat mux.Handle("POST /chat", http.HandlerFunc(app.Chat(sseManager))) // Server Sent Events mux.Handle("GET /sse", http.HandlerFunc(app.SSE)) fmt.Print("Server started at http://localhost:3210") err = http.ListenAndServe(":3210", mux) log.Fatal(err) } func (a *App) Home(w http.ResponseWriter, r *http.Request) { tmpl, err := template.ParseFiles("index.html") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } tmpl.Execute(w, nil) } func (a *App) SSE(w http.ResponseWriter, r *http.Request) { cl := sse.NewClient(randStringRunes(10)) sseManager.Handle(w, r, cl) } func (a *App) Notify(w http.ResponseWriter, r *http.Request) { query := strings.ToLower(r.PostFormValue("message")) if query == "" { _, _ = w.Write([]byte("Please enter a message.")) return } agentInstance.Ask( WithText(query), ) _, _ = w.Write([]byte("Message sent")) } func (a *App) Chat(m sse.Manager) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { query := strings.ToLower(r.PostFormValue("message")) if query == "" { _, _ = w.Write([]byte("Please enter a message.")) return } m.Send( sse.NewMessage( chatDiv(query, "blue"), ).WithEvent("messages")) go func() { res := agentInstance.Ask( WithText(query), ) m.Send( sse.NewMessage( chatDiv(res.Response, "red"), ).WithEvent("messages")) }() result := `message received` _, _ = w.Write([]byte(result)) } } 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) }