186 lines
4.6 KiB
Go
186 lines
4.6 KiB
Go
package config
|
|
|
|
import (
|
|
"bufio"
|
|
"github.com/spf13/viper"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type Config struct {
|
|
Http struct {
|
|
IP string
|
|
Port int
|
|
}
|
|
BasicAuthUser string `mapstructure:"basic_auth_user"`
|
|
BasicAuthPassword string `mapstructure:"basic_auth_password"`
|
|
FrontendPath string `mapstructure:"frontend_path"`
|
|
Log struct {
|
|
Level string
|
|
LogPath string `mapstructure:"logPath"`
|
|
} `mapstructure:"log"`
|
|
Chat struct {
|
|
Model string `mapstructure:"model"`
|
|
MaxTokens int `mapstructure:"max_tokens"`
|
|
Temperature float32 `mapstructure:"temperature"`
|
|
TopP float32 `mapstructure:"top_p"`
|
|
PresencePenalty float32 `mapstructure:"presence_penalty"`
|
|
FrequencyPenalty float32 `mapstructure:"frequency_penalty"`
|
|
BotDesc string `mapstructure:"bot_desc"`
|
|
MinResponseTokens int `mapstructure:"min_response_tokens"`
|
|
ContextTTL int `mapstructure:"context_ttl"`
|
|
ContextLen int `mapstructure:"context_len"`
|
|
}
|
|
DependOn struct {
|
|
AiChatService struct {
|
|
Address string
|
|
AccessToken string
|
|
} `mapstructure:"ai-chat-service"`
|
|
}
|
|
}
|
|
|
|
var conf *Config
|
|
|
|
func InitConfig(filePath string, typ ...string) {
|
|
loadProjectDotEnv(filePath)
|
|
v := viper.New()
|
|
v.SetConfigFile(filePath)
|
|
if len(typ) > 0 {
|
|
v.SetConfigType(typ[0])
|
|
}
|
|
err := v.ReadInConfig()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
conf = &Config{}
|
|
err = v.Unmarshal(conf)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
normalizeConfig(conf)
|
|
overrideConfigFromEnv(conf)
|
|
|
|
}
|
|
|
|
func GetConfig() *Config {
|
|
return conf
|
|
}
|
|
|
|
func normalizeConfig(conf *Config) {
|
|
if conf.Http.IP == "" {
|
|
conf.Http.IP = "0.0.0.0"
|
|
}
|
|
if conf.Http.Port == 0 {
|
|
conf.Http.Port = 7080
|
|
}
|
|
if conf.FrontendPath == "" {
|
|
conf.FrontendPath = "www"
|
|
}
|
|
if conf.Chat.Model == "" {
|
|
conf.Chat.Model = "kimi-k2.5"
|
|
}
|
|
if conf.Chat.MaxTokens == 0 {
|
|
conf.Chat.MaxTokens = 4096
|
|
}
|
|
if conf.Chat.Temperature == 0 {
|
|
conf.Chat.Temperature = 1.0
|
|
}
|
|
if conf.Chat.TopP == 0 {
|
|
conf.Chat.TopP = 1.0
|
|
}
|
|
if conf.Chat.MinResponseTokens == 0 {
|
|
conf.Chat.MinResponseTokens = 600
|
|
}
|
|
if conf.DependOn.AiChatService.Address == "" {
|
|
conf.DependOn.AiChatService.Address = "localhost:50055"
|
|
}
|
|
}
|
|
|
|
func overrideConfigFromEnv(conf *Config) {
|
|
overrideString(&conf.Http.IP, os.Getenv("SERVER_HOST"))
|
|
overrideInt(&conf.Http.Port, os.Getenv("SERVER_PORT"))
|
|
overrideString(&conf.BasicAuthUser, os.Getenv("BASIC_AUTH_USER"))
|
|
overrideString(&conf.BasicAuthPassword, os.Getenv("BASIC_AUTH_PASSWORD"))
|
|
overrideString(&conf.FrontendPath, os.Getenv("FRONTEND_PATH"))
|
|
overrideString(&conf.Chat.Model, os.Getenv("OPENAI_MODEL"))
|
|
overrideInt(&conf.Chat.MaxTokens, os.Getenv("OPENAI_MAX_TOKENS"))
|
|
overrideScaledFloat32(&conf.Chat.Temperature, os.Getenv("OPENAI_TEMPERATURE"))
|
|
overrideScaledFloat32(&conf.Chat.PresencePenalty, os.Getenv("OPENAI_PRESENCE_PENALTY"))
|
|
overrideScaledFloat32(&conf.Chat.FrequencyPenalty, os.Getenv("OPENAI_FREQUENCY_PENALTY"))
|
|
overrideInt(&conf.Chat.MinResponseTokens, os.Getenv("CHAT_MIN_RESPONSE_TOKENS"))
|
|
overrideString(&conf.DependOn.AiChatService.Address, firstNonEmpty(
|
|
os.Getenv("AI_CHAT_SERVICE_ADDRESS"),
|
|
os.Getenv("AI_CHAT_SERVICE_ADDR"),
|
|
))
|
|
overrideString(&conf.DependOn.AiChatService.AccessToken, os.Getenv("AI_CHAT_SERVICE_ACCESS_TOKEN"))
|
|
}
|
|
|
|
func loadProjectDotEnv(configFilePath string) {
|
|
projectRoot := filepath.Dir(filepath.Dir(configFilePath))
|
|
envPath := filepath.Join(projectRoot, ".env")
|
|
file, err := os.Open(envPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
key, value, ok := strings.Cut(line, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
key = strings.TrimSpace(key)
|
|
value = strings.TrimSpace(value)
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if _, exists := os.LookupEnv(key); exists {
|
|
continue
|
|
}
|
|
_ = os.Setenv(key, value)
|
|
}
|
|
}
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return value
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func overrideString(target *string, value string) {
|
|
if strings.TrimSpace(value) != "" {
|
|
*target = strings.TrimSpace(value)
|
|
}
|
|
}
|
|
|
|
func overrideInt(target *int, value string) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return
|
|
}
|
|
if parsed, err := strconv.Atoi(value); err == nil {
|
|
*target = parsed
|
|
}
|
|
}
|
|
|
|
func overrideScaledFloat32(target *float32, value string) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return
|
|
}
|
|
if parsed, err := strconv.ParseFloat(value, 32); err == nil {
|
|
*target = float32(parsed / 100.0)
|
|
}
|
|
}
|