package llm import ( "errors" "fmt" "strings" "sync" "time" "aichat/config" ark "github.com/volcengine/volcengine-go-sdk/service/arkruntime" ) type Profile struct { Config config.OpenAIConfig Client *ark.Client } type State struct { mu sync.RWMutex profiles map[string]*Profile order []string activeName string } type ListResponse struct { Active string `json:"active"` Profiles []config.OpenAIConfig `json:"profiles"` } func NewState(configs []config.OpenAIConfig) (*State, error) { state := &State{ profiles: make(map[string]*Profile, len(configs)), order: make([]string, 0, len(configs)), } for _, item := range configs { if strings.TrimSpace(item.Name) == "" { return nil, errors.New("openai.name 不能为空") } if strings.TrimSpace(item.APIKey) == "" { return nil, fmt.Errorf("openai.%s.api_key 未配置,也未设置环境变量 ARK_API_KEY", item.Name) } if strings.TrimSpace(item.Model) == "" { return nil, fmt.Errorf("openai.%s.model 未配置", item.Name) } if strings.TrimSpace(item.BaseURL) == "" { return nil, fmt.Errorf("openai.%s.base_url 未配置", item.Name) } if item.Timeout <= 0 { return nil, fmt.Errorf("openai.%s.timeout 必须大于 0", item.Name) } if _, ok := state.profiles[item.Name]; ok { return nil, fmt.Errorf("openai 配置名称重复: %s", item.Name) } state.profiles[item.Name] = &Profile{ Config: item, Client: ark.NewClientWithApiKey( item.APIKey, ark.WithBaseUrl(item.BaseURL), ark.WithTimeout(time.Duration(item.Timeout)*time.Second), ), } state.order = append(state.order, item.Name) if item.Active && state.activeName == "" { state.activeName = item.Name } } if len(state.order) == 0 { return nil, errors.New("openai 配置不能为空") } if state.activeName == "" { state.activeName = state.order[0] } return state, nil } func (s *State) ActiveProfile() *Profile { s.mu.RLock() defer s.mu.RUnlock() return s.profiles[s.activeName] } func (s *State) GetProfile(name string) (*Profile, error) { s.mu.RLock() defer s.mu.RUnlock() if strings.TrimSpace(name) == "" { return s.profiles[s.activeName], nil } profile, ok := s.profiles[strings.TrimSpace(name)] if !ok { return nil, fmt.Errorf("OpenAI 配置不存在: %s", name) } return profile, nil } func (s *State) SwitchActive(name string) (*Profile, error) { name = strings.TrimSpace(name) if name == "" { return nil, errors.New("OpenAI 配置名称不能为空") } s.mu.Lock() defer s.mu.Unlock() profile, ok := s.profiles[name] if !ok { return nil, fmt.Errorf("OpenAI 配置不存在: %s", name) } s.activeName = name return profile, nil } func (s *State) ListProfiles() ListResponse { s.mu.RLock() defer s.mu.RUnlock() profiles := make([]config.OpenAIConfig, 0, len(s.order)) for _, name := range s.order { profile := s.profiles[name] cfg := profile.Config cfg.APIKey = "" cfg.Active = name == s.activeName profiles = append(profiles, cfg) } return ListResponse{Active: s.activeName, Profiles: profiles} } func PublicConfig(profile *Profile, active bool) config.OpenAIConfig { cfg := profile.Config cfg.APIKey = "" cfg.Active = active return cfg }