Add status to show the reasoning log
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mudler/local-agent-framework/example/webui/connector"
|
"github.com/mudler/local-agent-framework/example/webui/connector"
|
||||||
@@ -44,15 +45,34 @@ type AgentConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AgentPool struct {
|
type AgentPool struct {
|
||||||
|
sync.Mutex
|
||||||
file string
|
file string
|
||||||
pooldir string
|
pooldir string
|
||||||
pool AgentPoolData
|
pool AgentPoolData
|
||||||
agents map[string]*Agent
|
agents map[string]*Agent
|
||||||
managers map[string]Manager
|
managers map[string]Manager
|
||||||
|
agentStatus map[string]*Status
|
||||||
apiURL, model string
|
apiURL, model string
|
||||||
ragDB RAGDB
|
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
|
type AgentPoolData map[string]AgentConfig
|
||||||
|
|
||||||
func loadPoolFromFile(path string) (*AgentPoolData, error) {
|
func loadPoolFromFile(path string) (*AgentPoolData, error) {
|
||||||
@@ -75,14 +95,15 @@ func NewAgentPool(model, apiURL, directory string, RagDB RAGDB) (*AgentPool, err
|
|||||||
if _, err := os.Stat(poolfile); err != nil {
|
if _, err := os.Stat(poolfile); err != nil {
|
||||||
// file does not exist, create a new pool
|
// file does not exist, create a new pool
|
||||||
return &AgentPool{
|
return &AgentPool{
|
||||||
file: poolfile,
|
file: poolfile,
|
||||||
pooldir: directory,
|
pooldir: directory,
|
||||||
apiURL: apiURL,
|
apiURL: apiURL,
|
||||||
model: model,
|
model: model,
|
||||||
ragDB: RagDB,
|
ragDB: RagDB,
|
||||||
agents: make(map[string]*Agent),
|
agents: make(map[string]*Agent),
|
||||||
pool: make(map[string]AgentConfig),
|
pool: make(map[string]AgentConfig),
|
||||||
managers: make(map[string]Manager),
|
agentStatus: make(map[string]*Status),
|
||||||
|
managers: make(map[string]Manager),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,14 +112,15 @@ func NewAgentPool(model, apiURL, directory string, RagDB RAGDB) (*AgentPool, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &AgentPool{
|
return &AgentPool{
|
||||||
file: poolfile,
|
file: poolfile,
|
||||||
apiURL: apiURL,
|
apiURL: apiURL,
|
||||||
pooldir: directory,
|
pooldir: directory,
|
||||||
ragDB: RagDB,
|
ragDB: RagDB,
|
||||||
model: model,
|
model: model,
|
||||||
agents: make(map[string]*Agent),
|
agents: make(map[string]*Agent),
|
||||||
managers: make(map[string]Manager),
|
managers: make(map[string]Manager),
|
||||||
pool: *poolData,
|
agentStatus: map[string]*Status{},
|
||||||
|
pool: *poolData,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +246,12 @@ func (a *AgentConfig) availableConnectors() []Connector {
|
|||||||
return connectors
|
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 {
|
func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error {
|
||||||
manager := NewManager(5)
|
manager := NewManager(5)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -275,6 +303,13 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
|||||||
}),
|
}),
|
||||||
WithSystemPrompt(config.SystemPrompt),
|
WithSystemPrompt(config.SystemPrompt),
|
||||||
WithAgentResultCallback(func(state ActionState) {
|
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)
|
fmt.Println("Reasoning", state.Reasoning)
|
||||||
|
|
||||||
text := fmt.Sprintf(`Reasoning: %s
|
text := fmt.Sprintf(`Reasoning: %s
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
fiber "github.com/gofiber/fiber/v2"
|
fiber "github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
"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) {
|
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
|
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.Get("/notify/:name", app.Notify(pool))
|
||||||
webapp.Post("/chat/:name", app.Chat(pool))
|
webapp.Post("/chat/:name", app.Chat(pool))
|
||||||
webapp.Post("/create", app.Create(pool))
|
webapp.Post("/create", app.Create(pool))
|
||||||
@@ -81,3 +95,15 @@ func randStringRunes(n int) string {
|
|||||||
}
|
}
|
||||||
return string(b)
|
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">
|
<tbody class="bg-gray-800 divide-y divide-gray-700">
|
||||||
<!-- Dynamic agent rows go here -->
|
<!-- Dynamic agent rows go here -->
|
||||||
{{ range .Agents }}
|
{{ 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 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">
|
<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">
|
<a href="/talk/{{.}}" class="text-indigo-500 hover:text-indigo-400">
|
||||||
<i class="fas fa-comments"></i> Talk
|
<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