Documentation
¶
Index ¶
- Variables
- func GetAppActivity(db *gorm.DB, appIDs []uint, since time.Time) (map[uint]*AppActivity, error)
- func GetAppSpending(db *gorm.DB, windows []AppSpendWindow, end time.Time) (map[uint]float64, error)
- func GetBudgetUsage(db *gorm.DB, startDate, endDate *time.Time, llmID *uint) ([]models.BudgetUsage, error)
- func GetChatLogsForChatID(db *gorm.DB, chatID uint) ([]models.LLMChatLogEntry, error)
- func GetCostAnalysis(db *gorm.DB, startDate, endDate time.Time, ...) (map[string]*ChartData, error)
- func GetProxyLogsForAppID(db *gorm.DB, startDate, endDate time.Time, appID uint, page, pageSize int, ...) ([]models.ProxyLog, int64, error)
- func GetProxyLogsForLLM(db *gorm.DB, startDate, endDate time.Time, llmID uint, page, pageSize int, ...) ([]models.ProxyLog, int64, error)
- func GetTokenUsageAndCostForApp(db *gorm.DB, startDate, endDate time.Time, appID uint) (*models.MultiAxisChartData, error)
- func GetToolOperationsUsageOverTime(db *gorm.DB, toolID uint, startDate, endDate time.Time) (*models.MultiAxisChartData, error)
- func GetUsage(db *gorm.DB, startDate, endDate time.Time, vendor string, llmID, appID *uint, ...) (*models.MultiAxisChartData, error)
- func Init(ctx context.Context, db *gorm.DB)
- func InitDefault(ctx context.Context, db *gorm.DB)
- func Migrate(db *gorm.DB) error
- func RecordChatLogEntry(ctx context.Context, log *models.LLMChatLogEntry)
- func RecordChatRecord(ctx context.Context, record *models.LLMChatRecord)
- func RecordChatRecordsBatch(ctx context.Context, records []*models.LLMChatRecord)
- func RecordComplianceEvents(ctx context.Context, events []*models.ComplianceEvent)
- func RecordContentMessage(mc *llms.MessageContent, cr *llms.ContentResponse, vendor models.Vendor, ...)
- func RecordExchange(ctx context.Context, log *models.ProxyLog, rec *models.LLMChatRecord)
- func RecordProxyLog(ctx context.Context, log *models.ProxyLog)
- func RecordProxyLogsBatch(ctx context.Context, logs []*models.ProxyLog)
- func RecordToolCall(ctx context.Context, name string, t time.Time, execTime int, toolID uint)
- func RequestLatencyMS(ctx context.Context, end time.Time) (int, bool)
- func ResetHandler()
- func SetBufferSize(n int)
- func SetHandler(handler AnalyticsHandler)
- func StartRecording(ctx context.Context, db *gorm.DB)
- func WithRequestStart(ctx context.Context, start time.Time) context.Context
- type AnalyticsHandler
- type AppActivity
- type AppSpendWindow
- type ChartData
- func GetAppInteractionsOverTime(db *gorm.DB, startDate, endDate time.Time, appID uint) (*ChartData, error)
- func GetChatInteractionsForChat(db *gorm.DB, startDate, endDate time.Time, chatID string) (*ChartData, error)
- func GetChatRecordsPerDay(db *gorm.DB, startDate, endDate *time.Time) (*ChartData, error)
- func GetChatRecordsPerUser(db *gorm.DB, startDate, endDate time.Time) (*ChartData, error)
- func GetModelUsage(db *gorm.DB, startDate, endDate time.Time, modelName string) (*ChartData, error)
- func GetMostUsedLLMModels(db *gorm.DB, startDate, endDate time.Time, ...) (*ChartData, error)
- func GetTokenUsageForApp(db *gorm.DB, startDate, endDate time.Time, appID uint) (*ChartData, error)
- func GetTokenUsagePerApp(db *gorm.DB, startDate, endDate time.Time, ...) (*ChartData, error)
- func GetTokenUsagePerUser(db *gorm.DB, startDate, endDate time.Time, ...) (*ChartData, error)
- func GetToolCallsPerDay(db *gorm.DB, startDate, endDate time.Time) (*ChartData, error)
- func GetToolUsageStatistics(db *gorm.DB, startDate, endDate time.Time) (*ChartData, error)
- func GetUniqueUsersPerDay(db *gorm.DB, startDate, endDate time.Time) (*ChartData, error)
- func GetVendorUsage(db *gorm.DB, startDate, endDate time.Time, vendor string, llmID *uint) (*ChartData, error)
- type DatabaseHandler
- func (h *DatabaseHandler) RecordChatLogEntry(_ context.Context, logEntry *models.LLMChatLogEntry)
- func (h *DatabaseHandler) RecordChatRecord(_ context.Context, record *models.LLMChatRecord)
- func (h *DatabaseHandler) RecordChatRecordsBatch(_ context.Context, records []*models.LLMChatRecord)
- func (h *DatabaseHandler) RecordComplianceEvents(_ context.Context, events []*models.ComplianceEvent)
- func (h *DatabaseHandler) RecordProxyLog(_ context.Context, log *models.ProxyLog)
- func (h *DatabaseHandler) RecordProxyLogsBatch(_ context.Context, logs []*models.ProxyLog)
- func (h *DatabaseHandler) RecordToolCall(_ context.Context, name string, timestamp time.Time, execTime int, toolID uint)
- func (h *DatabaseHandler) SetAsGlobalHandler()
- func (h *DatabaseHandler) Stop()
- type ExchangeRecorder
- type ModelAppUsage
- type ScanTime
- type Tee
- func (t *Tee) Dropped() []uint64
- func (t *Tee) RecordChatLogEntry(ctx context.Context, log *models.LLMChatLogEntry)
- func (t *Tee) RecordChatRecord(ctx context.Context, record *models.LLMChatRecord)
- func (t *Tee) RecordChatRecordsBatch(ctx context.Context, records []*models.LLMChatRecord)
- func (t *Tee) RecordComplianceEvents(ctx context.Context, events []*models.ComplianceEvent)
- func (t *Tee) RecordExchange(ctx context.Context, log *models.ProxyLog, rec *models.LLMChatRecord)
- func (t *Tee) RecordProxyLog(ctx context.Context, log *models.ProxyLog)
- func (t *Tee) RecordProxyLogsBatch(ctx context.Context, logs []*models.ProxyLog)
- func (t *Tee) RecordToolCall(ctx context.Context, name string, timestamp time.Time, execTime int, ...)
- func (t *Tee) SetAsGlobalHandler()
- func (t *Tee) Stop(timeout time.Duration)
- type VendorModelCost
Constants ¶
This section is empty.
Variables ¶
var TeeQueueSize = 10000
TeeQueueSize is the per-sink queue length.
Functions ¶
func GetAppActivity ¶
GetAppActivity returns activity keyed by app id for the given apps. Apps with no chat records are absent from the map.
func GetAppSpending ¶
GetAppSpending returns each app's spend between its own window start and `end`, in the same units as the budget service (llm_chat_records.cost is stored x10000; see proxy/analyze_utils.go). One query for every app: each app contributes an (app_id, start) predicate, so a user with twenty apps costs one round trip rather than twenty. Apps with no records are absent.
func GetBudgetUsage ¶
func GetBudgetUsage(db *gorm.DB, startDate, endDate *time.Time, llmID *uint) ([]models.BudgetUsage, error)
GetBudgetUsage returns usage statistics for all LLMs and Apps that have costs, with optional date range
func GetChatLogsForChatID ¶
GetChatLogsForChatID retrieves all chat log entries for a specific chat ID
func GetCostAnalysis ¶
func GetCostAnalysis(db *gorm.DB, startDate, endDate time.Time, interactionType *models.InteractionType) (map[string]*ChartData, error)
GetCostAnalysis returns the total cost per day for each currency and interaction type
func GetProxyLogsForAppID ¶
func GetProxyLogsForAppID(db *gorm.DB, startDate, endDate time.Time, appID uint, page, pageSize int, search string) ([]models.ProxyLog, int64, error)
GetProxyLogsForAppID returns paginated proxy logs for a specific app Optional search parameter searches request_body and response_body fields
func GetProxyLogsForLLM ¶
func GetProxyLogsForLLM(db *gorm.DB, startDate, endDate time.Time, llmID uint, page, pageSize int, search string) ([]models.ProxyLog, int64, error)
GetProxyLogsForLLM returns paginated proxy logs for a specific LLM by filtering on vendor Optional search parameter searches request_body and response_body fields
func GetTokenUsageAndCostForApp ¶
func GetTokenUsageAndCostForApp(db *gorm.DB, startDate, endDate time.Time, appID uint) (*models.MultiAxisChartData, error)
GetTokenUsageAndCostForApp returns the token usage and total cost for a specific app over time
func GetToolOperationsUsageOverTime ¶
func GetToolOperationsUsageOverTime(db *gorm.DB, toolID uint, startDate, endDate time.Time) (*models.MultiAxisChartData, error)
GetToolOperationsUsageOverTime returns the usage count for each operation of a specific tool over time
func GetUsage ¶
func GetUsage(db *gorm.DB, startDate, endDate time.Time, vendor string, llmID, appID *uint, interactionType *models.InteractionType, modelName string) (*models.MultiAxisChartData, error)
GetUsage returns token usage and cost data based on provided filters modelName, when non-empty, restricts the series to a single model (matched on the recorded model name, so an alias appears under its resolved name).
func InitDefault ¶
InitDefault initializes the default database analytics handler
func Migrate ¶
Migrate creates or updates the analytics tables. pkg/studio runs it with the other migrations under the cross-instance migration lock. The handler does not migrate: it may write to a database whose schema another instance owns, so whoever owns the schema calls Migrate before recording starts.
func RecordChatLogEntry ¶
func RecordChatLogEntry(ctx context.Context, log *models.LLMChatLogEntry)
func RecordChatRecord ¶
func RecordChatRecord(ctx context.Context, record *models.LLMChatRecord)
func RecordChatRecordsBatch ¶
func RecordChatRecordsBatch(ctx context.Context, records []*models.LLMChatRecord)
RecordChatRecordsBatch records multiple chat records in a batch for improved performance
func RecordComplianceEvents ¶
func RecordComplianceEvents(ctx context.Context, events []*models.ComplianceEvent)
RecordComplianceEvents records filter script compliance events asynchronously
func RecordContentMessage ¶
func RecordContentMessage( mc *llms.MessageContent, cr *llms.ContentResponse, vendor models.Vendor, name, chatID string, timeMs int, userID, appID, llmID uint, t time.Time, svc services.ServiceInterface, dontLogBodies bool, )
func RecordExchange ¶
RecordExchange records a proxied request's proxy log together with its chat record (nil when the response yielded no usage). A handler that implements ExchangeRecorder gets both in one call; any other handler gets RecordProxyLog followed by RecordChatRecord, as before.
func RecordProxyLogsBatch ¶
RecordProxyLogsBatch records multiple proxy logs in a batch for improved performance
func RecordToolCall ¶
func RequestLatencyMS ¶
RequestLatencyMS is the time from the request's start to end, in milliseconds. end is normally the moment the response completed (a record's TimeStamp); when it is not after the start (a timestamp taken at the start of the request) the time elapsed until now is used instead. ok is false when the context carries no start.
func ResetHandler ¶
func ResetHandler()
ResetHandler resets the global analytics handler (useful for testing). When the handler is a stopped database recorder, it waits (briefly) for the recorder's worker to finish, so it does not outlive the Studio that stopped it: a Studio started next in the process replaces the package state (the logger) the worker still uses.
func SetBufferSize ¶
func SetBufferSize(n int)
SetBufferSize sets how many records wait in memory to be written (Studio passes AppConf.AnalyticsBufferSize); without it, ANALYTICS_BUFFER_SIZE decides. Call it before StartRecording.
func SetHandler ¶
func SetHandler(handler AnalyticsHandler)
SetHandler sets the global analytics handler implementation
func StartRecording ¶
StartRecording is deprecated, use InitDefault instead
Types ¶
type AnalyticsHandler ¶
type AnalyticsHandler interface {
// RecordChatRecord records LLM chat/proxy usage
RecordChatRecord(ctx context.Context, record *models.LLMChatRecord)
// RecordChatLogEntry records detailed chat log entries
RecordChatLogEntry(ctx context.Context, log *models.LLMChatLogEntry)
// RecordProxyLog records proxy request/response logs
RecordProxyLog(ctx context.Context, log *models.ProxyLog)
// RecordToolCall records tool call execution
RecordToolCall(ctx context.Context, name string, timestamp time.Time, execTime int, toolID uint)
// SetAsGlobalHandler sets this handler as the global analytics handler
SetAsGlobalHandler()
// Batch processing methods for improved performance
RecordChatRecordsBatch(ctx context.Context, records []*models.LLMChatRecord)
RecordProxyLogsBatch(ctx context.Context, logs []*models.ProxyLog)
// RecordComplianceEvents records filter script compliance events
RecordComplianceEvents(ctx context.Context, events []*models.ComplianceEvent)
}
AnalyticsHandler defines the interface for analytics implementations
func GetHandler ¶
func GetHandler() AnalyticsHandler
GetHandler returns the current analytics handler (useful for testing)
type AppActivity ¶
AppActivity is the portal overview's per-app activity summary: when the app last completed an LLM interaction and how many it made in the window.
It reads llm_chat_records, the same table the app page's token, cost and interaction charts and the budget spend are computed from, so the overview never disagrees with the app page (proxy_logs also records rejected and failed attempts, which the app page does not count). One grouped query over the caller's apps, so the overview costs one round trip however many apps there are.
type AppSpendWindow ¶
AppSpendWindow names an app and the start of its budget period.
type ChartData ¶
type ChartData struct {
Labels []string `json:"labels"`
Data []float64 `json:"data"`
Cost []float64 `json:"cost,omitempty"`
}
func GetAppInteractionsOverTime ¶
func GetAppInteractionsOverTime(db *gorm.DB, startDate, endDate time.Time, appID uint) (*ChartData, error)
GetAppInteractionsOverTime returns the number of LLM interactions for a specific app over time
func GetChatInteractionsForChat ¶
func GetChatInteractionsForChat(db *gorm.DB, startDate, endDate time.Time, chatID string) (*ChartData, error)
GetChatInteractionsForChat returns the number of interactions for a specific chat over time
func GetChatRecordsPerDay ¶
GetChatRecordsPerDay returns the total number of chat records per day for a given time period
func GetChatRecordsPerUser ¶
GetChatRecordsPerUser returns the total number of chat records per user for a given time period
func GetModelUsage ¶
GetModelUsage returns the usage statistics for a specific model over time
func GetMostUsedLLMModels ¶
func GetMostUsedLLMModels(db *gorm.DB, startDate, endDate time.Time, interactionType *models.InteractionType) (*ChartData, error)
GetMostUsedLLMModels returns the usage count for each LLM model
func GetTokenUsageForApp ¶
GetTokenUsageForApp returns the token usage for a specific app over time
func GetTokenUsagePerApp ¶
func GetTokenUsagePerApp(db *gorm.DB, startDate, endDate time.Time, interactionType *models.InteractionType) (*ChartData, error)
GetTokenUsagePerApp returns the total token usage for each app
func GetTokenUsagePerUser ¶
func GetTokenUsagePerUser(db *gorm.DB, startDate, endDate time.Time, interactionType *models.InteractionType) (*ChartData, error)
GetTokenUsagePerUser returns the total token usage for each user
func GetToolCallsPerDay ¶
func GetToolUsageStatistics ¶
GetToolUsageStatistics returns the usage count for each tool
func GetUniqueUsersPerDay ¶
GetUniqueUsersPerDay returns the number of unique users per day
type DatabaseHandler ¶
type DatabaseHandler struct {
// contains filtered or unexported fields
}
DatabaseHandler implements AnalyticsHandler using the existing database/channel system
func NewDatabaseHandler ¶
func NewDatabaseHandler(ctx context.Context, db *gorm.DB) *DatabaseHandler
NewDatabaseHandler creates a new database analytics handler
func (*DatabaseHandler) RecordChatLogEntry ¶
func (h *DatabaseHandler) RecordChatLogEntry(_ context.Context, logEntry *models.LLMChatLogEntry)
RecordChatLogEntry implements analytics.AnalyticsHandler
func (*DatabaseHandler) RecordChatRecord ¶
func (h *DatabaseHandler) RecordChatRecord(_ context.Context, record *models.LLMChatRecord)
Implement AnalyticsHandler interface methods
func (*DatabaseHandler) RecordChatRecordsBatch ¶
func (h *DatabaseHandler) RecordChatRecordsBatch(_ context.Context, records []*models.LLMChatRecord)
RecordChatRecordsBatch records multiple chat records asynchronously using background worker This method is non-blocking and returns immediately to avoid impacting request latency
func (*DatabaseHandler) RecordComplianceEvents ¶
func (h *DatabaseHandler) RecordComplianceEvents(_ context.Context, events []*models.ComplianceEvent)
RecordComplianceEvents records filter script compliance events asynchronously
func (*DatabaseHandler) RecordProxyLog ¶
func (h *DatabaseHandler) RecordProxyLog(_ context.Context, log *models.ProxyLog)
func (*DatabaseHandler) RecordProxyLogsBatch ¶
func (h *DatabaseHandler) RecordProxyLogsBatch(_ context.Context, logs []*models.ProxyLog)
RecordProxyLogsBatch records multiple proxy logs asynchronously using background worker This method is non-blocking and returns immediately to avoid impacting request latency
func (*DatabaseHandler) RecordToolCall ¶
func (*DatabaseHandler) SetAsGlobalHandler ¶
func (h *DatabaseHandler) SetAsGlobalHandler()
SetAsGlobalHandler sets this handler as the global analytics handler
func (*DatabaseHandler) Stop ¶
func (h *DatabaseHandler) Stop()
Stop gracefully stops the database handler
type ExchangeRecorder ¶
type ExchangeRecorder interface {
// RecordExchange records one proxied request. rec is nil when the
// response yielded no usage (an error, or nothing to parse).
RecordExchange(ctx context.Context, log *models.ProxyLog, rec *models.LLMChatRecord)
}
ExchangeRecorder is implemented by handlers that record a proxied request's proxy log and its chat record together. The gateway produces both for one request; a handler that stores them as one row needs them in one call rather than having to pair two separate calls up again.
type ModelAppUsage ¶
type ModelAppUsage struct {
AppID uint `json:"appId"`
AppName string `json:"appName"`
AppDeleted bool `json:"appDeleted"`
OwnerUserID uint `json:"ownerUserId"`
OwnerEmail string `json:"ownerEmail"`
RequestCount int64 `json:"requestCount"`
TotalTokens int64 `json:"totalTokens"`
TotalCost float64 `json:"totalCost"`
FirstUsed ScanTime `json:"firstUsed"`
LastUsed ScanTime `json:"lastUsed"`
}
ModelAppUsage is one app's usage of a specific model under a specific LLM entry. It answers "who is using model X?" with enough detail to contact the app owner.
func GetAppsForModel ¶
func GetAppsForModel(db *gorm.DB, startDate, endDate time.Time, llmID uint, modelName string, interactionType *models.InteractionType) ([]ModelAppUsage, error)
GetAppsForModel returns every app that called the given model through the given LLM entry within the date range, most recently used first. The LLM scope matters: the same model name can sit under two LLM entries (two keys for the same vendor), and the question is asked from one provider's page. Soft-deleted apps are still returned so historical usage stays attributable; they are flagged rather than hidden.
type ScanTime ¶
ScanTime is a time.Time that can be scanned from an aggregated datetime column such as MAX(time_stamp). An aggregate result column has no declared type, so the SQLite driver hands back the raw TEXT instead of a time.Time (Postgres keeps the timestamp type). Scanning such a column straight into a time.Time fails on SQLite with "unsupported Scan, storing driver.Value type string into type *time.Time"; this type accepts all three representations.
type Tee ¶
type Tee struct {
// contains filtered or unexported fields
}
Tee sends every record to a primary handler (Studio's database handler: budgets and spend are computed from it) and to extra sinks, for a host that ships analytics into its own pipeline. Sinks never slow a request: each has a bounded queue drained by its own goroutine, and records that do not fit are dropped and counted. Sinks receive copies, so they never share a record with the primary handler (which writes IDs back into it).
func NewTee ¶
func NewTee(primary AnalyticsHandler, sinks ...AnalyticsHandler) *Tee
NewTee returns a Tee over primary and sinks. Stop it to drain the sinks.
func (*Tee) RecordChatLogEntry ¶
func (t *Tee) RecordChatLogEntry(ctx context.Context, log *models.LLMChatLogEntry)
func (*Tee) RecordChatRecord ¶
func (t *Tee) RecordChatRecord(ctx context.Context, record *models.LLMChatRecord)
func (*Tee) RecordChatRecordsBatch ¶
func (t *Tee) RecordChatRecordsBatch(ctx context.Context, records []*models.LLMChatRecord)
func (*Tee) RecordComplianceEvents ¶
func (t *Tee) RecordComplianceEvents(ctx context.Context, events []*models.ComplianceEvent)
func (*Tee) RecordExchange ¶
RecordExchange keeps a proxied request's log and chat record together for handlers that store them as one (ExchangeRecorder) and splits them for the others, as the package-level RecordExchange does.
func (*Tee) RecordProxyLog ¶
func (*Tee) RecordProxyLogsBatch ¶
func (*Tee) RecordToolCall ¶
func (*Tee) SetAsGlobalHandler ¶
func (t *Tee) SetAsGlobalHandler()
SetAsGlobalHandler makes the Tee the process's analytics handler.
type VendorModelCost ¶
type VendorModelCost struct {
Model string `json:"model"`
ModelPriceID *uint `json:"modelPriceId"`
TotalCost float64 `json:"totalCost"`
PromptCost float64 `json:"promptCost"`
ResponseCost float64 `json:"responseCost"`
CacheWriteCost float64 `json:"cacheWriteCost"`
CacheReadCost float64 `json:"cacheReadCost"`
TotalTokens int64 `json:"totalTokens"` // Total of all token types
PromptTokens int64 `json:"promptTokens"`
ResponseTokens int64 `json:"responseTokens"`
CacheWriteTokens int64 `json:"cacheWriteTokens"`
CacheReadTokens int64 `json:"cacheReadTokens"`
// Usage columns backing the "Models in use" view on the LLM details page.
RequestCount int64 `json:"requestCount"`
AppCount int64 `json:"appCount"`
LastUsed ScanTime `json:"lastUsed"`
}
VendorModelCost represents the total cost for a specific vendor and model
func GetTotalCostPerVendorAndModel ¶
func GetTotalCostPerVendorAndModel(db *gorm.DB, startDate, endDate time.Time, interactionType *models.InteractionType, llmID *uint) ([]VendorModelCost, error)
GetTotalCostPerVendorAndModel returns the total cost per vendor and model with detailed breakdowns