feat: add AI APM module for AI/LLM call telemetry
- internal/aiapm/types.go: AICallRecord, filter, summary, and stats types - internal/aiapm/pricing.go: vendor pricing tables (Anthropic, OpenAI, Google, Mistral, DeepSeek, Groq) - internal/aiapm/store.go: PostgreSQL storage with batch insert, filtered queries, aggregations, timeseries - internal/aiapm/collector.go: async collector with buffered channel and background batch writer - internal/api/aiapm_handlers.go: Fiber route handlers for ingest, summary, models, vendors, costs, calls, pricing - cmd/server/main.go: register AI APM routes and create ai_calls table at startup
This commit is contained in:
102
internal/aiapm/types.go
Normal file
102
internal/aiapm/types.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package aiapm
|
||||
|
||||
import "time"
|
||||
|
||||
// AICallRecord represents a single AI/LLM API call
|
||||
type AICallRecord struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ServiceName string `json:"service_name"`
|
||||
ProjectID string `json:"project_id"`
|
||||
Vendor string `json:"vendor"`
|
||||
Model string `json:"model"`
|
||||
TokensIn int `json:"tokens_in"`
|
||||
TokensOut int `json:"tokens_out"`
|
||||
TokensCacheRead int `json:"tokens_cache_read"`
|
||||
TokensCacheWrite int `json:"tokens_cache_write"`
|
||||
EstimatedCost float64 `json:"estimated_cost"`
|
||||
LatencyMs int `json:"latency_ms"`
|
||||
TTFBMs int `json:"ttfb_ms"`
|
||||
Status string `json:"status"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Stream bool `json:"stream"`
|
||||
Cached bool `json:"cached"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// AICallFilter defines query filters for AI call records
|
||||
type AICallFilter struct {
|
||||
ServiceName string `json:"service_name"`
|
||||
ProjectID string `json:"project_id"`
|
||||
Vendor string `json:"vendor"`
|
||||
Model string `json:"model"`
|
||||
Status string `json:"status"`
|
||||
From time.Time `json:"from"`
|
||||
To time.Time `json:"to"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
}
|
||||
|
||||
// AIUsageSummary aggregated usage statistics
|
||||
type AIUsageSummary struct {
|
||||
TotalCalls int `json:"total_calls"`
|
||||
TotalTokensIn int64 `json:"total_tokens_in"`
|
||||
TotalTokensOut int64 `json:"total_tokens_out"`
|
||||
TotalCacheRead int64 `json:"total_cache_read"`
|
||||
TotalCacheWrite int64 `json:"total_cache_write"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
||||
AvgTTFBMs float64 `json:"avg_ttfb_ms"`
|
||||
ErrorCount int `json:"error_count"`
|
||||
ErrorRate float64 `json:"error_rate"`
|
||||
CacheHitRate float64 `json:"cache_hit_rate"`
|
||||
UniqueModels int `json:"unique_models"`
|
||||
UniqueVendors int `json:"unique_vendors"`
|
||||
UniqueServices int `json:"unique_services"`
|
||||
}
|
||||
|
||||
// AIModelStats per-model breakdown
|
||||
type AIModelStats struct {
|
||||
Vendor string `json:"vendor"`
|
||||
Model string `json:"model"`
|
||||
TotalCalls int `json:"total_calls"`
|
||||
TotalTokens int64 `json:"total_tokens"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
||||
ErrorCount int `json:"error_count"`
|
||||
ErrorRate float64 `json:"error_rate"`
|
||||
}
|
||||
|
||||
// AIVendorStats per-vendor breakdown
|
||||
type AIVendorStats struct {
|
||||
Vendor string `json:"vendor"`
|
||||
TotalCalls int `json:"total_calls"`
|
||||
TotalTokens int64 `json:"total_tokens"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
AvgLatencyMs float64 `json:"avg_latency_ms"`
|
||||
ModelCount int `json:"model_count"`
|
||||
ErrorCount int `json:"error_count"`
|
||||
ErrorRate float64 `json:"error_rate"`
|
||||
}
|
||||
|
||||
// AICostBreakdown cost breakdown by dimension
|
||||
type AICostBreakdown struct {
|
||||
Dimension string `json:"dimension"` // vendor, model, service, project
|
||||
Key string `json:"key"`
|
||||
Cost float64 `json:"cost"`
|
||||
Calls int `json:"calls"`
|
||||
Tokens int64 `json:"tokens"`
|
||||
}
|
||||
|
||||
// TimeseriesPoint a single point in a time series
|
||||
type TimeseriesPoint struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Value float64 `json:"value"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// IngestRequest payload for the ingest endpoint
|
||||
type IngestRequest struct {
|
||||
Call *AICallRecord `json:"call,omitempty"`
|
||||
Calls []AICallRecord `json:"calls,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user