initial upload
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
// Dynagate config manager
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ModelConfig struct {
|
||||
Model string
|
||||
Key string
|
||||
Endpoint string
|
||||
Extra string
|
||||
}
|
||||
|
||||
type ConfigManager struct {
|
||||
filePath string
|
||||
mu sync.RWMutex
|
||||
configs []ModelConfig
|
||||
uniqueModels []string
|
||||
lastModTime time.Time
|
||||
lastSize int64
|
||||
}
|
||||
|
||||
func NewConfigManager(filePath string) *ConfigManager {
|
||||
cm := &ConfigManager{
|
||||
filePath: filePath,
|
||||
}
|
||||
if err := cm.Load(); err != nil {
|
||||
log.Printf("Initial configuration load failed or file not found (will retry on change): %v", err)
|
||||
}
|
||||
return cm
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) Load() error {
|
||||
file, err := os.Open(cm.filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reader := csv.NewReader(file)
|
||||
reader.FieldsPerRecord = -1
|
||||
rows, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var configs []ModelConfig
|
||||
if len(rows) == 0 {
|
||||
cm.update(configs, stat.ModTime(), stat.Size())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Detect header row
|
||||
hasHeader := false
|
||||
firstRow := rows[0]
|
||||
for _, col := range firstRow {
|
||||
colLower := strings.ToLower(strings.TrimSpace(col))
|
||||
if colLower == "model" || colLower == "key" || colLower == "endpoint" {
|
||||
hasHeader = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var startIdx int
|
||||
modelIdx, keyIdx, endpointIdx, extraIdx := 0, 1, 2, -1
|
||||
if hasHeader {
|
||||
startIdx = 1
|
||||
modelIdx, keyIdx, endpointIdx, extraIdx = -1, -1, -1, -1
|
||||
for i, col := range firstRow {
|
||||
colLower := strings.ToLower(strings.TrimSpace(col))
|
||||
switch colLower {
|
||||
case "model":
|
||||
modelIdx = i
|
||||
case "key":
|
||||
keyIdx = i
|
||||
case "endpoint":
|
||||
endpointIdx = i
|
||||
case "extra":
|
||||
extraIdx = i
|
||||
}
|
||||
}
|
||||
// Fallbacks if columns are not named exactly
|
||||
if modelIdx == -1 {
|
||||
modelIdx = 0
|
||||
}
|
||||
if keyIdx == -1 {
|
||||
keyIdx = 1
|
||||
}
|
||||
if endpointIdx == -1 {
|
||||
endpointIdx = 2
|
||||
}
|
||||
} else {
|
||||
startIdx = 0
|
||||
}
|
||||
|
||||
for i := startIdx; i < len(rows); i++ {
|
||||
row := rows[i]
|
||||
maxIdx := modelIdx
|
||||
if keyIdx > maxIdx {
|
||||
maxIdx = keyIdx
|
||||
}
|
||||
if endpointIdx > maxIdx {
|
||||
maxIdx = endpointIdx
|
||||
}
|
||||
if len(row) <= maxIdx {
|
||||
continue
|
||||
}
|
||||
|
||||
m := strings.TrimSpace(row[modelIdx])
|
||||
k := strings.TrimSpace(row[keyIdx])
|
||||
ep := strings.TrimSpace(row[endpointIdx])
|
||||
|
||||
if m == "" || k == "" || ep == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
extra := ""
|
||||
if hasHeader {
|
||||
if extraIdx != -1 && len(row) > extraIdx {
|
||||
extra = strings.TrimSpace(row[extraIdx])
|
||||
}
|
||||
} else {
|
||||
if len(row) > 3 {
|
||||
extra = strings.TrimSpace(row[3])
|
||||
}
|
||||
}
|
||||
|
||||
configs = append(configs, ModelConfig{
|
||||
Model: m,
|
||||
Key: k,
|
||||
Endpoint: ep,
|
||||
Extra: extra,
|
||||
})
|
||||
}
|
||||
|
||||
cm.update(configs, stat.ModTime(), stat.Size())
|
||||
log.Printf("Loaded %d model configurations from %s", len(configs), cm.filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) update(configs []ModelConfig, modTime time.Time, size int64) {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
cm.configs = configs
|
||||
cm.lastModTime = modTime
|
||||
cm.lastSize = size
|
||||
|
||||
var uniqueModels []string
|
||||
seen := make(map[string]bool)
|
||||
for _, cfg := range configs {
|
||||
if !seen[cfg.Model] {
|
||||
seen[cfg.Model] = true
|
||||
uniqueModels = append(uniqueModels, cfg.Model)
|
||||
}
|
||||
}
|
||||
cm.uniqueModels = uniqueModels
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) GetConfigs() []ModelConfig {
|
||||
cm.mu.RLock()
|
||||
defer cm.mu.RUnlock()
|
||||
res := make([]ModelConfig, len(cm.configs))
|
||||
copy(res, cm.configs)
|
||||
return res
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) GetUniqueModels() []string {
|
||||
cm.mu.RLock()
|
||||
defer cm.mu.RUnlock()
|
||||
res := make([]string, len(cm.uniqueModels))
|
||||
copy(res, cm.uniqueModels)
|
||||
return res
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) Watch(ctx context.Context) {
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
stat, err := os.Stat(cm.filePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
cm.mu.RLock()
|
||||
lastMod := cm.lastModTime
|
||||
lastSz := cm.lastSize
|
||||
cm.mu.RUnlock()
|
||||
|
||||
if stat.ModTime().After(lastMod) || stat.Size() != lastSz {
|
||||
log.Printf("Detected changes in %s, reloading...", cm.filePath)
|
||||
if err := cm.Load(); err != nil {
|
||||
log.Printf("Error reloading configuration: %v", err)
|
||||
} else {
|
||||
log.Printf("Configuration reloaded successfully")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user