4c9ed3c24b
Complete rewrite from Python/FastAPI to Go/Gin: - Go backend: auth (API keys + bcrypt), llama.cpp subprocess manager, hot-swap multi-model, rate limiting, quota system, webhooks - Vue 3 SPA admin panel (src/) with Tailwind CSS - Deployment: Docker multi-stage, docker-compose, nginx, systemd - GORM/SQLite models: ApiKey, Model, UsageLog, Quota, Webhook - REST API: /api/v1/admin/* (keys, models, chat, usage, health) - Embedded frontend via go:embed (build output at web/dist/) Removed legacy Python artifacts (app/, tests/, pyproject.toml, etc.)
138 lines
3.5 KiB
Go
138 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"log/slog"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
// Server
|
|
LlamalinkEnv string
|
|
LlamalinkHost string
|
|
LlamalinkPort int
|
|
|
|
// Database
|
|
DatabaseURL string
|
|
DatabaseMaxOpenConns int
|
|
DatabaseMaxIdleConns int
|
|
DatabaseConnMaxLifetime int // seconds
|
|
|
|
// Llama server management
|
|
ManageLlamaServer bool
|
|
LlamaServerBin string
|
|
LlamaServerHost string
|
|
LlamaServerPort int
|
|
LlamaServerStartupTimeout int // seconds
|
|
LlamaServerStopTimeout int // seconds
|
|
ModelSwapCooldown int // seconds
|
|
|
|
// Rate limiting
|
|
RateLimitPerMinute int
|
|
RateLimitStorage string // "memory" or "redis"
|
|
|
|
// Auth
|
|
AdminToken string
|
|
|
|
// Logging
|
|
LogLevel string
|
|
LogFormat string // "json" or "text"
|
|
}
|
|
|
|
func Load() *Config {
|
|
c := &Config{
|
|
LlamalinkEnv: getEnv("LLAMALINK_ENV", "development"),
|
|
LlamalinkHost: getEnv("LLAMALINK_HOST", "0.0.0.0"),
|
|
LlamalinkPort: intEnv("LLAMALINK_PORT", 8000),
|
|
DatabaseURL: getEnv("DATABASE_URL", "sqlite:///./llamalink.db"),
|
|
DatabaseMaxOpenConns: intEnv("DATABASE_MAX_OPEN_CONNS", 25),
|
|
DatabaseMaxIdleConns: intEnv("DATABASE_MAX_IDLE_CONNS", 5),
|
|
DatabaseConnMaxLifetime: intEnv("DATABASE_CONN_MAX_LIFETIME", 300),
|
|
ManageLlamaServer: boolEnv("MANAGE_LLAMA_SERVER", true),
|
|
LlamaServerBin: getEnv("LLAMA_SERVER_BIN", "/usr/local/bin/llama-server"),
|
|
LlamaServerHost: getEnv("LLAMA_SERVER_HOST", "127.0.0.1"),
|
|
LlamaServerPort: intEnv("LLAMA_SERVER_PORT", 8080),
|
|
LlamaServerStartupTimeout: intEnv("LLAMA_SERVER_STARTUP_TIMEOUT", 120),
|
|
LlamaServerStopTimeout: intEnv("LLAMA_SERVER_STOP_TIMEOUT", 10),
|
|
ModelSwapCooldown: intEnv("MODEL_SWAP_COOLDOWN", 2),
|
|
RateLimitPerMinute: intEnv("RATE_LIMIT_PER_MINUTE", 60),
|
|
RateLimitStorage: getEnv("RATE_LIMIT_STORAGE", "memory"),
|
|
AdminToken: getEnv("ADMIN_TOKEN", "changeme"),
|
|
LogLevel: getEnv("LOG_LEVEL", "info"),
|
|
LogFormat: getEnv("LOG_FORMAT", "json"),
|
|
}
|
|
return c
|
|
}
|
|
|
|
func (c *Config) LlamaServerURL() string {
|
|
return "http://" + c.LlamaServerHost + ":" + strconv.Itoa(c.LlamaServerPort)
|
|
}
|
|
|
|
func (c *Config) LlamaServerStartupTimeoutDuration() time.Duration {
|
|
return time.Duration(c.LlamaServerStartupTimeout) * time.Second
|
|
}
|
|
|
|
func (c *Config) LlamaServerStopTimeoutDuration() time.Duration {
|
|
return time.Duration(c.LlamaServerStopTimeout) * time.Second
|
|
}
|
|
|
|
func (c *Config) ModelSwapCooldownDuration() time.Duration {
|
|
return time.Duration(c.ModelSwapCooldown) * time.Second
|
|
}
|
|
|
|
func (c *Config) Logger() *slog.Logger {
|
|
var level slog.Level
|
|
switch c.LogLevel {
|
|
case "debug":
|
|
level = slog.LevelDebug
|
|
case "warn":
|
|
level = slog.LevelWarn
|
|
case "error":
|
|
level = slog.LevelError
|
|
default:
|
|
level = slog.LevelInfo
|
|
}
|
|
|
|
opts := &slog.HandlerOptions{
|
|
Level: level,
|
|
}
|
|
|
|
var handler slog.Handler
|
|
if c.LogFormat == "text" {
|
|
handler = slog.NewTextHandler(os.Stdout, opts)
|
|
} else {
|
|
handler = slog.NewJSONHandler(os.Stdout, opts)
|
|
}
|
|
|
|
return slog.New(handler)
|
|
}
|
|
|
|
func getEnv(key, defaultValue string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func intEnv(key string, defaultValue int) int {
|
|
if v := os.Getenv(key); v != "" {
|
|
if i, err := strconv.Atoi(v); err == nil {
|
|
return i
|
|
}
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func boolEnv(key string, defaultValue bool) bool {
|
|
if v := os.Getenv(key); v != "" {
|
|
if v == "true" || v == "1" || v == "yes" {
|
|
return true
|
|
}
|
|
if v == "false" || v == "0" || v == "no" {
|
|
return false
|
|
}
|
|
}
|
|
return defaultValue
|
|
}
|