mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-18 21:47:17 +02:00
123 lines
4.2 KiB
Go
123 lines
4.2 KiB
Go
package database
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestModelTokenUsagePersistsFromUsageProcessDetail(t *testing.T) {
|
|
db := newModelTokenUsageTestDB(t)
|
|
conv, err := db.CreateConversation("usage", ConversationCreateMeta{})
|
|
if err != nil {
|
|
t.Fatalf("CreateConversation: %v", err)
|
|
}
|
|
msg, err := db.AddMessage(conv.ID, "assistant", "done", nil)
|
|
if err != nil {
|
|
t.Fatalf("AddMessage: %v", err)
|
|
}
|
|
if err := db.AddProcessDetail(msg.ID, conv.ID, modelTokenUsageEventType, "usage", map[string]interface{}{
|
|
"source": "eino",
|
|
"orchestration": "deep",
|
|
"reason": "final",
|
|
"model": "gpt-test",
|
|
"modelCalls": 2,
|
|
"promptTokens": 10,
|
|
"completionTokens": 3,
|
|
"totalTokens": 13,
|
|
"cachedTokens": 4,
|
|
"reasoningTokens": 1,
|
|
"cacheWriteTokens": 2,
|
|
}); err != nil {
|
|
t.Fatalf("AddProcessDetail: %v", err)
|
|
}
|
|
|
|
stats, err := db.GetModelTokenUsageStats(ModelTokenUsageFilter{})
|
|
if err != nil {
|
|
t.Fatalf("GetModelTokenUsageStats: %v", err)
|
|
}
|
|
if stats.Summary.Events != 1 || stats.Summary.ModelCalls != 2 || stats.Summary.TotalTokens != 13 || stats.Summary.CachedTokens != 4 || stats.Summary.ReasoningTokens != 1 || stats.Summary.CacheWriteTokens != 2 {
|
|
t.Fatalf("summary = %#v", stats.Summary)
|
|
}
|
|
if len(stats.ByModel) != 1 || stats.ByModel[0].Key != "gpt-test" || stats.ByModel[0].TotalTokens != 13 || stats.ByModel[0].CacheWriteTokens != 2 {
|
|
t.Fatalf("by model = %#v", stats.ByModel)
|
|
}
|
|
if len(stats.Recent) != 1 || stats.Recent[0].CacheWriteTokens != 2 || stats.Today.CacheWriteTokens != 2 || len(stats.ByDay) != 1 || stats.ByDay[0].CacheWriteTokens != 2 {
|
|
t.Fatalf("cache write usage lost: %#v", stats)
|
|
}
|
|
}
|
|
|
|
func TestModelTokenUsageBackfillIsIdempotent(t *testing.T) {
|
|
db := newModelTokenUsageTestDB(t)
|
|
conv, err := db.CreateConversation("usage", ConversationCreateMeta{})
|
|
if err != nil {
|
|
t.Fatalf("CreateConversation: %v", err)
|
|
}
|
|
msg, err := db.AddMessage(conv.ID, "assistant", "done", nil)
|
|
if err != nil {
|
|
t.Fatalf("AddMessage: %v", err)
|
|
}
|
|
if err := db.AddProcessDetail(msg.ID, conv.ID, modelTokenUsageEventType, "usage", map[string]interface{}{
|
|
"source": "eino", "modelCalls": 1, "promptTokens": 7, "completionTokens": 5, "totalTokens": 12,
|
|
}); err != nil {
|
|
t.Fatalf("AddProcessDetail: %v", err)
|
|
}
|
|
if err := db.BackfillModelTokenUsageFromProcessDetails(); err != nil {
|
|
t.Fatalf("Backfill 1: %v", err)
|
|
}
|
|
if err := db.BackfillModelTokenUsageFromProcessDetails(); err != nil {
|
|
t.Fatalf("Backfill 2: %v", err)
|
|
}
|
|
stats, err := db.GetModelTokenUsageStats(ModelTokenUsageFilter{})
|
|
if err != nil {
|
|
t.Fatalf("GetModelTokenUsageStats: %v", err)
|
|
}
|
|
if stats.Summary.Events != 1 || stats.Summary.TotalTokens != 12 {
|
|
t.Fatalf("summary after backfill = %#v", stats.Summary)
|
|
}
|
|
}
|
|
|
|
func newModelTokenUsageTestDB(t *testing.T) *DB {
|
|
t.Helper()
|
|
db, err := NewDB(filepath.Join(t.TempDir(), "usage.db"), zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("NewDB: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = db.Close() })
|
|
return db
|
|
}
|
|
|
|
func TestModelTokenUsageStatsRespectConversationAccess(t *testing.T) {
|
|
db := newModelTokenUsageTestDB(t)
|
|
var ownID string
|
|
for _, owner := range []string{"alice", "bob"} {
|
|
conv, err := db.CreateConversation(owner, ConversationCreateMeta{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if owner == "alice" {
|
|
ownID = conv.ID
|
|
}
|
|
if _, err := db.Exec(`UPDATE conversations SET owner_user_id = ? WHERE id = ?`, owner, conv.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
msg, err := db.AddMessage(conv.ID, "assistant", "done", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.AddProcessDetail(msg.ID, conv.ID, modelTokenUsageEventType, "usage", map[string]interface{}{
|
|
"model": owner, "modelCalls": 1, "promptTokens": 100, "completionTokens": 20, "cacheWriteTokens": 30,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
stats, err := db.GetModelTokenUsageStats(ModelTokenUsageFilter{Access: RBACListAccess{UserID: "alice", Scope: "own"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stats.Summary.Events != 1 || stats.Summary.TotalTokens != 120 || stats.Summary.CacheWriteTokens != 30 || len(stats.Recent) != 1 || stats.Recent[0].ConversationID != ownID || len(stats.ByModel) != 1 || stats.ByModel[0].Key != "alice" {
|
|
t.Fatalf("usage leaks across users: %#v", stats)
|
|
}
|
|
}
|