refactor: 认证系统重构 + DevTools CLI 重写 + 文档全面更新
- auth: Login 简化为管理员始终通过 .env 验证,GetProfile 修正 admin DB 查询 - devtools: .sh/.bat 同步重写为完整 CLI (start/stop/status/logs/build/db:*) - docs: 新增 devtools.md,重写 Deploy.md (三种方式+Windows说明),更新 README/gateway-api - voice-service: DashScope 实时流式 STT 支持 - gateway: Phase 6 多模型配置 + 多端客户端管理 + WebSocket 增强 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -11,20 +11,44 @@ type Config struct {
|
||||
MaxAudioSize int64 // 字节
|
||||
|
||||
// DashScope STT 配置
|
||||
DashScopeAPIKey string
|
||||
DashScopeModel string
|
||||
DashScopeAPIKey string
|
||||
DashScopeModel string // 离线/非实时 ASR 模型
|
||||
DashScopeSTTRealtime string // 实时 ASR 模型
|
||||
}
|
||||
|
||||
// Load 从环境变量加载配置
|
||||
// Load 从 models.json 和环境变量加载配置。
|
||||
// models.json 优先级高于环境变量。
|
||||
func Load() *Config {
|
||||
// 从 models.json 加载 ASR 配置
|
||||
modelsAPIKey, modelsOffline, modelsRealtime := LoadModelsConfig()
|
||||
|
||||
// .env / 环境变量作为回退
|
||||
envAPIKey := getEnv("DASHSCOPE_API_KEY", "")
|
||||
envModel := getEnv("DASHSCOPE_STT_MODEL", "qwen3-asr-flash-2026-02-10")
|
||||
envRealtime := getEnv("DASHSCOPE_STT_REALTIME_MODEL", "qwen3-asr-flash-realtime")
|
||||
|
||||
apiKey := modelsAPIKey
|
||||
if apiKey == "" {
|
||||
apiKey = envAPIKey
|
||||
}
|
||||
offlineModel := modelsOffline
|
||||
if offlineModel == "" {
|
||||
offlineModel = envModel
|
||||
}
|
||||
realtimeModel := modelsRealtime
|
||||
if realtimeModel == "" {
|
||||
realtimeModel = envRealtime
|
||||
}
|
||||
|
||||
return &Config{
|
||||
Port: getEnv("PORT", "8093"),
|
||||
WhisperBinary: getEnv("WHISPER_BINARY", "./whisper.cpp/main"),
|
||||
WhisperModel: getEnv("WHISPER_MODEL", "./whisper.cpp/models/ggml-small.bin"),
|
||||
WhisperLanguage: getEnv("WHISPER_LANGUAGE", "zh"),
|
||||
MaxAudioSize: 10 * 1024 * 1024, // 10MB
|
||||
DashScopeAPIKey: getEnv("DASHSCOPE_API_KEY", ""),
|
||||
DashScopeModel: getEnv("DASHSCOPE_STT_MODEL", "gummy-chat-v1"),
|
||||
Port: getEnv("PORT", "8093"),
|
||||
WhisperBinary: getEnv("WHISPER_BINARY", "./whisper.cpp/main"),
|
||||
WhisperModel: getEnv("WHISPER_MODEL", "./whisper.cpp/models/ggml-small.bin"),
|
||||
WhisperLanguage: getEnv("WHISPER_LANGUAGE", "zh"),
|
||||
MaxAudioSize: 10 * 1024 * 1024, // 10MB
|
||||
DashScopeAPIKey: apiKey,
|
||||
DashScopeModel: offlineModel,
|
||||
DashScopeSTTRealtime: realtimeModel,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ModelsJSON 映射 models.json 文件结构(仅提取语音相关字段)。
|
||||
type ModelsJSON struct {
|
||||
Providers map[string]ModelsProvider `json:"providers"`
|
||||
Models map[string]ModelsModel `json:"models"`
|
||||
Routing map[string]ModelsRouting `json:"routing"`
|
||||
}
|
||||
|
||||
type ModelsProvider struct {
|
||||
Name string `json:"name"`
|
||||
BaseURL string `json:"base_url"`
|
||||
APIKey string `json:"api_key"`
|
||||
}
|
||||
|
||||
type ModelsModel struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
}
|
||||
|
||||
type ModelsRouting struct {
|
||||
Purpose string `json:"purpose"`
|
||||
FallbackChain []string `json:"fallback_chain"`
|
||||
}
|
||||
|
||||
// LoadModelsConfig 从 backend/models.json 加载模型配置。
|
||||
// 返回 provider API key 和 ASR 模型名称。如果文件不存在则返回零值。
|
||||
func LoadModelsConfig() (apiKey string, offlineModel string, realtimeModel string) {
|
||||
// 尝试多个可能的路径
|
||||
candidates := []string{
|
||||
"models.json",
|
||||
"../models.json",
|
||||
"../../models.json",
|
||||
filepath.Join("..", "models.json"),
|
||||
}
|
||||
|
||||
var data []byte
|
||||
var err error
|
||||
for _, p := range candidates {
|
||||
data, err = os.ReadFile(p)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var cfg ModelsJSON
|
||||
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 从 speech_recognition_offline 路由取离线模型
|
||||
if r, ok := cfg.Routing["speech_recognition_offline"]; ok && len(r.FallbackChain) > 0 {
|
||||
modelID := r.FallbackChain[0]
|
||||
if m, ok := cfg.Models[modelID]; ok {
|
||||
offlineModel = m.Name
|
||||
if m.Name == "" {
|
||||
offlineModel = m.ID
|
||||
}
|
||||
// 从 provider 取 API key
|
||||
if p, ok := cfg.Providers[m.Provider]; ok && apiKey == "" {
|
||||
apiKey = p.APIKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从 speech_recognition 路由取实时模型
|
||||
if r, ok := cfg.Routing["speech_recognition"]; ok && len(r.FallbackChain) > 0 {
|
||||
modelID := r.FallbackChain[0]
|
||||
if m, ok := cfg.Models[modelID]; ok {
|
||||
realtimeModel = m.Name
|
||||
if realtimeModel == "" {
|
||||
realtimeModel = m.ID
|
||||
}
|
||||
// 从 provider 取 API key
|
||||
if p, ok := cfg.Providers[m.Provider]; ok && apiKey == "" {
|
||||
apiKey = p.APIKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 routing 里没有,尝试从所有 models 中找带有 ASR tag 的模型
|
||||
if offlineModel == "" {
|
||||
for _, m := range cfg.Models {
|
||||
if m.Provider == "dashscope" && m.Name != "" {
|
||||
if offlineModel == "" {
|
||||
offlineModel = m.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从 provider 取 API key (兜底)
|
||||
if apiKey == "" {
|
||||
if p, ok := cfg.Providers["dashscope"]; ok {
|
||||
apiKey = p.APIKey
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user