mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-12 23:23:21 +00:00
fixed bugs
This commit is contained in:
148
app/config.py
148
app/config.py
@@ -1,5 +1,6 @@
|
|||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
@@ -52,12 +53,121 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
||||||
# Plan limits
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# MODEL CATALOG — Single source of truth for all model metadata
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# To add a new model:
|
||||||
|
# 1. Add it here with name/provider/badge/description
|
||||||
|
# 2. Add its model_id → provider mapping in MODEL_PROVIDERS
|
||||||
|
# 3. Add it to the appropriate plan(s) in PLAN_LIMITS
|
||||||
|
# That's it — the frontend loads everything from GET /api/v1/models/available
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
MODEL_CATALOG = {
|
||||||
|
# ── Free tier (Fireworks - lightweight) ────────────────────────────────────
|
||||||
|
"accounts/fireworks/models/kimi-k2-instruct-0905": {
|
||||||
|
"name": "Kimi K2",
|
||||||
|
"provider": "Fireworks AI",
|
||||||
|
"badge": "Free",
|
||||||
|
"description": "Free model for building and testing chatbots",
|
||||||
|
},
|
||||||
|
|
||||||
|
# ── Starter tier (Fireworks - powerful) ────────────────────────────────────
|
||||||
|
"accounts/fireworks/models/llama-v3p1-70b-instruct": {
|
||||||
|
"name": "Llama 3.1 70B",
|
||||||
|
"provider": "Fireworks AI",
|
||||||
|
"badge": "Fast",
|
||||||
|
"description": "Fast open-source model, great for most tasks",
|
||||||
|
},
|
||||||
|
"accounts/fireworks/models/mixtral-8x7b-instruct": {
|
||||||
|
"name": "Mixtral 8x7B",
|
||||||
|
"provider": "Fireworks AI",
|
||||||
|
"badge": "Balanced",
|
||||||
|
"description": "Balanced speed and quality",
|
||||||
|
},
|
||||||
|
"accounts/fireworks/models/qwen2p5-72b-instruct": {
|
||||||
|
"name": "Qwen 2.5 72B",
|
||||||
|
"provider": "Fireworks AI",
|
||||||
|
"badge": "Multilingual",
|
||||||
|
"description": "Excellent multilingual capabilities",
|
||||||
|
},
|
||||||
|
|
||||||
|
# ── Pro tier (Premium providers) ───────────────────────────────────────────
|
||||||
|
"gpt-4o": {
|
||||||
|
"name": "GPT-4o",
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"badge": "Powerful",
|
||||||
|
"description": "Most capable OpenAI model",
|
||||||
|
},
|
||||||
|
"gpt-4-turbo": {
|
||||||
|
"name": "GPT-4 Turbo",
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"badge": "Smart",
|
||||||
|
"description": "Fast and capable with large context",
|
||||||
|
},
|
||||||
|
"gpt-3.5-turbo": {
|
||||||
|
"name": "GPT-3.5 Turbo",
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"badge": "Efficient",
|
||||||
|
"description": "Cost-effective for simpler tasks",
|
||||||
|
},
|
||||||
|
"claude-3-5-sonnet-20241022": {
|
||||||
|
"name": "Claude 3.5 Sonnet",
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"badge": "Reasoning",
|
||||||
|
"description": "Excellent at analysis and reasoning",
|
||||||
|
},
|
||||||
|
"claude-3-opus-20240229": {
|
||||||
|
"name": "Claude 3 Opus",
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"badge": "Advanced",
|
||||||
|
"description": "Most capable Anthropic model",
|
||||||
|
},
|
||||||
|
"gemini-1.5-pro": {
|
||||||
|
"name": "Gemini 1.5 Pro",
|
||||||
|
"provider": "Google",
|
||||||
|
"badge": "Long Context",
|
||||||
|
"description": "Handles very long documents well",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Model ID → LLM provider mapping (used by llm_client.py for routing) ─────
|
||||||
|
|
||||||
|
MODEL_PROVIDERS = {
|
||||||
|
"accounts/fireworks/models/kimi-k2-instruct-0905": "fireworks",
|
||||||
|
"accounts/fireworks/models/llama-v3p1-70b-instruct": "fireworks",
|
||||||
|
"accounts/fireworks/models/mixtral-8x7b-instruct": "fireworks",
|
||||||
|
"accounts/fireworks/models/qwen2p5-72b-instruct": "fireworks",
|
||||||
|
"gpt-4o": "openai",
|
||||||
|
"gpt-4-turbo": "openai",
|
||||||
|
"gpt-3.5-turbo": "openai",
|
||||||
|
"claude-3-5-sonnet-20241022": "anthropic",
|
||||||
|
"claude-3-opus-20240229": "anthropic",
|
||||||
|
"gemini-1.5-pro": "google",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Default model per plan (pre-selected in the frontend) ────────────────────
|
||||||
|
|
||||||
|
DEFAULT_MODELS = {
|
||||||
|
"free": "accounts/fireworks/models/kimi-k2-instruct-0905",
|
||||||
|
"starter": "accounts/fireworks/models/llama-v3p1-70b-instruct",
|
||||||
|
"pro": "gpt-4o",
|
||||||
|
"enterprise": "gpt-4o",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Plan limits ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
PLAN_LIMITS = {
|
PLAN_LIMITS = {
|
||||||
"free": {
|
"free": {
|
||||||
"max_chatbots": 999999, # unlimited creation
|
"max_chatbots": 999999, # unlimited creation
|
||||||
"max_published": 0, # cannot publish
|
"max_published": 0, # cannot publish
|
||||||
"models": [],
|
"models": [
|
||||||
|
"accounts/fireworks/models/kimi-k2-instruct-0905",
|
||||||
|
],
|
||||||
"conversations_limit": 999999, # unlimited preview
|
"conversations_limit": 999999, # unlimited preview
|
||||||
"code_export": False,
|
"code_export": False,
|
||||||
"features": ["preview_mode", "testing"],
|
"features": ["preview_mode", "testing"],
|
||||||
@@ -67,8 +177,9 @@ PLAN_LIMITS = {
|
|||||||
"max_published": 1,
|
"max_published": 1,
|
||||||
"models": [
|
"models": [
|
||||||
"accounts/fireworks/models/kimi-k2-instruct-0905",
|
"accounts/fireworks/models/kimi-k2-instruct-0905",
|
||||||
"accounts/fireworks/models/deepseek-v3p2",
|
"accounts/fireworks/models/llama-v3p1-70b-instruct",
|
||||||
"accounts/fireworks/models/glm-4p7",
|
"accounts/fireworks/models/mixtral-8x7b-instruct",
|
||||||
|
"accounts/fireworks/models/qwen2p5-72b-instruct",
|
||||||
],
|
],
|
||||||
"conversations_limit": 5000,
|
"conversations_limit": 5000,
|
||||||
"code_export": False,
|
"code_export": False,
|
||||||
@@ -79,7 +190,8 @@ PLAN_LIMITS = {
|
|||||||
"max_published": 3,
|
"max_published": 3,
|
||||||
"models": [
|
"models": [
|
||||||
"accounts/fireworks/models/kimi-k2-instruct-0905",
|
"accounts/fireworks/models/kimi-k2-instruct-0905",
|
||||||
"accounts/fireworks/models/deepseek-v3p2",
|
"accounts/fireworks/models/llama-v3p1-70b-instruct",
|
||||||
|
"accounts/fireworks/models/mixtral-8x7b-instruct",
|
||||||
"gpt-4o",
|
"gpt-4o",
|
||||||
"gpt-4-turbo",
|
"gpt-4-turbo",
|
||||||
"gpt-3.5-turbo",
|
"gpt-3.5-turbo",
|
||||||
@@ -101,27 +213,9 @@ PLAN_LIMITS = {
|
|||||||
"enterprise": {
|
"enterprise": {
|
||||||
"max_chatbots": 999999,
|
"max_chatbots": 999999,
|
||||||
"max_published": 999999,
|
"max_published": 999999,
|
||||||
"models": ["*"],
|
"models": ["*"], # resolves to all MODEL_CATALOG keys
|
||||||
"conversations_limit": 999999,
|
"conversations_limit": 999999,
|
||||||
"code_export": True,
|
"code_export": True,
|
||||||
"features": ["*"],
|
"features": ["*"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
MODEL_PROVIDERS = {
|
|
||||||
"accounts/fireworks/models/kimi-k2-instruct-0905": "fireworks",
|
|
||||||
"accounts/fireworks/models/deepseek-v3p2": "fireworks",
|
|
||||||
"accounts/fireworks/models/glm-4p7": "fireworks",
|
|
||||||
"gpt-4o": "openai",
|
|
||||||
"gpt-4-turbo": "openai",
|
|
||||||
"gpt-3.5-turbo": "openai",
|
|
||||||
"claude-3-5-sonnet-20241022": "anthropic",
|
|
||||||
"claude-3-opus-20240229": "anthropic",
|
|
||||||
"gemini-1.5-pro": "google",
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_MODELS = {
|
|
||||||
"starter": "accounts/fireworks/models/kimi-k2-instruct-0905",
|
|
||||||
"pro": "gpt-4o",
|
|
||||||
"enterprise": "gpt-4o",
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from fastapi.responses import JSONResponse
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.routers import auth, chatbots, documents, chat, marketplace, billing
|
from app.routers import auth, chatbots, documents, chat, marketplace, billing, models
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -53,6 +53,7 @@ app.include_router(documents.router, prefix="/api/v1")
|
|||||||
app.include_router(chat.router, prefix="/api/v1")
|
app.include_router(chat.router, prefix="/api/v1")
|
||||||
app.include_router(marketplace.router, prefix="/api/v1")
|
app.include_router(marketplace.router, prefix="/api/v1")
|
||||||
app.include_router(billing.router, prefix="/api/v1")
|
app.include_router(billing.router, prefix="/api/v1")
|
||||||
|
app.include_router(models.router, prefix="/api/v1")
|
||||||
|
|
||||||
# ── Health & Info ──────────────────────────────────────────────────────────────
|
# ── Health & Info ──────────────────────────────────────────────────────────────
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ class ChatbotCreate(BaseModel):
|
|||||||
max_tokens: int = Field(default=1000, ge=100, le=8000)
|
max_tokens: int = Field(default=1000, ge=100, le=8000)
|
||||||
primary_color: str = "#6366f1"
|
primary_color: str = "#6366f1"
|
||||||
welcome_message: str = "Hello! How can I help you today?"
|
welcome_message: str = "Hello! How can I help you today?"
|
||||||
|
logo_url: Optional[str] = None
|
||||||
category: Optional[str] = None
|
category: Optional[str] = None
|
||||||
industry: Optional[str] = None
|
industry: Optional[str] = None
|
||||||
languages: List[str] = ["en"]
|
languages: List[str] = ["en"]
|
||||||
@@ -117,6 +118,7 @@ class ChatbotUpdate(BaseModel):
|
|||||||
max_tokens: Optional[int] = None
|
max_tokens: Optional[int] = None
|
||||||
primary_color: Optional[str] = None
|
primary_color: Optional[str] = None
|
||||||
welcome_message: Optional[str] = None
|
welcome_message: Optional[str] = None
|
||||||
|
logo_url: Optional[str] = None
|
||||||
category: Optional[str] = None
|
category: Optional[str] = None
|
||||||
industry: Optional[str] = None
|
industry: Optional[str] = None
|
||||||
languages: Optional[List[str]] = None
|
languages: Optional[List[str]] = None
|
||||||
@@ -133,6 +135,7 @@ class ChatbotResponse(BaseModel):
|
|||||||
max_tokens: int
|
max_tokens: int
|
||||||
primary_color: str
|
primary_color: str
|
||||||
welcome_message: str
|
welcome_message: str
|
||||||
|
logo_url: Optional[str] = None
|
||||||
category: Optional[str] = None
|
category: Optional[str] = None
|
||||||
industry: Optional[str] = None
|
industry: Optional[str] = None
|
||||||
languages: List[str]
|
languages: List[str]
|
||||||
@@ -156,6 +159,7 @@ class ChatbotPublicResponse(BaseModel):
|
|||||||
languages: List[str]
|
languages: List[str]
|
||||||
primary_color: str
|
primary_color: str
|
||||||
welcome_message: str
|
welcome_message: str
|
||||||
|
logo_url: Optional[str] = None
|
||||||
average_rating: Optional[float] = None
|
average_rating: Optional[float] = None
|
||||||
total_conversations: int = 0
|
total_conversations: int = 0
|
||||||
company_name: Optional[str] = None
|
company_name: Optional[str] = None
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ async def create_chatbot(data: ChatbotCreate, user=Depends(get_current_user)):
|
|||||||
vector_store.create_collection(collection_name)
|
vector_store.create_collection(collection_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to create Qdrant collection: {e}")
|
logger.error(f"Failed to create Qdrant collection: {e}")
|
||||||
# Continue without vector store for now
|
|
||||||
collection_name = None
|
collection_name = None
|
||||||
|
|
||||||
chatbot_data = {
|
chatbot_data = {
|
||||||
@@ -67,6 +66,7 @@ async def create_chatbot(data: ChatbotCreate, user=Depends(get_current_user)):
|
|||||||
"max_tokens": data.max_tokens,
|
"max_tokens": data.max_tokens,
|
||||||
"primary_color": data.primary_color,
|
"primary_color": data.primary_color,
|
||||||
"welcome_message": data.welcome_message,
|
"welcome_message": data.welcome_message,
|
||||||
|
"logo_url": data.logo_url,
|
||||||
"category": data.category,
|
"category": data.category,
|
||||||
"industry": data.industry,
|
"industry": data.industry,
|
||||||
"languages": data.languages,
|
"languages": data.languages,
|
||||||
@@ -227,6 +227,7 @@ def _format_chatbot(chatbot: dict, supabase) -> ChatbotResponse:
|
|||||||
max_tokens=chatbot.get("max_tokens", 1000),
|
max_tokens=chatbot.get("max_tokens", 1000),
|
||||||
primary_color=chatbot.get("primary_color", "#6366f1"),
|
primary_color=chatbot.get("primary_color", "#6366f1"),
|
||||||
welcome_message=chatbot.get("welcome_message", "Hello! How can I help?"),
|
welcome_message=chatbot.get("welcome_message", "Hello! How can I help?"),
|
||||||
|
logo_url=chatbot.get("logo_url"),
|
||||||
category=chatbot.get("category"),
|
category=chatbot.get("category"),
|
||||||
industry=chatbot.get("industry"),
|
industry=chatbot.get("industry"),
|
||||||
languages=chatbot.get("languages", ["en"]),
|
languages=chatbot.get("languages", ["en"]),
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ async def list_marketplace_chatbots(
|
|||||||
languages=c.get("languages", ["en"]),
|
languages=c.get("languages", ["en"]),
|
||||||
primary_color=c.get("primary_color", "#6366f1"),
|
primary_color=c.get("primary_color", "#6366f1"),
|
||||||
welcome_message=c.get("welcome_message", "Hello!"),
|
welcome_message=c.get("welcome_message", "Hello!"),
|
||||||
|
logo_url=c.get("logo_url"),
|
||||||
average_rating=c.get("average_rating"),
|
average_rating=c.get("average_rating"),
|
||||||
total_conversations=c.get("total_conversations", 0),
|
total_conversations=c.get("total_conversations", 0),
|
||||||
company_name=company_data.get("name"),
|
company_name=company_data.get("name"),
|
||||||
@@ -74,23 +75,27 @@ async def list_marketplace_chatbots(
|
|||||||
total=total,
|
total=total,
|
||||||
page=page,
|
page=page,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
has_more=(offset + limit) < total,
|
has_more=(offset + limit < total),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/chatbots/{chatbot_id}", response_model=ChatbotPublicResponse)
|
@router.get("/chatbots/{chatbot_id}", response_model=ChatbotPublicResponse)
|
||||||
async def get_marketplace_chatbot(chatbot_id: str):
|
async def get_marketplace_chatbot(chatbot_id: str, user=Depends(get_optional_user)):
|
||||||
supabase = get_supabase()
|
supabase = get_supabase()
|
||||||
result = supabase.table("chatbots").select("*, companies(name, logo_url)") \
|
|
||||||
.eq("id", chatbot_id) \
|
result = supabase.table("chatbots").select(
|
||||||
.eq("is_published", True) \
|
"*, companies(name, logo_url)"
|
||||||
.execute()
|
).eq("id", chatbot_id).eq("is_published", True).execute()
|
||||||
|
|
||||||
if not result.data:
|
if not result.data:
|
||||||
raise HTTPException(status_code=404, detail="Chatbot not found in marketplace")
|
raise HTTPException(status_code=404, detail="Chatbot not found")
|
||||||
|
|
||||||
c = result.data[0]
|
c = result.data[0]
|
||||||
company_data = c.get("companies") or {}
|
company_data = c.get("companies") or {}
|
||||||
|
|
||||||
|
conv_count = supabase.table("conversations").select("id", count="exact") \
|
||||||
|
.eq("chatbot_id", chatbot_id).execute()
|
||||||
|
|
||||||
return ChatbotPublicResponse(
|
return ChatbotPublicResponse(
|
||||||
id=c["id"],
|
id=c["id"],
|
||||||
name=c["name"],
|
name=c["name"],
|
||||||
@@ -100,8 +105,9 @@ async def get_marketplace_chatbot(chatbot_id: str):
|
|||||||
languages=c.get("languages", ["en"]),
|
languages=c.get("languages", ["en"]),
|
||||||
primary_color=c.get("primary_color", "#6366f1"),
|
primary_color=c.get("primary_color", "#6366f1"),
|
||||||
welcome_message=c.get("welcome_message", "Hello!"),
|
welcome_message=c.get("welcome_message", "Hello!"),
|
||||||
|
logo_url=c.get("logo_url"),
|
||||||
average_rating=c.get("average_rating"),
|
average_rating=c.get("average_rating"),
|
||||||
total_conversations=c.get("total_conversations", 0),
|
total_conversations=conv_count.count or 0,
|
||||||
company_name=company_data.get("name"),
|
company_name=company_data.get("name"),
|
||||||
company_logo=company_data.get("logo_url"),
|
company_logo=company_data.get("logo_url"),
|
||||||
created_at=c.get("created_at"),
|
created_at=c.get("created_at"),
|
||||||
|
|||||||
107
app/routers/models.py
Normal file
107
app/routers/models.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
"""
|
||||||
|
Models router - serves available AI models based on user subscription plan.
|
||||||
|
|
||||||
|
Single source of truth flow:
|
||||||
|
config.py (PLAN_LIMITS + MODEL_CATALOG) → this router → frontend
|
||||||
|
|
||||||
|
To add/remove/rename a model, only edit config.py.
|
||||||
|
"""
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from app.dependencies import get_current_user
|
||||||
|
from app.database import get_supabase
|
||||||
|
from app.config import PLAN_LIMITS, MODEL_CATALOG, DEFAULT_MODELS
|
||||||
|
from typing import List, Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
router = APIRouter(prefix="/models", tags=["Models"])
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Response Models ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class AvailableModel(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
provider: str
|
||||||
|
badge: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
is_default: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class ModelsResponse(BaseModel):
|
||||||
|
models: List[AvailableModel]
|
||||||
|
plan: str
|
||||||
|
default_model: Optional[str] = None
|
||||||
|
has_premium_access: bool
|
||||||
|
upgrade_label: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _get_user_plan(user_id: str) -> str:
|
||||||
|
"""Get user's current subscription plan."""
|
||||||
|
supabase = get_supabase()
|
||||||
|
result = supabase.table("subscriptions") \
|
||||||
|
.select("plan") \
|
||||||
|
.eq("user_id", user_id) \
|
||||||
|
.eq("status", "active") \
|
||||||
|
.execute()
|
||||||
|
return result.data[0]["plan"] if result.data else "free"
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Endpoint ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@router.get("/available", response_model=ModelsResponse)
|
||||||
|
async def get_available_models(user=Depends(get_current_user)):
|
||||||
|
"""
|
||||||
|
Returns the list of AI models the user can access based on their plan.
|
||||||
|
Frontend uses this to populate model selection dynamically.
|
||||||
|
|
||||||
|
- free: gets a default model for preview/testing
|
||||||
|
- starter: Fireworks AI models
|
||||||
|
- pro: Fireworks + OpenAI + Anthropic + Google
|
||||||
|
- enterprise: all models
|
||||||
|
"""
|
||||||
|
plan = _get_user_plan(user.id)
|
||||||
|
plan_config = PLAN_LIMITS.get(plan, PLAN_LIMITS["free"])
|
||||||
|
allowed_model_ids = plan_config.get("models", [])
|
||||||
|
|
||||||
|
# Enterprise wildcard → resolve to all catalog models
|
||||||
|
if "*" in allowed_model_ids:
|
||||||
|
allowed_model_ids = list(MODEL_CATALOG.keys())
|
||||||
|
|
||||||
|
# Build model list from catalog metadata
|
||||||
|
default_model_id = DEFAULT_MODELS.get(plan)
|
||||||
|
models: List[AvailableModel] = []
|
||||||
|
|
||||||
|
for model_id in allowed_model_ids:
|
||||||
|
meta = MODEL_CATALOG.get(model_id)
|
||||||
|
if not meta:
|
||||||
|
logger.warning(f"Model '{model_id}' in PLAN_LIMITS[{plan}] but not in MODEL_CATALOG — skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
|
models.append(AvailableModel(
|
||||||
|
id=model_id,
|
||||||
|
name=meta["name"],
|
||||||
|
provider=meta["provider"],
|
||||||
|
badge=meta["badge"],
|
||||||
|
description=meta.get("description"),
|
||||||
|
is_default=(model_id == default_model_id),
|
||||||
|
))
|
||||||
|
|
||||||
|
# Determine upgrade messaging
|
||||||
|
has_premium = plan in ("pro", "enterprise")
|
||||||
|
upgrade_label = None
|
||||||
|
if plan == "free":
|
||||||
|
upgrade_label = "Upgrade to Starter for more models and publishing"
|
||||||
|
elif plan == "starter":
|
||||||
|
upgrade_label = "Upgrade to Pro for GPT-4o, Claude, Gemini"
|
||||||
|
|
||||||
|
return ModelsResponse(
|
||||||
|
models=models,
|
||||||
|
plan=plan,
|
||||||
|
default_model=default_model_id,
|
||||||
|
has_premium_access=has_premium,
|
||||||
|
upgrade_label=upgrade_label,
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user