修复 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:
@@ -258,3 +258,69 @@ func (s *Service) Stop() {
|
||||
func (s *Service) Enabled() bool {
|
||||
return s.enabled
|
||||
}
|
||||
|
||||
// ReloadLLMProvider reloads a specific LLM provider configuration
|
||||
func (s *Service) ReloadLLMProvider(config interface{}) error {
|
||||
if !s.enabled || s.LLMState == nil {
|
||||
return nil
|
||||
}
|
||||
providerConfig, err := convertToProviderConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.LLMState.UpdateProvider(providerConfig)
|
||||
}
|
||||
|
||||
// AddLLMProvider adds a new LLM provider
|
||||
func (s *Service) AddLLMProvider(config interface{}) error {
|
||||
if !s.enabled || s.LLMState == nil {
|
||||
return nil
|
||||
}
|
||||
providerConfig, err := convertToProviderConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.LLMState.AddProvider(providerConfig)
|
||||
}
|
||||
|
||||
// RemoveLLMProvider removes an LLM provider
|
||||
func (s *Service) RemoveLLMProvider(name string) error {
|
||||
if !s.enabled || s.LLMState == nil {
|
||||
return nil
|
||||
}
|
||||
return s.LLMState.RemoveProvider(name)
|
||||
}
|
||||
|
||||
// convertToProviderConfig converts a map to llm.ProviderConfig
|
||||
func convertToProviderConfig(config interface{}) (llm.ProviderConfig, error) {
|
||||
m, ok := config.(map[string]interface{})
|
||||
if !ok {
|
||||
return llm.ProviderConfig{}, fmt.Errorf("invalid config type: expected map[string]interface{}")
|
||||
}
|
||||
|
||||
pc := llm.ProviderConfig{}
|
||||
|
||||
if v, ok := m["Name"].(string); ok {
|
||||
pc.Name = v
|
||||
}
|
||||
if v, ok := m["Active"].(bool); ok {
|
||||
pc.Active = v
|
||||
}
|
||||
if v, ok := m["APIKey"].(string); ok {
|
||||
pc.APIKey = v
|
||||
}
|
||||
if v, ok := m["BaseURL"].(string); ok {
|
||||
pc.BaseURL = v
|
||||
}
|
||||
if v, ok := m["Model"].(string); ok {
|
||||
pc.Model = v
|
||||
}
|
||||
if v, ok := m["Timeout"].(int); ok {
|
||||
pc.Timeout = v
|
||||
}
|
||||
if v, ok := m["ContextWindowTokens"].(int); ok {
|
||||
pc.ContextWindowTokens = v
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user