M4a:神识全量网关能力——OpenAI 兼容适配器(元炁共用)、候选路由+凭证/绑定级冷却+首字超时切道+粘性会话、用量计量与 /me/usage,故障注入测试四场景
This commit is contained in:
1 parent
df5caa69e7
commit
7279904043
8 files changed
+717
-35
No files matched your search
@@ -0,0 +1,88 @@
|
||||
// Package metering 用量计量:每次尝试一条记录(stdout 结构化日志 + 进程内聚合)。
|
||||
// 生产部署接 PG(shenshi-gateway.md §7);M4a 先以日志与内存聚合支撑 /me/usage 与对账抽样。
|
||||
package metering
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Attempt 一次端点尝试的计量记录。
|
||||
type Attempt struct {
|
||||
RunID string `json:"run_id"`
|
||||
CharacterID string `json:"character_id,omitempty"`
|
||||
Model string `json:"model"`
|
||||
Slot string `json:"slot"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Status string `json:"status"` // ok / error / first_byte_timeout
|
||||
ErrorCode string `json:"error_code,omitempty"`
|
||||
FirstTokenMS int64 `json:"first_token_ms"`
|
||||
TotalMS int64 `json:"total_ms"`
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
At time.Time `json:"at"`
|
||||
}
|
||||
|
||||
// UsageAgg /me/usage 聚合行。
|
||||
type UsageAgg struct {
|
||||
Model string `json:"model"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Requests int `json:"requests"`
|
||||
OK int `json:"ok"`
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
}
|
||||
|
||||
// Log 进程内计量器(有界环形,避免无界增长)。
|
||||
type Log struct {
|
||||
mu sync.Mutex
|
||||
entries []Attempt
|
||||
limit int
|
||||
}
|
||||
|
||||
// New 构造计量器(保留最近 N 条)。
|
||||
func New(limit int) *Log {
|
||||
return &Log{entries: make([]Attempt, 0, limit), limit: limit}
|
||||
}
|
||||
|
||||
// Record 记录一次尝试:stdout JSON 日志 + 内存追加。
|
||||
func (l *Log) Record(a Attempt) {
|
||||
a.At = time.Now().UTC()
|
||||
if raw, err := json.Marshal(a); err == nil {
|
||||
log.Printf(`{"kind":"shenshi_usage",%s`, string(raw[1:]))
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if len(l.entries) >= l.limit {
|
||||
l.entries = l.entries[1:]
|
||||
}
|
||||
l.entries = append(l.entries, a)
|
||||
}
|
||||
|
||||
// Aggregate 按 模型×端点 聚合。
|
||||
func (l *Log) Aggregate() []UsageAgg {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
agg := map[string]*UsageAgg{}
|
||||
for _, e := range l.entries {
|
||||
key := e.Model + "|" + e.Endpoint
|
||||
row := agg[key]
|
||||
if row == nil {
|
||||
row = &UsageAgg{Model: e.Model, Endpoint: e.Endpoint}
|
||||
agg[key] = row
|
||||
}
|
||||
row.Requests++
|
||||
if e.Status == "ok" {
|
||||
row.OK++
|
||||
row.PromptTokens += e.PromptTokens
|
||||
row.CompletionTokens += e.CompletionTokens
|
||||
}
|
||||
}
|
||||
out := make([]UsageAgg, 0, len(agg))
|
||||
for _, row := range agg {
|
||||
out = append(out, *row)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user