Files
ttstd-infinite-canvas/backend/internal/app/channel_model_catalog_plugin.go

118 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"net/url"
"os"
"sort"
"strings"
"sync"
"yingce/backend/internal/provider"
"yingce/backend/internal/provider/bailian"
)
var pluginsInitOnce sync.Once
func initModelCatalogPlugins() {
pluginsInitOnce.Do(func() {
provider.Register(bailian.New())
})
}
// extendChannelModelCatalog 在已通过统一安全出站链路获取的标准目录上补充厂商条目。
// 插件不持有密钥也不自行发 HTTP,避免绕过 SSRF、请求头和响应大小边界。
func extendChannelModelCatalog(baseURL string, apiFormat string, headers []OutboundHeader, catalog []ChannelModelCatalogItem) []ChannelModelCatalogItem {
initModelCatalogPlugins()
discovery := provider.MatchProvider(baseURL, outboundHeadersMap(headers))
if discovery == nil {
return catalog
}
extra := discovery.AdditionalModels(provider.DiscoveryConfig{
BaseURL: baseURL,
APIFormat: apiFormat,
Headers: outboundHeadersMap(headers),
Region: modelCatalogRegion(baseURL),
})
if len(extra) == 0 {
return catalog
}
indexByID := make(map[string]int, len(catalog)+len(extra))
for index := range catalog {
indexByID[catalog[index].ID] = index
}
for _, model := range extra {
item := providerCatalogItem(model)
if item.ID == "" {
continue
}
if index, exists := indexByID[item.ID]; exists {
catalog[index] = enrichCatalogItem(catalog[index], item)
continue
}
indexByID[item.ID] = len(catalog)
catalog = append(catalog, item)
}
sort.Slice(catalog, func(left int, right int) bool {
return catalog[left].ID < catalog[right].ID
})
return catalog
}
func providerCatalogItem(item provider.Model) ChannelModelCatalogItem {
modelType := ""
for _, capability := range item.Capability {
if normalized := normalizeCatalogModelType(capability); normalized != "" {
modelType = normalized
break
}
}
return ChannelModelCatalogItem{
ID: strings.TrimPrefix(strings.TrimSpace(item.ID), "models/"),
DisplayName: strings.TrimSpace(item.DisplayName),
ModelType: modelType,
SupportedEndpointTypes: normalizeCatalogEndpointTypes(item.SupportedEndpointTypes),
}
}
func enrichCatalogItem(current ChannelModelCatalogItem, fallback ChannelModelCatalogItem) ChannelModelCatalogItem {
if current.DisplayName == "" {
current.DisplayName = fallback.DisplayName
}
if current.ModelType == "" {
current.ModelType = fallback.ModelType
}
if len(current.SupportedEndpointTypes) == 0 {
current.SupportedEndpointTypes = fallback.SupportedEndpointTypes
}
return current
}
func outboundHeadersMap(headers []OutboundHeader) map[string]string {
result := make(map[string]string, len(headers))
for _, header := range headers {
result[header.Name] = header.Value
}
return result
}
func modelCatalogRegion(baseURL string) string {
parsed, err := url.Parse(strings.TrimSpace(baseURL))
if err != nil {
return ""
}
host := strings.ToLower(parsed.Hostname())
if strings.Contains(host, "dashscope-us") {
return "us-east-1"
}
if strings.Contains(host, "ap-southeast-1") {
return "ap-southeast-1"
}
return "cn-beijing"
}
func (s *Service) isPluginEnabled() bool {
value := strings.ToLower(strings.TrimSpace(os.Getenv("ENABLE_PROVIDER_PLUGINS")))
return value == "true" || value == "1"
}