feat: complete agent monitoring - hook, UI, and backend filter
- Add event_type and framework filters to events query endpoint - Add /agents SPA route to web-ui server - Add Agents nav link and route in frontend - Add agents page CSS (timeline, VM pills, stats panel) - Build VM status strip, activity timeline, and real-time stats - Add agentmon hook for OpenClaw (HOOK.md + handler.ts) - Add docker-compose, Dockerfile, and supporting infra files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
# Agentmon SDK
|
||||
|
||||
The agentmon SDK provides a Go client for sending telemetry events to the agentmon backend.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get agentmon/internal/sdk
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"agentmon/internal/sdk"
|
||||
)
|
||||
|
||||
func main() {
|
||||
emitter, err := sdk.NewEmitter(sdk.Config{
|
||||
ServerURL: "http://localhost:8080",
|
||||
Framework: "my-agent",
|
||||
ClientID: "my-client-001",
|
||||
Host: "localhost",
|
||||
BufferSize: 100,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer emitter.Close(context.Background())
|
||||
|
||||
ctx := context.Background()
|
||||
sessionID := "session-123"
|
||||
|
||||
// Start a session
|
||||
sessionStart := sdk.NewSessionStart(sessionID, sdk.WithSource(emitter))
|
||||
if err := emitter.Emit(ctx, sessionStart); err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
}
|
||||
|
||||
// ... do work ...
|
||||
|
||||
// End the session
|
||||
sessionEnd := sdk.NewSessionEnd(sessionID, sdk.WithSource(emitter))
|
||||
if err := emitter.Emit(ctx, sessionEnd); err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
}
|
||||
|
||||
// Flush buffered events
|
||||
if err := emitter.Flush(ctx); err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The `Config` struct configures the emitter:
|
||||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `ServerURL` | string | Yes | - | URL of the ingest gateway (e.g., `http://localhost:8080`) |
|
||||
| `APIKey` | string | No | - | Optional API key for authentication |
|
||||
| `Framework` | string | Yes | - | Name of the framework (e.g., `opencode`, `claude-code`) |
|
||||
| `ClientID` | string | Yes | - | Stable identifier for this emitter instance |
|
||||
| `Host` | string | No | `localhost` | Hostname where events originate |
|
||||
| `BufferSize` | int | No | `100` | Max number of events to buffer before flushing |
|
||||
| `UseWebSocket` | bool | No | `false` | Enable WebSocket streaming mode |
|
||||
| `EnableLogging` | bool | No | `false` | Enable debug logging |
|
||||
|
||||
## Event Types
|
||||
|
||||
### Session Events
|
||||
|
||||
```go
|
||||
// Start a session
|
||||
sessionStart := sdk.NewSessionStart(sessionID,
|
||||
sdk.WithSource(emitter),
|
||||
sdk.WithAttributes(map[string]any{
|
||||
"cwd": "/home/user/project",
|
||||
"repo": "myrepo",
|
||||
"branch": "main",
|
||||
}),
|
||||
)
|
||||
|
||||
// End a session
|
||||
sessionEnd := sdk.NewSessionEnd(sessionID,
|
||||
sdk.WithSource(emitter),
|
||||
)
|
||||
```
|
||||
|
||||
### Run Events
|
||||
|
||||
```go
|
||||
// Start a run
|
||||
runStart := sdk.NewRunStart(sessionID, runID,
|
||||
sdk.WithSource(emitter),
|
||||
sdk.WithAttributes(map[string]any{
|
||||
"command": "my-command",
|
||||
"agent": "my-agent",
|
||||
}),
|
||||
)
|
||||
|
||||
// End a run
|
||||
runEnd := sdk.NewRunEnd(sessionID, runID, "success", 60000,
|
||||
sdk.WithSource(emitter),
|
||||
sdk.WithLLMUsage("claude-3-opus", 1000, 500, 0.015),
|
||||
)
|
||||
```
|
||||
|
||||
### Span Events
|
||||
|
||||
```go
|
||||
// Start a span
|
||||
spanStart := sdk.NewSpanStart(sessionID, runID, traceID, spanID,
|
||||
sdk.WithSource(emitter),
|
||||
sdk.WithSpanKind("tool"),
|
||||
sdk.WithName("Bash"),
|
||||
sdk.WithAttributes(map[string]any{
|
||||
"command": "echo hello",
|
||||
}),
|
||||
)
|
||||
|
||||
// End a span
|
||||
spanEnd := sdk.NewSpanEnd(sessionID, runID, traceID, spanID, "success", 1000,
|
||||
sdk.WithSource(emitter),
|
||||
sdk.WithSpanKind("tool"),
|
||||
sdk.WithName("Bash"),
|
||||
)
|
||||
```
|
||||
|
||||
### Error Events
|
||||
|
||||
```go
|
||||
errEvent := sdk.NewError(sessionID, runID, traceID, spanID,
|
||||
"validation", "invalid input",
|
||||
sdk.WithSource(emitter),
|
||||
sdk.WithErrorDetails("VAL001", false),
|
||||
)
|
||||
```
|
||||
|
||||
### Metric Snapshots
|
||||
|
||||
```go
|
||||
metrics := sdk.NewMetricSnapshot(sessionID, runID, map[string]any{
|
||||
"tokens_in": 1000.0,
|
||||
"tokens_out": 500.0,
|
||||
"cost_usd": 0.015,
|
||||
"latency_ms": 300.0,
|
||||
"error_count": 0,
|
||||
})
|
||||
```
|
||||
|
||||
## Event Options
|
||||
|
||||
Event options are functions that modify events before sending:
|
||||
|
||||
- `WithSource(emitter)` - Add source information (framework, client_id, host)
|
||||
- `WithAttributes(attrs)` - Add arbitrary attributes
|
||||
- `WithSpanKind(kind)` - Set the span_kind attribute (`llm`, `tool`, `skill`, `internal`)
|
||||
- `WithName(name)` - Set the name attribute
|
||||
- `WithParentSpanID(parentID)` - Set the parent span ID
|
||||
- `WithPayload(payload)` - Set custom payload
|
||||
- `WithSeq(seq)` - Set sequence number (for WebSocket mode)
|
||||
- `WithLLMUsage(model, inTokens, outTokens, costUSD)` - Add LLM usage to run.end or span.end
|
||||
- `WithErrorDetails(code, retryable)` - Add error details
|
||||
|
||||
## Span Kinds
|
||||
|
||||
Common span kinds:
|
||||
|
||||
- `llm` - LLM API calls
|
||||
- `tool` - Tool/function calls
|
||||
- `skill` - Skill execution
|
||||
- `internal` - Internal operations
|
||||
|
||||
## WebSocket Mode
|
||||
|
||||
For real-time streaming, enable WebSocket mode:
|
||||
|
||||
```go
|
||||
emitter, err := sdk.NewEmitter(sdk.Config{
|
||||
ServerURL: "http://localhost:8080",
|
||||
Framework: "my-agent",
|
||||
ClientID: "my-client-001",
|
||||
Host: "localhost",
|
||||
UseWebSocket: true,
|
||||
})
|
||||
```
|
||||
|
||||
In WebSocket mode, events are sent immediately rather than buffered.
|
||||
|
||||
## Example
|
||||
|
||||
See `examples/sdk-example/main.go` for a complete example.
|
||||
|
||||
## Testing
|
||||
|
||||
Run tests with:
|
||||
|
||||
```bash
|
||||
go test ./internal/sdk/...
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Same license as the agentmon project.
|
||||
Reference in New Issue
Block a user