feat(usage): add event analytics dashboard

This commit is contained in:
Goon committed 2026-06-12 17:51:12 +07:00
1 parent b4c63e4afb
commit 4002bb4c13
31 files changed
+2316 -24

No files matched your search

+24 -6
View File
@@ -21,7 +21,7 @@ func (l *Loop) makeExecuteToolCall(req *RunRequest, bridgeRS *runState) func(ctx
emitRun := makeToolEmitRun(l, req)
return func(ctx context.Context, state *pipeline.RunState, tc providers.ToolCall) ([]providers.Message, error) {
tc = l.normalizeToolCall(tc)
registryName := l.resolveToolCallName(tc.Name)
registryName := l.canonicalToolName(l.resolveToolCallName(tc.Name))
argsJSON, _ := json.Marshal(tc.Arguments)
slog.Info("tool call", "agent", l.id, "tool", tc.Name, "args_len", len(argsJSON))
@@ -34,7 +34,7 @@ func (l *Loop) makeExecuteToolCall(req *RunRequest, bridgeRS *runState) func(ctx
// Emit tool span start for tracing.
toolStart := time.Now().UTC()
toolSpanID := l.emitToolSpanStart(ctx, toolStart, tc.Name, tc.ID, string(argsJSON))
toolSpanID := l.emitToolSpanStart(ctx, toolStart, registryName, tc.ID, string(argsJSON))
// Inject agent audio snapshot so TTS tool (and any future audio consumers)
// can read agent-level voice/model config without an extra DB lookup.
@@ -55,6 +55,7 @@ func (l *Loop) makeExecuteToolCall(req *RunRequest, bridgeRS *runState) func(ctx
toolDuration := time.Since(toolStart)
l.emitToolSpanEnd(ctx, toolSpanID, toolStart, result)
l.recordToolUsageEvent(ctx, req, registryName, tc.Name, tc.ID, tc.Arguments, toolStart, result, toolSpanID)
// v3 evolution metrics: record tool execution non-blocking (best-effort).
l.recordToolMetric(ctx, req.SessionKey, registryName, !result.IsError, toolDuration)
@@ -73,6 +74,10 @@ func (l *Loop) makeExecuteToolCall(req *RunRequest, bridgeRS *runState) func(ctx
type toolRawResult struct {
result *tools.Result
duration time.Duration
start time.Time
spanID uuid.UUID
toolName string
rawName string
}
// makeExecuteToolRaw wraps tool I/O only (parallel-safe, no state mutation).
@@ -81,7 +86,7 @@ func (l *Loop) makeExecuteToolRaw(req *RunRequest) func(ctx context.Context, tc
emitRun := makeToolEmitRun(l, req)
return func(ctx context.Context, tc providers.ToolCall) (providers.Message, any, error) {
tc = l.normalizeToolCall(tc)
registryName := l.resolveToolCallName(tc.Name)
registryName := l.canonicalToolName(l.resolveToolCallName(tc.Name))
argsJSON, _ := json.Marshal(tc.Arguments)
slog.Info("tool call", "agent", l.id, "tool", tc.Name, "args_len", len(argsJSON))
@@ -98,7 +103,7 @@ func (l *Loop) makeExecuteToolRaw(req *RunRequest) func(ctx context.Context, tc
// Emit tool span start (goroutine-safe: channel send only).
start := time.Now().UTC()
spanID := l.emitToolSpanStart(ctx, start, tc.Name, tc.ID, string(argsJSON))
spanID := l.emitToolSpanStart(ctx, start, registryName, tc.ID, string(argsJSON))
// Inject agent audio snapshot (parallel path — same as sequential makeExecuteToolCall).
if l.agentUUID != uuid.Nil {
@@ -125,7 +130,7 @@ func (l *Loop) makeExecuteToolRaw(req *RunRequest) func(ctx context.Context, tc
ToolCallID: tc.ID,
IsError: result.IsError,
}
return msg, &toolRawResult{result: result, duration: dur}, nil
return msg, &toolRawResult{result: result, duration: dur, start: start, spanID: spanID, toolName: registryName, rawName: tc.Name}, nil
}
}
@@ -151,20 +156,33 @@ func (l *Loop) makeProcessToolResult(req *RunRequest, bridgeRS *runState) func(c
emitRun := makeToolEmitRun(l, req)
return func(ctx context.Context, state *pipeline.RunState, tc providers.ToolCall, rawMsg providers.Message, rawData any) []providers.Message {
tc = l.normalizeToolCall(tc)
registryName := l.resolveToolCallName(tc.Name)
registryName := l.canonicalToolName(l.resolveToolCallName(tc.Name))
// Extract result and timing from toolRawResult wrapper.
var result *tools.Result
var dur time.Duration
var start time.Time
var spanID uuid.UUID
var rawName string
if raw, ok := rawData.(*toolRawResult); ok && raw != nil {
result = raw.result
dur = raw.duration
start = raw.start
spanID = raw.spanID
registryName = raw.toolName
rawName = raw.rawName
} else if r, ok := rawData.(*tools.Result); ok {
result = r // backward compat
}
if result == nil {
return []providers.Message{rawMsg}
}
if rawName == "" {
rawName = tc.Name
}
if !start.IsZero() {
l.recordToolUsageEvent(ctx, req, registryName, rawName, tc.ID, tc.Arguments, start, result, spanID)
}
// Record tool metrics (non-blocking, best-effort).
l.recordToolMetric(ctx, req.SessionKey, registryName, !result.IsError, dur)