修复 LLM Provider 配置热更新问题
问题:在 /admin/llm/api 页面修改 AI 提供商配置后,配置没有立即生效,AI 依然使用旧的提供商工作 根本原因: - LLM Provider 配置在程序启动时加载到内存 (llm.State) - 管理后台修改配置时,只更新了数据库,内存配置未更新 - AI 继续使用内存中的旧配置 解决方案: 1. 为 llm.State 添加动态更新方法 (UpdateProvider/AddProvider/RemoveProvider) 2. 在 AI Service 中暴露配置更新接口,支持运行时重新加载 3. 在配置保存后自动触发内存配置重新加载 4. 创建新的 LLM Client 使新配置立即生效 关键特性: - 线程安全:使用 sync.RWMutex 保护并发访问 - 容错处理:重新加载失败不影响数据库更新 - 无需重启:配置修改后立即生效 - 完整测试:添加单元测试验证功能 修改文件: - internal/llm/state.go: 添加配置更新方法 - internal/ai/service.go: 添加配置重新加载接口 - internal/llmadmin/admin_llm_routes.go: 配置更新时触发重新加载 - internal/web/web.go: 传递 AI Service 到路由 - main.go: 连接组件 - internal/llm/state_test.go: 新增单元测试 - internal/web/map_tile_proxy_routes_test.go: 修复测试 测试: - ✅ 所有现有测试通过 - ✅ 新增测试覆盖核心功能 - ✅ 项目成功编译 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+13
-6
@@ -27,10 +27,17 @@ import (
|
||||
"meshtastic_mqtt_server/internal/webutil"
|
||||
)
|
||||
|
||||
func NewHTTPServer(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) *http.Server {
|
||||
// LLMProviderReloader is the interface for reloading LLM provider configuration
|
||||
type LLMProviderReloader interface {
|
||||
ReloadLLMProvider(config interface{}) error
|
||||
AddLLMProvider(config interface{}) error
|
||||
RemoveLLMProvider(name string) error
|
||||
}
|
||||
|
||||
func NewHTTPServer(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender, aiService LLMProviderReloader) *http.Server {
|
||||
return &http.Server{
|
||||
Addr: net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port)),
|
||||
Handler: NewRouter(cfg, consoleLog, store, sessions, mqttStatus, blocking, forwarder, settings, botSender),
|
||||
Handler: NewRouter(cfg, consoleLog, store, sessions, mqttStatus, blocking, forwarder, settings, botSender, aiService),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +67,7 @@ func ServeUnixSocket(server *http.Server, socketPath string) error {
|
||||
return server.Serve(listener)
|
||||
}
|
||||
|
||||
func NewRouter(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) *gin.Engine {
|
||||
func NewRouter(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender, aiService LLMProviderReloader) *gin.Engine {
|
||||
r := gin.New()
|
||||
if consoleLog {
|
||||
r.Use(gin.Logger(), gin.Recovery())
|
||||
@@ -69,7 +76,7 @@ func NewRouter(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store,
|
||||
}
|
||||
api := r.Group("/api")
|
||||
registerAPIRoutes(api, store, cfg.MapTileCacheDir)
|
||||
registerAdminRoutes(api.Group("/admin"), store, sessions, mqttStatus, blocking, forwarder, settings, botSender)
|
||||
registerAdminRoutes(api.Group("/admin"), store, sessions, mqttStatus, blocking, forwarder, settings, botSender, aiService)
|
||||
registerStaticRoutes(r, cfg.StaticDir)
|
||||
return r
|
||||
}
|
||||
@@ -163,7 +170,7 @@ func registerAPIRoutes(r gin.IRouter, store *storepkg.Store, mapTileCacheDir str
|
||||
})
|
||||
}
|
||||
|
||||
func registerAdminRoutes(r gin.IRouter, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender) {
|
||||
func registerAdminRoutes(r gin.IRouter, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender, aiService LLMProviderReloader) {
|
||||
type loginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
@@ -230,7 +237,7 @@ func registerAdminRoutes(r gin.IRouter, store *storepkg.Store, sessions *auth.Ma
|
||||
mappkg.RegisterAdminRoutes(protected, store)
|
||||
helppkg.RegisterAdminRoutes(protected, store)
|
||||
botpkg.RegisterRoutes(protected, store, botSender)
|
||||
llmadminpkg.RegisterRoutes(protected, store)
|
||||
llmadminpkg.RegisterRoutes(protected, store, aiService)
|
||||
protected.GET("/me", func(c *gin.Context) {
|
||||
claims := c.MustGet("admin_claims").(*auth.SessionClaims)
|
||||
c.JSON(http.StatusOK, gin.H{"user": auth.AdminUserDTO{Username: claims.Username, Role: claims.Role}})
|
||||
|
||||
Reference in New Issue
Block a user