Add "clear history" option to the agent observables (#312)

This commit is contained in:
Michel Weimerskirch
2025-10-03 23:06:21 +02:00
committed by GitHub
parent b06522d1ec
commit 500862b768
3 changed files with 57 additions and 5 deletions

View File

@@ -14,6 +14,7 @@ type Observer interface {
NewObservable() *types.Observable
Update(types.Observable)
History() []types.Observable
ClearHistory()
}
type SSEObserver struct {
@@ -21,8 +22,8 @@ type SSEObserver struct {
maxID int32
manager sse.Manager
mutex sync.Mutex
history []types.Observable
mutex sync.Mutex
history []types.Observable
historyLast int
}
@@ -36,8 +37,8 @@ func NewSSEObserver(agent string, manager sse.Manager) *SSEObserver {
}
func (s *SSEObserver) NewObservable() *types.Observable {
id := atomic.AddInt32(&s.maxID, 1)
id := atomic.AddInt32(&s.maxID, 1)
return &types.Observable{
ID: id - 1,
Agent: s.agent,
@@ -86,3 +87,11 @@ func (s *SSEObserver) History() []types.Observable {
return h
}
func (s *SSEObserver) ClearHistory() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.history = make([]types.Observable, 100)
s.historyLast = 0
}

View File

@@ -216,6 +216,7 @@ function AgentStatus() {
const [observableMap, setObservableMap] = useState({});
const [observableTree, setObservableTree] = useState([]);
const [expandedCards, setExpandedCards] = useState(new Map());
const [clearLoading, setClearLoading] = useState(false);
// Update document title
useEffect(() => {
@@ -342,6 +343,26 @@ function AgentStatus() {
};
}, [name]);
const handleClearObservables = async () => {
if (clearLoading) return;
setClearLoading(true);
try {
const resp = await fetch(`/api/agent/${name}/observables`, { method: 'DELETE' });
if (!resp.ok) {
console.error('Failed to clear observables, status:', resp.status);
} else {
// Clear local state immediately
setObservableMap({});
setObservableTree([]);
setExpandedCards(new Map());
}
} catch (e) {
console.error('Error clearing observables:', e);
} finally {
setClearLoading(false);
}
};
// Helper function to safely convert any value to a displayable string
const formatValue = (value) => {
if (value === null || value === undefined) {
@@ -435,7 +456,17 @@ function AgentStatus() {
</div>
{observableTree.length > 0 && (
<div>
<h2>Observable Updates</h2>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h2 style={{ margin: 0 }}>Observable Updates</h2>
<button
className="action-btn delete-btn"
onClick={handleClearObservables}
disabled={clearLoading}
title="Clear observable history"
>
{clearLoading ? 'Clearing' : 'Clear history'}
</button>
</div>
<div style={{ color: '#aaa', fontSize: 14, margin: '5px 0 10px 2px' }}>
Drill down into what the agent is doing and thinking when activated by a connector
</div>

View File

@@ -258,6 +258,7 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
})
})
// API endpoint to retrieve agent observables
webapp.Get("/api/agent/:name/observables", func(c *fiber.Ctx) error {
name := c.Params("name")
agent := pool.GetAgent(name)
@@ -273,6 +274,17 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
})
})
// API endpoint to clear agent observables
webapp.Delete("/api/agent/:name/observables", func(c *fiber.Ctx) error {
name := c.Params("name")
agent := pool.GetAgent(name)
if agent == nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Agent not found"})
}
agent.Observer().ClearHistory()
return c.JSON(fiber.Map{"Name": name, "cleared": true})
})
webapp.Post("/settings/import", app.ImportAgent(pool))
webapp.Get("/settings/export/:name", app.ExportAgent(pool))