mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-08-07 22:21:36 +00:00
Add provider-scoped runtime monitor for Codex pool owners:
- New `GET /v1/providers/{id}/codex-pool-activity` endpoint aggregates pool health across all agents
- New Pool Activity section on provider detail page (pool owners only)
- Shows aggregate member health, recent requests, top agents with drill-down links
- 7-day time window on provider-scoped span query for performance
- Reuses `buildCodexPoolActivity()` — zero duplicated aggregation logic
- i18n complete (en/vi/zh), accessible markup
Closes #499
130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package pg
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/lib/pq"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
)
|
|
|
|
const listCodexPoolSpansQuery = `
|
|
SELECT
|
|
sp.id,
|
|
sp.trace_id,
|
|
sp.start_time,
|
|
COALESCE(sp.duration_ms, 0),
|
|
sp.status,
|
|
COALESCE(sp.provider, ''),
|
|
COALESCE(sp.model, ''),
|
|
COALESCE(sp.metadata, '{}'::jsonb)
|
|
FROM spans sp
|
|
JOIN traces t ON t.id = sp.trace_id
|
|
WHERE t.agent_id = $1
|
|
AND t.tenant_id = $2
|
|
AND t.parent_trace_id IS NULL
|
|
AND sp.tenant_id = $2
|
|
AND sp.span_type = 'llm_call'
|
|
AND (
|
|
sp.provider = ANY($3)
|
|
OR COALESCE(sp.metadata->'chatgpt_oauth_routing'->>'selected_provider', '') = ANY($3)
|
|
OR COALESCE(sp.metadata->'chatgpt_oauth_routing'->>'serving_provider', '') = ANY($3)
|
|
)
|
|
ORDER BY sp.start_time DESC
|
|
LIMIT $4`
|
|
|
|
// ListCodexPoolSpans returns recent LLM call spans for agents using Codex OAuth pool providers.
|
|
func (s *PGTracingStore) ListCodexPoolSpans(ctx context.Context, agentID, tenantID uuid.UUID, poolProviders []string, limit int) ([]store.CodexPoolSpan, error) {
|
|
rows, err := s.db.QueryContext(ctx, listCodexPoolSpansQuery, agentID, tenantID, pq.Array(poolProviders), limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
spans := make([]store.CodexPoolSpan, 0, limit)
|
|
for rows.Next() {
|
|
var item store.CodexPoolSpan
|
|
var metadata json.RawMessage
|
|
if err := rows.Scan(
|
|
&item.SpanID,
|
|
&item.TraceID,
|
|
&item.StartedAt,
|
|
&item.DurationMS,
|
|
&item.Status,
|
|
&item.Provider,
|
|
&item.Model,
|
|
&metadata,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
item.Metadata = metadata
|
|
spans = append(spans, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return spans, nil
|
|
}
|
|
|
|
const listCodexPoolSpansByProvidersQuery = `
|
|
SELECT
|
|
sp.id,
|
|
sp.trace_id,
|
|
sp.start_time,
|
|
COALESCE(sp.duration_ms, 0),
|
|
sp.status,
|
|
COALESCE(sp.provider, ''),
|
|
COALESCE(sp.model, ''),
|
|
COALESCE(sp.metadata, '{}'::jsonb),
|
|
t.agent_id
|
|
FROM spans sp
|
|
JOIN traces t ON t.id = sp.trace_id
|
|
WHERE t.tenant_id = $1
|
|
AND t.parent_trace_id IS NULL
|
|
AND sp.tenant_id = $1
|
|
AND sp.span_type = 'llm_call'
|
|
AND sp.start_time > NOW() - INTERVAL '7 days'
|
|
AND (
|
|
sp.provider = ANY($2)
|
|
OR COALESCE(sp.metadata->'chatgpt_oauth_routing'->>'selected_provider', '') = ANY($2)
|
|
OR COALESCE(sp.metadata->'chatgpt_oauth_routing'->>'serving_provider', '') = ANY($2)
|
|
)
|
|
ORDER BY sp.start_time DESC
|
|
LIMIT $3`
|
|
|
|
// ListCodexPoolSpansByProviders returns recent LLM call spans across all agents for the given pool providers.
|
|
func (s *PGTracingStore) ListCodexPoolSpansByProviders(ctx context.Context, tenantID uuid.UUID, poolProviders []string, limit int) ([]store.CodexPoolProviderSpan, error) {
|
|
rows, err := s.db.QueryContext(ctx, listCodexPoolSpansByProvidersQuery, tenantID, pq.Array(poolProviders), limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
spans := make([]store.CodexPoolProviderSpan, 0, limit)
|
|
for rows.Next() {
|
|
var item store.CodexPoolProviderSpan
|
|
var metadata json.RawMessage
|
|
if err := rows.Scan(
|
|
&item.SpanID,
|
|
&item.TraceID,
|
|
&item.StartedAt,
|
|
&item.DurationMS,
|
|
&item.Status,
|
|
&item.Provider,
|
|
&item.Model,
|
|
&metadata,
|
|
&item.AgentID,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
item.Metadata = metadata
|
|
spans = append(spans, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return spans, nil
|
|
}
|