yaml_integration.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. package model
  2. import (
  3. "os"
  4. "strings"
  5. "sync"
  6. "time"
  7. "github.com/labring/aiproxy/core/common/config"
  8. "github.com/labring/aiproxy/core/relay/mode"
  9. log "github.com/sirupsen/logrus"
  10. "gopkg.in/yaml.v3"
  11. )
  12. // ChannelItem wraps Channel for YAML configuration
  13. // Adds TypeName field for human-readable channel type specification
  14. type ChannelItem struct {
  15. Channel ` yaml:",inline"` // Embed Channel to inherit all fields
  16. TypeName string `yaml:"type_name,omitempty"` // Alternative to Type (e.g., "openai", "claude")
  17. }
  18. // GetChannelType returns the channel type, converting from TypeName if Type is not set
  19. func (c *ChannelItem) GetChannelType() ChannelType {
  20. if c.Type != 0 {
  21. return c.Type
  22. }
  23. // Convert TypeName to Type
  24. return ChannelType(ChannelTypeNameToType(c.TypeName))
  25. }
  26. // ModelConfigItem wraps ModelConfig for YAML configuration
  27. // Adds TypeName field for human-readable model type specification
  28. type ModelConfigItem struct {
  29. ModelConfig ` yaml:",inline"` // Embed ModelConfig to inherit all fields
  30. TypeName string `yaml:"type_name,omitempty"` // Alternative to Type (e.g., "chat", "embedding")
  31. }
  32. // GetModelType returns the model type, converting from TypeName if Type is not set
  33. func (m *ModelConfigItem) GetModelType() mode.Mode {
  34. if m.Type != 0 {
  35. return m.Type
  36. }
  37. // Convert TypeName to Type
  38. return ModelTypeNameToType(m.TypeName)
  39. }
  40. // ChannelTypeNameToType converts a channel type name to its numeric type
  41. func ChannelTypeNameToType(typeName string) int {
  42. typeName = strings.ToLower(strings.TrimSpace(typeName))
  43. typeMap := map[string]int{
  44. "openai": 1,
  45. "azure": 3,
  46. "azure2": 4,
  47. "google gemini (openai)": 12,
  48. "gemini-openai": 12,
  49. "baidu v2": 13,
  50. "baiduv2": 13,
  51. "anthropic": 14,
  52. "claude": 14,
  53. "baidu": 15,
  54. "zhipu": 16,
  55. "ali": 17,
  56. "aliyun": 17,
  57. "xunfei": 18,
  58. "ai360": 19,
  59. "360": 19,
  60. "openrouter": 20,
  61. "tencent": 23,
  62. "google gemini": 24,
  63. "gemini": 24,
  64. "moonshot": 25,
  65. "baichuan": 26,
  66. "minimax": 27,
  67. "mistral": 28,
  68. "groq": 29,
  69. "ollama": 30,
  70. "lingyiwanwu": 31,
  71. "stepfun": 32,
  72. "aws": 33,
  73. "coze": 34,
  74. "cohere": 35,
  75. "deepseek": 36,
  76. "cloudflare": 37,
  77. "doubao": 40,
  78. "novita": 41,
  79. "vertexai": 42,
  80. "vertex": 42,
  81. "siliconflow": 43,
  82. "doubao audio": 44,
  83. "doubaoaudio": 44,
  84. "xai": 45,
  85. "doc2x": 46,
  86. "jina": 47,
  87. "huggingface text-embeddings-inference": 48,
  88. "text-embeddings-inference": 48,
  89. "tei": 48,
  90. "qianfan": 49,
  91. "sangfor aicp": 50,
  92. "aicp": 50,
  93. "streamlake": 51,
  94. "zhipu coding": 52,
  95. "zhipucoding": 52,
  96. }
  97. if typ, ok := typeMap[typeName]; ok {
  98. return typ
  99. }
  100. return 0
  101. }
  102. // ModelTypeNameToType converts a model type name to its numeric type
  103. func ModelTypeNameToType(typeName string) mode.Mode {
  104. typeName = strings.ToLower(strings.TrimSpace(typeName))
  105. typeMap := map[string]mode.Mode{
  106. "chat": mode.ChatCompletions,
  107. "chatcompletion": mode.ChatCompletions,
  108. "chatcompletions": mode.ChatCompletions,
  109. "completion": mode.Completions,
  110. "completions": mode.Completions,
  111. "embedding": mode.Embeddings,
  112. "embeddings": mode.Embeddings,
  113. "moderation": mode.Moderations,
  114. "moderations": mode.Moderations,
  115. "image": mode.ImagesGenerations,
  116. "imagegeneration": mode.ImagesGenerations,
  117. "imagegenerations": mode.ImagesGenerations,
  118. "imageedit": mode.ImagesEdits,
  119. "imageedits": mode.ImagesEdits,
  120. "audio": mode.AudioSpeech,
  121. "audiospeech": mode.AudioSpeech,
  122. "speech": mode.AudioSpeech,
  123. "audiotranscription": mode.AudioTranscription,
  124. "transcription": mode.AudioTranscription,
  125. "audiotranslation": mode.AudioTranslation,
  126. "translation": mode.AudioTranslation,
  127. "rerank": mode.Rerank,
  128. "parsepdf": mode.ParsePdf,
  129. "pdf": mode.ParsePdf,
  130. "anthropic": mode.Anthropic,
  131. "videogeneration": mode.VideoGenerationsJobs,
  132. "videogenerationsjobs": mode.VideoGenerationsJobs,
  133. "videogenerationsgetjobs": mode.VideoGenerationsGetJobs,
  134. "videogenerationscontent": mode.VideoGenerationsContent,
  135. "responses": mode.Responses,
  136. "responsesget": mode.ResponsesGet,
  137. "responsesdelete": mode.ResponsesDelete,
  138. "responsescancel": mode.ResponsesCancel,
  139. "responsesinputitems": mode.ResponsesInputItems,
  140. }
  141. if typ, ok := typeMap[typeName]; ok {
  142. return typ
  143. }
  144. return mode.Unknown
  145. }
  146. // YAMLConfig represents the complete configuration with proper types
  147. type YAMLConfig struct {
  148. Channels []ChannelItem `yaml:"channels,omitempty"`
  149. ModelConfigs []ModelConfigItem `yaml:"modelconfigs,omitempty"`
  150. Options map[string]string `yaml:"options,omitempty"`
  151. }
  152. var (
  153. yamlConfigCache *YAMLConfig
  154. yamlConfigCacheTime time.Time
  155. yamlConfigCacheMutex sync.RWMutex
  156. yamlConfigCacheTTL = 60 * time.Second
  157. )
  158. // LoadYAMLConfig loads and parses YAML configuration with proper types
  159. // Uses a 60-second cache with double-check locking for performance
  160. func LoadYAMLConfig() *YAMLConfig {
  161. yamlConfigCacheMutex.RLock()
  162. if yamlConfigCache != nil && time.Since(yamlConfigCacheTime) < yamlConfigCacheTTL {
  163. cache := yamlConfigCache
  164. yamlConfigCacheMutex.RUnlock()
  165. return cache
  166. }
  167. yamlConfigCacheMutex.RUnlock()
  168. // Acquire write lock to update cache
  169. yamlConfigCacheMutex.Lock()
  170. defer yamlConfigCacheMutex.Unlock()
  171. // Double check: another goroutine might have updated the cache
  172. if yamlConfigCache != nil && time.Since(yamlConfigCacheTime) < yamlConfigCacheTTL {
  173. return yamlConfigCache
  174. }
  175. // Load raw YAML data from file
  176. data, err := config.LoadYAMLConfigData()
  177. if err != nil {
  178. if os.IsNotExist(err) {
  179. yamlConfigCache = nil
  180. yamlConfigCacheTime = time.Now()
  181. return nil
  182. }
  183. log.Errorf("load config: %v", err)
  184. yamlConfigCache = nil
  185. yamlConfigCacheTime = time.Now()
  186. return nil
  187. }
  188. // Parse YAML directly into our types
  189. var yamlConfig YAMLConfig
  190. //nolint:musttag
  191. if err := yaml.Unmarshal(data, &yamlConfig); err != nil {
  192. log.Errorf("unmarshal config: %v", err)
  193. yamlConfigCache = nil
  194. yamlConfigCacheTime = time.Now()
  195. return nil
  196. }
  197. // Update cache
  198. yamlConfigCache = &yamlConfig
  199. yamlConfigCacheTime = time.Now()
  200. return yamlConfigCache
  201. }
  202. // applyYAMLConfigToModelConfigCache applies YAML model configs to the model config cache
  203. // Creates a wrapper cache that checks YAML first, then falls back to database cache
  204. func applyYAMLConfigToModelConfigCache(
  205. cache ModelConfigCache,
  206. ) ModelConfigCache {
  207. yamlConfig := LoadYAMLConfig()
  208. if yamlConfig == nil || len(yamlConfig.ModelConfigs) == 0 {
  209. // No YAML model configs, use existing cache from database
  210. return cache
  211. }
  212. // Build YAML model config map
  213. yamlModelConfigMap := make(map[string]ModelConfig)
  214. for i := range yamlConfig.ModelConfigs {
  215. modelConfigItem := &yamlConfig.ModelConfigs[i]
  216. // Convert ModelConfigItem to ModelConfig
  217. modelConfig := modelConfigItem.ModelConfig
  218. // Convert TypeName to Type if Type is not set
  219. if modelConfigItem.TypeName != "" && modelConfig.Type == 0 {
  220. modelConfig.Type = modelConfigItem.GetModelType()
  221. }
  222. if modelConfig.Model != "" {
  223. yamlModelConfigMap[modelConfig.Model] = modelConfig
  224. }
  225. }
  226. log.Infof("loaded %d model configs from config", len(yamlModelConfigMap))
  227. // Create wrapper cache: YAML configs override database configs
  228. wrappedCache := &yamlModelConfigCache{
  229. yamlConfigs: yamlModelConfigMap,
  230. dbCache: cache,
  231. }
  232. return wrappedCache
  233. }
  234. // yamlModelConfigCache wraps database cache with YAML overrides
  235. // When looking up a model config:
  236. // 1. First check YAML configs (high priority)
  237. // 2. If not found, fall back to database cache (low priority)
  238. var _ ModelConfigCache = (*yamlModelConfigCache)(nil)
  239. type yamlModelConfigCache struct {
  240. yamlConfigs map[string]ModelConfig
  241. dbCache ModelConfigCache
  242. }
  243. func (y *yamlModelConfigCache) GetModelConfig(model string) (ModelConfig, bool) {
  244. // First check YAML configs
  245. if config, ok := y.yamlConfigs[model]; ok {
  246. return config, true
  247. }
  248. // Fall back to database cache
  249. return y.dbCache.GetModelConfig(model)
  250. }
  251. // NewConfigChannels merges YAML channels with database channels
  252. // YAML channels are assigned negative IDs to distinguish them from database channels
  253. // Note: YAML channels are NOT persisted to the database
  254. func NewConfigChannels(yamlConfig *YAMLConfig, status int) []*Channel {
  255. if yamlConfig == nil || len(yamlConfig.Channels) == 0 {
  256. return nil
  257. }
  258. newChannels := make([]*Channel, 0, len(yamlConfig.Channels))
  259. // Start negative IDs from -1000 to avoid conflicts
  260. nextNegativeID := -1
  261. // Add all YAML channels with negative IDs (they don't override database channels)
  262. for _, yamlChannelItem := range yamlConfig.Channels {
  263. // Convert ChannelItem to Channel
  264. channel := &yamlChannelItem.Channel
  265. if status != 0 && channel.Status != status {
  266. continue
  267. }
  268. // Convert TypeName to Type if Type is not set
  269. if yamlChannelItem.TypeName != "" && channel.Type == 0 {
  270. channel.Type = yamlChannelItem.GetChannelType()
  271. }
  272. // Assign negative ID to distinguish from database channels
  273. channel.ID = nextNegativeID
  274. nextNegativeID--
  275. initializeChannelModels(channel)
  276. initializeChannelModelMapping(channel)
  277. newChannels = append(newChannels, channel)
  278. }
  279. return newChannels
  280. }