first commit

This commit is contained in:
Beyhan Ogur
2026-05-11 15:08:50 +03:00
commit a408821410
47 changed files with 4670 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
// Package main is the entry point for the LLM Gateway / Proxy service.
//
// @title LLM Gateway API
// @version 1.0
// @description OpenAI-compatible and Anthropic-compatible LLM proxy/gateway with Bifrost mapping.
// @termsOfService http://swagger.io/terms/
//
// @contact.name optoant
// @contact.email admin@optoant.local
//
// @license.name MIT
// @license.url https://opensource.org/licenses/MIT
//
// @host localhost:8000
// @BasePath /
//
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/gofiber/fiber/v3"
"github.com/joho/godotenv"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"optoant/config"
"optoant/handlers"
"optoant/internal/logger"
"optoant/models"
)
func main() {
// Load .env file if present (silently ignore if missing)
_ = godotenv.Load()
cfg := config.Load()
// Logger verbosity from LOG_LEVEL env (debug, info, warn)
logger.SetLevel(cfg.LogLevel)
// ------------------------------------------------------------------
// Database setup (GORM — PostgreSQL or SQLite based on DB_MODE)
// ------------------------------------------------------------------
var db *gorm.DB
if cfg.PostgresDSN != "" || cfg.DBMode == "sqlite" {
var dialector gorm.Dialector
dbEngine := "postgres"
switch cfg.DBMode {
case "sqlite":
dsn := cfg.DBPath
if dsn == "" {
dsn = "data/gateway.db"
}
if err := os.MkdirAll(filepath.Dir(dsn), 0755); err != nil {
log.Printf("⚠️ Failed to create DB directory: %v", err)
}
dialector = sqlite.Open(dsn)
dbEngine = "sqlite"
default:
dialector = postgres.Open(cfg.PostgresDSN)
}
var err error
db, err = gorm.Open(dialector, &gorm.Config{
Logger: gormlogger.Default.LogMode(gormlogger.Warn),
})
if err != nil {
log.Printf("⚠️ DB connection failed (continuing without DB): %v", err)
db = nil
} else {
if err := db.AutoMigrate(&models.RequestLog{}); err != nil {
log.Printf("⚠️ AutoMigrate failed: %v", err)
} else {
log.Printf("✅ Database connected and migrated (%s)", dbEngine)
}
}
} else {
log.Println("️ No POSTGRES_DSN/DATABASE_DSN set — running without database logging")
}
// ------------------------------------------------------------------
// Fiber v3 application
// ------------------------------------------------------------------
app := fiber.New(fiber.Config{
AppName: "LLM Gateway v1.0",
ServerHeader: "optoant-gateway",
})
// ------------------------------------------------------------------
// Swagger UI at /swagger/* — Fiber v3 native, no v2 dependency
// Swagger UI is loaded from CDN; spec is served from /swagger/swagger.json
// ------------------------------------------------------------------
app.Get("/swagger/swagger.json", func(c fiber.Ctx) error {
return c.SendFile("./docs/swagger.json")
})
app.Get("/swagger", func(c fiber.Ctx) error {
return c.Redirect().To("/swagger/")
})
app.Get("/swagger/*", func(c fiber.Ctx) error {
html := `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LLM Gateway — Swagger UI</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = () => {
SwaggerUIBundle({
url: "/swagger/swagger.json",
dom_id: "#swagger-ui",
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
layout: "StandaloneLayout",
deepLinking: true,
})
}
</script>
</body>
</html>`
return c.Type("html").SendString(html)
})
// ------------------------------------------------------------------
// Routes
// ------------------------------------------------------------------
// Health check
app.Get("/health", handlers.HealthHandler(cfg, db))
// OpenAI-compatible: forward all /v1/* methods to OPENAI_BACKEND
app.All("/v1/*", handlers.OpenAIHandler(cfg, db))
// Anthropic-compatible: forward all /anthropic/* to backend or Bifrost
app.All("/anthropic", handlers.AnthropicHandler(cfg, db))
app.All("/anthropic/*", handlers.AnthropicHandler(cfg, db))
// ------------------------------------------------------------------
// Start server
// ------------------------------------------------------------------
addr := fmt.Sprintf(":%s", cfg.Port)
log.Printf("🚀 LLM Gateway starting on %s", addr)
log.Printf(" Log level : %s", cfg.LogLevel)
log.Printf(" Upstream backend: %s", cfg.OpenAIBackend)
log.Printf(" OpenAI endpoint : /v1/* (direct passthrough)")
log.Printf(" Anthropic endpoint: /anthropic/* (Anthropic↔OpenAI transform)")
if db != nil {
log.Printf(" DB logging : enabled")
}
if err := app.Listen(addr); err != nil {
log.Fatalf("server error: %v", err)
os.Exit(1)
}
}