package llm import ( "sync" "time" ) // CallRecord records a single LLM API call. type CallRecord struct { Time time.Time `json:"time"` Model string `json:"model"` Duration time.Duration `json:"duration_ms"` PromptTokens int `json:"prompt_tokens"` CompletionTokens int `json:"completion_tokens"` TotalTokens int `json:"total_tokens"` Success bool `json:"success"` Error string `json:"error,omitempty"` } // CallLogger is a thread-safe ring buffer for LLM call records. type CallLogger struct { mu sync.RWMutex records []CallRecord capacity int head int size int } var globalCallLogger = &CallLogger{capacity: 500} // LogCall records an LLM call. Safe for concurrent use. func LogCall(r CallRecord) { globalCallLogger.log(r) } // GetCalls returns recent call records, newest first. func GetCalls(limit int) []CallRecord { return globalCallLogger.get(limit) } func (cl *CallLogger) log(r CallRecord) { cl.mu.Lock() defer cl.mu.Unlock() if cl.records == nil { cl.records = make([]CallRecord, cl.capacity) } r.Time = time.Now() cl.records[cl.head] = r cl.head = (cl.head + 1) % cl.capacity if cl.size < cl.capacity { cl.size++ } } func (cl *CallLogger) get(limit int) []CallRecord { cl.mu.RLock() defer cl.mu.RUnlock() if limit <= 0 || limit > cl.size { limit = cl.size } result := make([]CallRecord, limit) for i := 0; i < limit; i++ { idx := (cl.head - 1 - i) % cl.capacity if idx < 0 { idx += cl.capacity } result[i] = cl.records[idx] } return result }