Add status to show the reasoning log
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mudler/local-agent-framework/example/webui/connector"
|
||||
@@ -44,15 +45,34 @@ type AgentConfig struct {
|
||||
}
|
||||
|
||||
type AgentPool struct {
|
||||
sync.Mutex
|
||||
file string
|
||||
pooldir string
|
||||
pool AgentPoolData
|
||||
agents map[string]*Agent
|
||||
managers map[string]Manager
|
||||
agentStatus map[string]*Status
|
||||
apiURL, model string
|
||||
ragDB RAGDB
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
results []ActionState
|
||||
}
|
||||
|
||||
func (s *Status) addResult(result ActionState) {
|
||||
// If we have more than 10 results, remove the oldest one
|
||||
if len(s.results) > 10 {
|
||||
s.results = s.results[1:]
|
||||
}
|
||||
|
||||
s.results = append(s.results, result)
|
||||
}
|
||||
|
||||
func (s *Status) Results() []ActionState {
|
||||
return s.results
|
||||
}
|
||||
|
||||
type AgentPoolData map[string]AgentConfig
|
||||
|
||||
func loadPoolFromFile(path string) (*AgentPoolData, error) {
|
||||
@@ -82,6 +102,7 @@ func NewAgentPool(model, apiURL, directory string, RagDB RAGDB) (*AgentPool, err
|
||||
ragDB: RagDB,
|
||||
agents: make(map[string]*Agent),
|
||||
pool: make(map[string]AgentConfig),
|
||||
agentStatus: make(map[string]*Status),
|
||||
managers: make(map[string]Manager),
|
||||
}, nil
|
||||
}
|
||||
@@ -98,6 +119,7 @@ func NewAgentPool(model, apiURL, directory string, RagDB RAGDB) (*AgentPool, err
|
||||
model: model,
|
||||
agents: make(map[string]*Agent),
|
||||
managers: make(map[string]Manager),
|
||||
agentStatus: map[string]*Status{},
|
||||
pool: *poolData,
|
||||
}, nil
|
||||
}
|
||||
@@ -224,6 +246,12 @@ func (a *AgentConfig) availableConnectors() []Connector {
|
||||
return connectors
|
||||
}
|
||||
|
||||
func (a *AgentPool) GetStatusHistory(name string) *Status {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.agentStatus[name]
|
||||
}
|
||||
|
||||
func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error {
|
||||
manager := NewManager(5)
|
||||
ctx := context.Background()
|
||||
@@ -275,6 +303,13 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
||||
}),
|
||||
WithSystemPrompt(config.SystemPrompt),
|
||||
WithAgentResultCallback(func(state ActionState) {
|
||||
a.Lock()
|
||||
if _, ok := a.agentStatus[name]; !ok {
|
||||
a.agentStatus[name] = &Status{}
|
||||
}
|
||||
|
||||
a.agentStatus[name].addResult(state)
|
||||
a.Unlock()
|
||||
fmt.Println("Reasoning", state.Reasoning)
|
||||
|
||||
text := fmt.Sprintf(`Reasoning: %s
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
fiber "github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
||||
"github.com/mudler/local-agent-framework/agent"
|
||||
)
|
||||
|
||||
func RegisterRoutes(webapp *fiber.App, pool *AgentPool, db *InMemoryDatabase, app *App) {
|
||||
@@ -54,6 +55,19 @@ func RegisterRoutes(webapp *fiber.App, pool *AgentPool, db *InMemoryDatabase, ap
|
||||
return nil
|
||||
})
|
||||
|
||||
webapp.Get("/status/:name", func(c *fiber.Ctx) error {
|
||||
history := pool.GetStatusHistory(c.Params("name"))
|
||||
if history == nil {
|
||||
history = &Status{results: []agent.ActionState{}}
|
||||
}
|
||||
// reverse history
|
||||
|
||||
return c.Render("views/status", fiber.Map{
|
||||
"Name": c.Params("name"),
|
||||
"History": Reverse(history.Results()),
|
||||
})
|
||||
})
|
||||
|
||||
webapp.Get("/notify/:name", app.Notify(pool))
|
||||
webapp.Post("/chat/:name", app.Chat(pool))
|
||||
webapp.Post("/create", app.Create(pool))
|
||||
@@ -81,3 +95,15 @@ func randStringRunes(n int) string {
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func Reverse[T any](original []T) (reversed []T) {
|
||||
reversed = make([]T, len(original))
|
||||
copy(reversed, original)
|
||||
|
||||
for i := len(reversed)/2 - 1; i >= 0; i-- {
|
||||
tmp := len(reversed) - 1 - i
|
||||
reversed[i], reversed[tmp] = reversed[tmp], reversed[i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,9 +48,13 @@
|
||||
<tbody class="bg-gray-800 divide-y divide-gray-700">
|
||||
<!-- Dynamic agent rows go here -->
|
||||
{{ range .Agents }}
|
||||
<tr hx-ext="sse" sse-connect="/sse/{{.}}">
|
||||
<tr hx-ext="sse">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-300">{{.}}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300"><div sse-swap="status" ></div></td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
||||
<a href="/status/{{.}}" class="text-indigo-500 hover:text-indigo-400">
|
||||
<i class="fas fa-info"></i> Status
|
||||
</a>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
|
||||
<a href="/talk/{{.}}" class="text-indigo-500 hover:text-indigo-400">
|
||||
<i class="fas fa-comments"></i> Talk
|
||||
|
||||
61
example/webui/views/status.html
Normal file
61
example/webui/views/status.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Smart Agent status</title>
|
||||
{{template "views/partials/header"}}
|
||||
<style>
|
||||
body { overflow: hidden; }
|
||||
.chat-container { height: 90vh; display: flex; flex-direction: column; }
|
||||
.chat-messages { overflow-y: auto; flex-grow: 1; }
|
||||
.htmx-indicator{
|
||||
opacity:0;
|
||||
transition: opacity 10ms ease-in;
|
||||
}
|
||||
.htmx-request .htmx-indicator{
|
||||
opacity:1
|
||||
}
|
||||
/* Loader (https://cssloaders.github.io/) */
|
||||
.loader {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin:15px auto;
|
||||
position: relative;
|
||||
color: #FFF;
|
||||
box-sizing: border-box;
|
||||
animation: animloader 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes animloader {
|
||||
0% { box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 -2px; }
|
||||
25% { box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 2px; }
|
||||
50% { box-shadow: 14px 0 0 -2px, 38px 0 0 -2px, -14px 0 0 2px, -38px 0 0 -2px; }
|
||||
75% { box-shadow: 14px 0 0 2px, 38px 0 0 -2px, -14px 0 0 -2px, -38px 0 0 -2px; }
|
||||
100% { box-shadow: 14px 0 0 -2px, 38px 0 0 2px, -14px 0 0 -2px, -38px 0 0 -2px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-900 p-4 text-white font-sans" hx-ext="sse" sse-connect="/sse/{{.Name}}">
|
||||
{{template "views/partials/menu"}}
|
||||
<div class="chat-container bg-gray-800 shadow-lg rounded-lg" >
|
||||
<!-- Chat Header -->
|
||||
<div class="border-b border-gray-700 p-4">
|
||||
<h1 class="text-lg font-semibold">{{.Name}}</h1>
|
||||
</div>
|
||||
<!-- Chat Messages -->
|
||||
<div class="chat-messages p-4">
|
||||
<div sse-swap="status" hx-swap="afterbegin" id="status"></div>
|
||||
{{ range .History }}
|
||||
<!-- Agent Status Box -->
|
||||
<div class="bg-gray-700 p-4">
|
||||
<h2 class="text-sm font-semibold">Agent:</h2>
|
||||
<div id="agentStatus" class="text-sm text-gray-300">
|
||||
Result: {{.Result}} Action: {{.Action}} Params: {{.Params}} Reasoning: {{.Reasoning}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user