Files
Cyrene_v2/backend/services/ai-core/llm/logger.go
T
Aska 6a4887e52a feat(llm): add LLM call recording with admin panel integration
- llm.Adapter: SetCallLogger hook for recording every API call
- llm.CallLogger: ring-buffer with Recent+Stats for admin API
- main.go: wire DeepSeek adapter to CallLogger
- Fix: single CallLogger instance for both recording and display
- Metrics: model name, duration, success/fail, tokens
- All 16 packages pass
2026-08-12 19:10:51 +08:00

62 lines
1.7 KiB
Go

package llm
import (
"sync"
"time"
)
type CallLogger struct {
mu sync.RWMutex
records []CallRecord
max int
index int
total int64
}
func NewCallLogger(max int) *CallLogger { return &CallLogger{records: make([]CallRecord, max), max: max} }
func (l *CallLogger) Record(r CallRecord) {
l.mu.Lock()
defer l.mu.Unlock()
l.records[l.index%l.max] = r
l.index++
l.total++
}
func (l *CallLogger) Recent(n int) []CallRecord {
l.mu.RLock(); defer l.mu.RUnlock()
if n > l.max { n = l.max }
count := l.index; if count > l.max { count = l.max }; if n > count { n = count }
result := make([]CallRecord, n)
for i := 0; i < n; i++ {
idx := (l.index - 1 - i + l.max) % l.max
if idx < l.max { result[n-1-i] = l.records[idx] }
}
return result
}
func (l *CallLogger) RecentMaps(n int) []map[string]any {
records := l.Recent(n); result := make([]map[string]any, len(records))
for i, r := range records {
result[i] = map[string]any{
"time": r.Time.Format(time.RFC3339), "model": r.Model, "purpose": r.Purpose,
"prompt_tokens": r.PromptTokens, "completion_tokens": r.CompletionTokens,
"duration_ms": r.DurationMs, "success": r.Success, "error": r.Error,
}
}
return result
}
func (l *CallLogger) Stats() map[string]any {
l.mu.RLock(); defer l.mu.RUnlock()
var calls, succ int64; var dur, tok int64
count := l.index; if count > l.max { count = l.max }
for i := 0; i < count; i++ {
r := l.records[(l.index-1-i+l.max)%l.max]
calls++; if r.Success { succ++ }
dur += r.DurationMs; tok += int64(r.PromptTokens + r.CompletionTokens)
}
avg := 0; if calls > 0 { avg = int(dur / calls) }
return map[string]any{"total_calls": calls, "success_calls": succ, "avg_duration_ms": avg, "total_tokens": tok}
}