Docs menu

Guides

Observability

RateGuard emits OpenTelemetry spans and metrics for every LLM call using the official GenAI semantic conventions. Span names follow {operation} {model} (e.g. chat gpt-4o); standard gen_ai.* attributes are used where the convention defines them, and RateGuard-specific data lives under rateguard.* so the reserved namespace stays clean.

span shape
chat gpt-4o                       ← span name: {operation} {model}
├── gen_ai.provider.name:   "openai" | "anthropic" | "google"
├── gen_ai.request.model:   "gpt-4o" | "claude-opus-4-5" | ...
├── gen_ai.operation.name:  "chat" | "text_completion" | "embedding"
├── gen_ai.usage.input_tokens:  1234
├── gen_ai.usage.output_tokens: 567
└── gen_ai.conversation.id / gen_ai.response.id (when provided)

Track any call manually

Go
ctx, span := rg.StartGenAICall(ctx, rateguard.GenAICall{
    Provider: "openai", Model: "gpt-4o", Operation: "chat",
})
resp, err := client.Chat(ctx, req)
span.RecordChunk() // optional, per streaming chunk — first call sets TTFT
span.End(rateguard.GenAICall{
    PromptTokens:     usage.Input,
    CompletionTokens: usage.Output,
}, err)

Cost is estimated automatically from the pricing table (14 models verified against provider pricing pages) when not provided. TTFT and TPOT are derived from RecordChunk() timing. Node and Python expose the same attribute builders via genaiSpanName / genai_span_name and friends.

Tip

Point OTLPCollectorEndpoint at your collector and the spans land in Datadog, Grafana, or Honeycomb with zero extra glue — they follow the semconv your backend already understands.

Prometheus

Go
http.Handle("/metrics", rg.Metrics())
// live counters: requests, rate limits, budgets,
// breakers, loops, outbound calls + fallbacks + tokens

Events

Every request emits an event — request.completed, request.rate_limited, token_budget.exceeded — for custom dashboards, alerts, or audit logs:

Go
// Send to an HTTP endpoint
rg := rateguard.New(rateguard.Config{
    EventEndpoint: "https://my-dashboard.example/api/events",
})

// Or handle in-process
rg = rateguard.New(rateguard.Config{
    EventEmitter: myCustomEmitter{},
})