47f9de2409
- 新增 call_log.go: 全局环形缓冲区记录每次 LLM 调用(模型/Token/耗时/错误) - OpenAIProvider.doChat/ChatStreamWithTools 自动记录调用日志 - ai-core 暴露 GET /api/v1/llm-calls 端点, DevTools 代理 + UI 面板 - ModelSelector.envProvider 改为单例缓存, 避免重复创建 HTTP Client - 新增 PurposeToolCalling 适配器, 后台思考工具调用走专用路由 - envFallback 超时 120s→180s, 显式设置 MaxRetries - devtools.bat 全英文, 解决 Windows CMD GBK 编码乱码问题 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
75 lines
1.6 KiB
Go
75 lines
1.6 KiB
Go
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
|
|
}
|