package main import ( "fmt" "math/rand" "strings" "time" ) // Generator creates deterministic demo data type Generator struct { rng *rand.Rand seederTag string profile string } // NewGenerator creates a new data generator func NewGenerator(rng *rand.Rand, seederTag, profile string) *Generator { return &Generator{ rng: rng, seederTag: seederTag, profile: profile, } } // --- Namespace Generation --- var namespaceTemplates = []struct { name string desc string }{ {"default", "Default namespace for general use"}, {"premium", "Premium tier with higher limits"}, {"staging", "Staging environment namespace"}, {"internal", "Internal services namespace"}, {"partner", "Partner integrations namespace"}, } func (g *Generator) GenerateNamespaces(count int) []NamespaceRequest { result := make([]NamespaceRequest, 0, count) for i := 0; i < count && i < len(namespaceTemplates); i++ { t := namespaceTemplates[i] result = append(result, NamespaceRequest{ Name: fmt.Sprintf("demo-%s", t.name), Status: "active", Description: fmt.Sprintf("%s [%s]", t.desc, g.seederTag), }) } return result } // --- Provider Group Generation --- var providerGroupTemplates = []struct { name string ptype string baseURL string models []string }{ { name: "openai-demo", ptype: "openai", baseURL: "https://api.openai.com/v1", models: []string{"gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo"}, }, { name: "anthropic-demo", ptype: "anthropic", baseURL: "https://api.anthropic.com", models: []string{"claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229"}, }, { name: "gemini-demo", ptype: "gemini", baseURL: "https://generativelanguage.googleapis.com/v1beta", models: []string{"gemini-1.5-pro", "gemini-1.5-flash", "gemini-2.0-flash-exp"}, }, { name: "deepseek-demo", ptype: "openai", baseURL: "https://api.deepseek.com/v1", models: []string{"deepseek-chat", "deepseek-coder"}, }, { name: "groq-demo", ptype: "openai", baseURL: "https://api.groq.com/openai/v1", models: []string{"llama-3.1-70b-versatile", "llama-3.1-8b-instant", "mixtral-8x7b-32768"}, }, { name: "mistral-demo", ptype: "openai", baseURL: "https://api.mistral.ai/v1", models: []string{"mistral-large-latest", "mistral-medium-latest", "mistral-small-latest"}, }, { name: "cohere-demo", ptype: "openai", baseURL: "https://api.cohere.ai/v1", models: []string{"command-r-plus", "command-r", "command"}, }, { name: "together-demo", ptype: "openai", baseURL: "https://api.together.xyz/v1", models: []string{"meta-llama/Llama-3-70b-chat-hf", "mistralai/Mixtral-8x22B-Instruct-v0.1"}, }, { name: "openrouter-demo", ptype: "openai", baseURL: "https://openrouter.ai/api/v1", models: []string{"openai/gpt-4o", "anthropic/claude-3.5-sonnet"}, }, { name: "azure-demo", ptype: "azure", baseURL: "https://demo.openai.azure.com/openai/deployments", models: []string{"gpt-4o-deploy", "gpt-35-turbo-deploy"}, }, } func (g *Generator) GenerateProviderGroups(count int) []ProviderGroupRequest { result := make([]ProviderGroupRequest, 0, count) for i := 0; i < count && i < len(providerGroupTemplates); i++ { t := providerGroupTemplates[i] result = append(result, ProviderGroupRequest{ Name: t.name, Type: t.ptype, BaseURL: t.baseURL, Models: t.models, Status: "active", }) } return result } // --- Provider (APIKey) Generation --- func (g *Generator) GenerateProviders(groupID uint, groupName string, count int) []APIKeyRequest { result := make([]APIKeyRequest, 0, count) for i := 0; i < count; i++ { // Generate a fake API key based on group name and index fakeKey := fmt.Sprintf("sk-%s-%s-%04d", groupName, g.randomString(24), i+1) result = append(result, APIKeyRequest{ GroupID: groupID, APIKey: fakeKey, Weight: 100, Status: "active", }) } return result } // --- Model Generation --- var modelTemplates = []struct { name string kind string contextWindow int maxOutput int vision bool functions bool toolChoice bool costPerToken float64 }{ {"gpt-4o", "chat", 128000, 16384, true, true, true, 0.000005}, {"gpt-4o-mini", "chat", 128000, 16384, true, true, true, 0.00000015}, {"gpt-4-turbo", "chat", 128000, 4096, true, true, true, 0.00001}, {"gpt-3.5-turbo", "chat", 16385, 4096, false, true, true, 0.0000005}, {"claude-3-5-sonnet-20241022", "chat", 200000, 8192, true, true, true, 0.000003}, {"claude-3-5-haiku-20241022", "chat", 200000, 8192, true, true, true, 0.00000025}, {"claude-3-opus-20240229", "chat", 200000, 4096, true, true, true, 0.000015}, {"gemini-1.5-pro", "chat", 2097152, 8192, true, true, true, 0.00000125}, {"gemini-1.5-flash", "chat", 1048576, 8192, true, true, true, 0.000000075}, {"gemini-2.0-flash-exp", "chat", 1048576, 8192, true, true, true, 0.0000001}, {"deepseek-chat", "chat", 64000, 4096, false, true, true, 0.00000014}, {"deepseek-coder", "chat", 64000, 4096, false, true, true, 0.00000014}, {"llama-3.1-70b-versatile", "chat", 131072, 8192, false, true, true, 0.00000059}, {"llama-3.1-8b-instant", "chat", 131072, 8192, false, true, true, 0.00000005}, {"mixtral-8x7b-32768", "chat", 32768, 4096, false, true, false, 0.00000027}, {"mistral-large-latest", "chat", 128000, 8192, false, true, true, 0.000002}, {"command-r-plus", "chat", 128000, 4096, false, true, true, 0.000003}, {"command-r", "chat", 128000, 4096, false, true, true, 0.0000005}, {"text-embedding-3-small", "embedding", 8191, 0, false, false, false, 0.00000002}, {"text-embedding-3-large", "embedding", 8191, 0, false, false, false, 0.00000013}, {"voyage-large-2", "embedding", 16000, 0, false, false, false, 0.00000012}, {"rerank-english-v3.0", "rerank", 4096, 0, false, false, false, 0.000001}, {"rerank-multilingual-v3.0", "rerank", 4096, 0, false, false, false, 0.000001}, {"o1-preview", "chat", 128000, 32768, true, false, false, 0.000015}, {"o1-mini", "chat", 128000, 65536, true, false, false, 0.000003}, {"dall-e-3", "other", 0, 0, false, false, false, 0.04}, {"whisper-1", "other", 0, 0, false, false, false, 0.006}, {"tts-1", "other", 0, 0, false, false, false, 0.000015}, {"tts-1-hd", "other", 0, 0, false, false, false, 0.00003}, {"codestral-latest", "chat", 32000, 8192, false, true, true, 0.000001}, } func (g *Generator) GenerateModels(count int) []ModelRequest { result := make([]ModelRequest, 0, count) for i := 0; i < count && i < len(modelTemplates); i++ { t := modelTemplates[i] result = append(result, ModelRequest{ Name: t.name, Kind: t.kind, ContextWindow: t.contextWindow, MaxOutputTokens: t.maxOutput, SupportsVision: t.vision, SupportsFunctions: t.functions, SupportsToolChoice: t.toolChoice, CostPerToken: t.costPerToken, }) } return result } // --- Binding Generation --- func (g *Generator) GenerateBindings(namespaces []string, groups []ProviderGroupResponse, count int) []BindingRequest { result := make([]BindingRequest, 0, count) if len(namespaces) == 0 || len(groups) == 0 { return result } // Create bindings for common models commonModels := []string{"gpt-4o", "gpt-4o-mini", "claude-3-5-sonnet-20241022", "gemini-1.5-pro"} for i := 0; i < count; i++ { ns := namespaces[i%len(namespaces)] group := groups[i%len(groups)] groupModels := group.GetModelsSlice() var modelName string if i < len(commonModels) { modelName = commonModels[i] } else { // Pick a model from the group's models if len(groupModels) > 0 { modelName = groupModels[g.rng.Intn(len(groupModels))] } else { modelName = fmt.Sprintf("model-%d", i) } } result = append(result, BindingRequest{ Namespace: ns, PublicModel: modelName, GroupID: group.ID, Weight: 100, SelectorType: "exact", SelectorValue: modelName, Status: "active", }) } return result } // --- Master Generation --- var masterTemplates = []struct { name string group string maxKeys int qps int }{ {"admin-demo", "default", 10, 100}, {"user-demo", "default", 5, 50}, {"developer-demo", "default", 20, 200}, {"partner-demo", "partner", 15, 150}, {"staging-demo", "staging", 10, 100}, {"internal-demo", "internal", 50, 500}, {"test-demo", "default", 5, 25}, {"premium-demo", "premium", 100, 1000}, {"enterprise-demo", "enterprise", 200, 2000}, {"trial-demo", "trial", 3, 10}, } func (g *Generator) GenerateMasters(count int) []MasterRequest { result := make([]MasterRequest, 0, count) for i := 0; i < count && i < len(masterTemplates); i++ { t := masterTemplates[i] result = append(result, MasterRequest{ Name: t.name, Group: t.group, MaxChildKeys: t.maxKeys, GlobalQPS: t.qps, }) } return result } // --- Key Generation --- func (g *Generator) GenerateKeys(count int) []KeyRequest { result := make([]KeyRequest, 0, count) for i := 0; i < count; i++ { result = append(result, KeyRequest{ Scopes: "chat,embedding", }) } return result } // --- Usage Sample Generation --- // UsageSampleContext contains all the data needed for generating usage samples type UsageSampleContext struct { Masters []MasterResponse Keys map[uint][]KeyResponse // master_id -> keys Groups []ProviderGroupResponse Providers map[uint][]APIKeyResponse // group_id -> api keys (providers) UsageDays int } func (g *Generator) GenerateUsageSamples(ctx UsageSampleContext) []LogRequest { result := make([]LogRequest, 0) if len(ctx.Masters) == 0 || len(ctx.Groups) == 0 { return result } now := time.Now() startTime := now.AddDate(0, 0, -ctx.UsageDays) // Generate samples for each day for day := 0; day < ctx.UsageDays; day++ { dayTime := startTime.AddDate(0, 0, day) // More traffic on weekdays isWeekday := dayTime.Weekday() >= time.Monday && dayTime.Weekday() <= time.Friday baseCount := 50 if isWeekday { baseCount = 100 } // Gradual growth trend growthFactor := 1.0 + float64(day)/float64(ctx.UsageDays)*0.5 samplesPerDay := int(float64(baseCount) * growthFactor) for i := 0; i < samplesPerDay; i++ { // Pick random master master := ctx.Masters[g.rng.Intn(len(ctx.Masters))] // Pick random key for this master masterKeys := ctx.Keys[master.ID] var keyID uint if len(masterKeys) > 0 { keyID = masterKeys[g.rng.Intn(len(masterKeys))].ID } // Pick random group group := ctx.Groups[g.rng.Intn(len(ctx.Groups))] groupModels := group.GetModelsSlice() // Pick random model from group var modelName string if len(groupModels) > 0 { modelName = groupModels[g.rng.Intn(len(groupModels))] } else { modelName = "gpt-4o" } // Pick random provider (API key) from group for ProviderID var providerID uint groupProviders := ctx.Providers[group.ID] if len(groupProviders) > 0 { providerID = groupProviders[g.rng.Intn(len(groupProviders))].ID } // Generate realistic metrics statusCode := 200 errorMsg := "" // 2-5% error rate if g.rng.Float64() < 0.03 { errorCodes := []int{400, 401, 429, 500, 503} statusCode = errorCodes[g.rng.Intn(len(errorCodes))] errorMsgs := []string{ "rate limit exceeded", "invalid request", "authentication failed", "internal server error", "service unavailable", } errorMsg = errorMsgs[g.rng.Intn(len(errorMsgs))] } // Realistic latency (50ms - 5000ms, with most around 200-500ms) latencyMs := int64(50 + g.rng.ExpFloat64()*300) if latencyMs > 5000 { latencyMs = 5000 } // Realistic token counts tokensIn := int64(100 + g.rng.Intn(2000)) tokensOut := int64(50 + g.rng.Intn(1000)) // Request/response sizes requestSize := tokensIn * 4 // ~4 bytes per token responseSize := tokensOut * 4 // Random client IP clientIP := fmt.Sprintf("192.168.%d.%d", g.rng.Intn(256), g.rng.Intn(256)) // NOTE: The /logs API uses gorm.Model which auto-sets created_at // We cannot inject historical timestamps via API. // For historical data, we would need direct DB access. // Current implementation creates logs at "now" time. // Format provider_name as group#keyID to match DP log format providerName := group.Name if providerID > 0 { providerName = fmt.Sprintf("%s#%d", group.Name, providerID) } result = append(result, LogRequest{ Group: master.Group, MasterID: master.ID, KeyID: keyID, Model: modelName, ProviderID: providerID, ProviderType: group.Type, ProviderName: providerName, StatusCode: statusCode, LatencyMs: latencyMs, TokensIn: tokensIn, TokensOut: tokensOut, ErrorMessage: errorMsg, ClientIP: clientIP, RequestSize: requestSize, ResponseSize: responseSize, }) } } return result } // --- Helper functions --- func (g *Generator) randomString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, length) for i := range b { b[i] = charset[g.rng.Intn(len(charset))] } return string(b) } // HasSeederTag checks if a string contains the seeder tag func HasSeederTag(s, tag string) bool { return strings.Contains(s, tag) } // ExtractSeederTag extracts the seeder tag from a string func ExtractSeederTag(s string) string { if idx := strings.Index(s, "seeder:"); idx != -1 { end := strings.Index(s[idx:], "]") if end == -1 { end = len(s) - idx } return s[idx : idx+end] } return "" }