package httpapi import ( "context" "encoding/json" "errors" "fmt" "io" "net/http" "net/url" "strings" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) const maxUsageSceneCatalogResponseBytes = 8 << 20 var supportedModelUsageScenes = map[string]struct{}{ "canvas_model_node": {}, "desktop": {}, } func parseModelUsageSceneQuery(query url.Values) (string, error) { values, present := query["usage_scene"] if !present { return "", nil } if len(values) != 1 { return "", errors.New("usage_scene must be provided exactly once") } scene := strings.TrimSpace(values[0]) if _, ok := supportedModelUsageScenes[scene]; !ok { return "", errors.New("usage_scene must be canvas_model_node or desktop") } return scene, nil } func (s *Server) listServerMainUsageSceneModelIDs(ctx context.Context, usageScene string) (map[string]struct{}, error) { baseURL := strings.TrimRight(strings.TrimSpace(s.cfg.ServerMainBaseURL), "/") if baseURL == "" { return nil, errors.New("SERVER_MAIN_BASE_URL is not configured") } var requestErrors []error for _, endpoint := range serverMainUsageSceneCatalogEndpoints(baseURL, usageScene) { ids, err := s.requestServerMainUsageSceneModelIDs(ctx, endpoint) if err == nil { return ids, nil } requestErrors = append(requestErrors, err) } return nil, fmt.Errorf("server-main model catalog request failed: %w", errors.Join(requestErrors...)) } func serverMainUsageSceneCatalogEndpoints(baseURL string, usageScene string) []string { path := "/integration-platform/models/strict/all?usage_scene=" + url.QueryEscape(usageScene) if strings.HasSuffix(baseURL, "/api") { return []string{baseURL + path} } return []string{baseURL + "/api" + path, baseURL + path} } func (s *Server) requestServerMainUsageSceneModelIDs(ctx context.Context, endpoint string) (map[string]struct{}, error) { request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return nil, fmt.Errorf("create server-main model catalog request: %w", err) } request.Header.Set("Accept", "application/json") client := http.DefaultClient if s.auth != nil && s.auth.HTTPClient != nil { client = s.auth.HTTPClient } response, err := client.Do(request) if err != nil { return nil, fmt.Errorf("request server-main model catalog: %w", err) } defer response.Body.Close() if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { _, _ = io.Copy(io.Discard, io.LimitReader(response.Body, 4<<10)) return nil, fmt.Errorf("server-main model catalog returned HTTP %d", response.StatusCode) } var payload any decoder := json.NewDecoder(io.LimitReader(response.Body, maxUsageSceneCatalogResponseBytes)) if err := decoder.Decode(&payload); err != nil { return nil, fmt.Errorf("decode server-main model catalog: %w", err) } items, ok := usageSceneCatalogItems(payload) if !ok { return nil, errors.New("server-main model catalog payload is malformed") } return usageSceneModelIDs(items), nil } func usageSceneCatalogItems(payload any) ([]any, bool) { if items, ok := payload.([]any); ok { return items, true } record, ok := payload.(map[string]any) if !ok { return nil, false } for _, key := range []string{"modelList", "items", "data"} { value, exists := record[key] if !exists { continue } if items, ok := value.([]any); ok { return items, true } if items, ok := usageSceneCatalogItems(value); ok { return items, true } } return nil, false } func usageSceneModelIDs(items []any) map[string]struct{} { ids := make(map[string]struct{}, len(items)) for _, item := range items { record, ok := item.(map[string]any) if !ok { continue } for _, key := range []string{ "alias", "invocationName", "invocation_name", "modelAlias", "model_alias", "name", "modelName", "model_name", "providerModelName", "provider_model_name", } { addUsageSceneModelID(ids, record[key]) } for _, key := range []string{"legacyAliases", "legacy_aliases"} { if aliases, ok := record[key].([]any); ok { for _, alias := range aliases { addUsageSceneModelID(ids, alias) } } } } return ids } func filterPlatformModelsByUsageSceneIDs(models []store.PlatformModel, availableModelIDs map[string]struct{}) []store.PlatformModel { filtered := make([]store.PlatformModel, 0, len(models)) for _, model := range models { if platformModelMatchesUsageSceneIDs(model, availableModelIDs) { filtered = append(filtered, model) } } return filtered } func platformModelMatchesUsageSceneIDs(model store.PlatformModel, availableModelIDs map[string]struct{}) bool { for _, value := range []string{model.ModelName, model.ProviderModelName, model.ModelAlias} { if _, ok := availableModelIDs[normalizeUsageSceneModelID(value)]; ok { return true } } for _, alias := range model.LegacyAliases { if _, ok := availableModelIDs[normalizeUsageSceneModelID(alias)]; ok { return true } } return false } func addUsageSceneModelID(ids map[string]struct{}, value any) { text, ok := value.(string) if !ok { return } if normalized := normalizeUsageSceneModelID(text); normalized != "" { ids[normalized] = struct{}{} } } func normalizeUsageSceneModelID(value string) string { return strings.ToLower(strings.TrimSpace(value)) }