|
|
|
|
|
""" |
|
|
Model Catalog API Router |
|
|
API برای دسترسی به کاتالوگ مدلهای AI |
|
|
""" |
|
|
|
|
|
from fastapi import APIRouter, Query, HTTPException |
|
|
from fastapi.responses import HTMLResponse, FileResponse |
|
|
from typing import List, Dict, Any, Optional |
|
|
import sys |
|
|
import os |
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) |
|
|
|
|
|
from backend.services.advanced_model_manager import get_model_manager, ModelInfo |
|
|
|
|
|
router = APIRouter(prefix="/api/models", tags=["Model Catalog"]) |
|
|
|
|
|
|
|
|
@router.get("/catalog", response_model=List[Dict[str, Any]]) |
|
|
async def get_model_catalog( |
|
|
category: Optional[str] = Query(None, description="Filter by category"), |
|
|
size: Optional[str] = Query(None, description="Filter by size"), |
|
|
max_size_mb: Optional[int] = Query(None, description="Max size in MB"), |
|
|
language: Optional[str] = Query(None, description="Filter by language"), |
|
|
free_only: bool = Query(True, description="Free models only"), |
|
|
no_auth: bool = Query(True, description="No authentication required"), |
|
|
min_performance: float = Query(0.0, description="Minimum performance score"), |
|
|
limit: int = Query(100, description="Max results") |
|
|
): |
|
|
""" |
|
|
دریافت لیست مدلها با فیلترهای مختلف |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/catalog?category=sentiment&max_size_mb=500&limit=10 |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
|
|
|
models = manager.filter_models( |
|
|
category=category, |
|
|
size=size, |
|
|
max_size_mb=max_size_mb, |
|
|
language=language, |
|
|
free_only=free_only, |
|
|
no_auth=no_auth, |
|
|
min_performance=min_performance |
|
|
) |
|
|
|
|
|
|
|
|
return [model.to_dict() for model in models[:limit]] |
|
|
|
|
|
|
|
|
@router.get("/model/{model_id}", response_model=Dict[str, Any]) |
|
|
async def get_model_details(model_id: str): |
|
|
""" |
|
|
دریافت جزئیات کامل یک مدل |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/model/cryptobert |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
model = manager.get_model_by_id(model_id) |
|
|
|
|
|
if not model: |
|
|
raise HTTPException(status_code=404, detail=f"Model {model_id} not found") |
|
|
|
|
|
return model.to_dict() |
|
|
|
|
|
|
|
|
@router.get("/search") |
|
|
async def search_models( |
|
|
q: str = Query(..., description="Search query"), |
|
|
limit: int = Query(10, description="Max results") |
|
|
): |
|
|
""" |
|
|
جستجو در مدلها |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/search?q=crypto&limit=5 |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
results = manager.search_models(q) |
|
|
|
|
|
return { |
|
|
"query": q, |
|
|
"total": len(results), |
|
|
"results": [model.to_dict() for model in results[:limit]] |
|
|
} |
|
|
|
|
|
|
|
|
@router.get("/best/{category}") |
|
|
async def get_best_models( |
|
|
category: str, |
|
|
top_n: int = Query(3, description="Number of top models"), |
|
|
max_size_mb: Optional[int] = Query(None, description="Max size in MB") |
|
|
): |
|
|
""" |
|
|
دریافت بهترین مدلها در یک category |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/best/sentiment?top_n=5&max_size_mb=500 |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
|
|
|
try: |
|
|
models = manager.get_best_models( |
|
|
category=category, |
|
|
top_n=top_n, |
|
|
max_size_mb=max_size_mb |
|
|
) |
|
|
|
|
|
return { |
|
|
"category": category, |
|
|
"count": len(models), |
|
|
"models": [model.to_dict() for model in models] |
|
|
} |
|
|
except Exception as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/recommend") |
|
|
async def recommend_models( |
|
|
use_case: str = Query(..., description="Use case (e.g., twitter, news, trading)"), |
|
|
max_models: int = Query(5, description="Max recommendations"), |
|
|
max_size_mb: Optional[int] = Query(None, description="Max size in MB") |
|
|
): |
|
|
""" |
|
|
توصیه مدلها بر اساس use case |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/recommend?use_case=twitter&max_models=3 |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
|
|
|
models = manager.recommend_models( |
|
|
use_case=use_case, |
|
|
max_models=max_models, |
|
|
max_size_mb=max_size_mb |
|
|
) |
|
|
|
|
|
return { |
|
|
"use_case": use_case, |
|
|
"count": len(models), |
|
|
"recommendations": [model.to_dict() for model in models] |
|
|
} |
|
|
|
|
|
|
|
|
@router.get("/stats") |
|
|
async def get_catalog_stats(): |
|
|
""" |
|
|
آمار کامل کاتالوگ مدلها |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/stats |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
return manager.get_model_stats() |
|
|
|
|
|
|
|
|
@router.get("/categories") |
|
|
async def get_categories(): |
|
|
""" |
|
|
لیست categories با آمار |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/models/categories |
|
|
``` |
|
|
""" |
|
|
manager = get_model_manager() |
|
|
return { |
|
|
"categories": manager.get_categories() |
|
|
} |
|
|
|
|
|
|
|
|
@router.get("/ui", response_class=HTMLResponse) |
|
|
async def model_catalog_ui(): |
|
|
""" |
|
|
رابط کاربری HTML برای مرور مدلها |
|
|
""" |
|
|
return """ |
|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>🤖 AI Models Catalog</title> |
|
|
<style> |
|
|
* { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
box-sizing: border-box; |
|
|
} |
|
|
|
|
|
body { |
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
min-height: 100vh; |
|
|
padding: 20px; |
|
|
} |
|
|
|
|
|
.container { |
|
|
max-width: 1400px; |
|
|
margin: 0 auto; |
|
|
background: white; |
|
|
border-radius: 20px; |
|
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3); |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.header { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
color: white; |
|
|
padding: 40px; |
|
|
text-align: center; |
|
|
} |
|
|
|
|
|
.header h1 { |
|
|
font-size: 2.5rem; |
|
|
margin-bottom: 10px; |
|
|
} |
|
|
|
|
|
.header p { |
|
|
font-size: 1.1rem; |
|
|
opacity: 0.9; |
|
|
} |
|
|
|
|
|
.stats-bar { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
|
|
gap: 20px; |
|
|
padding: 30px 40px; |
|
|
background: #f8f9fa; |
|
|
border-bottom: 1px solid #e9ecef; |
|
|
} |
|
|
|
|
|
.stat-card { |
|
|
background: white; |
|
|
padding: 20px; |
|
|
border-radius: 10px; |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
|
|
text-align: center; |
|
|
} |
|
|
|
|
|
.stat-card .value { |
|
|
font-size: 2rem; |
|
|
font-weight: bold; |
|
|
color: #667eea; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
|
|
|
.stat-card .label { |
|
|
font-size: 0.9rem; |
|
|
color: #6c757d; |
|
|
} |
|
|
|
|
|
.filters { |
|
|
padding: 30px 40px; |
|
|
background: #f8f9fa; |
|
|
border-bottom: 1px solid #e9ecef; |
|
|
} |
|
|
|
|
|
.filter-row { |
|
|
display: flex; |
|
|
gap: 15px; |
|
|
flex-wrap: wrap; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
.filter-group { |
|
|
flex: 1; |
|
|
min-width: 200px; |
|
|
} |
|
|
|
|
|
.filter-group label { |
|
|
display: block; |
|
|
margin-bottom: 5px; |
|
|
font-weight: 500; |
|
|
color: #495057; |
|
|
} |
|
|
|
|
|
.filter-group select, |
|
|
.filter-group input { |
|
|
width: 100%; |
|
|
padding: 10px 15px; |
|
|
border: 1px solid #ced4da; |
|
|
border-radius: 8px; |
|
|
font-size: 1rem; |
|
|
} |
|
|
|
|
|
.search-box { |
|
|
position: relative; |
|
|
flex: 2; |
|
|
min-width: 300px; |
|
|
} |
|
|
|
|
|
.search-box input { |
|
|
width: 100%; |
|
|
padding: 12px 45px 12px 15px; |
|
|
border: 2px solid #667eea; |
|
|
border-radius: 10px; |
|
|
font-size: 1rem; |
|
|
} |
|
|
|
|
|
.search-box button { |
|
|
position: absolute; |
|
|
right: 5px; |
|
|
top: 50%; |
|
|
transform: translateY(-50%); |
|
|
background: #667eea; |
|
|
color: white; |
|
|
border: none; |
|
|
padding: 8px 15px; |
|
|
border-radius: 8px; |
|
|
cursor: pointer; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.search-box button:hover { |
|
|
background: #5568d3; |
|
|
} |
|
|
|
|
|
.content { |
|
|
padding: 40px; |
|
|
} |
|
|
|
|
|
.models-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); |
|
|
gap: 25px; |
|
|
} |
|
|
|
|
|
.model-card { |
|
|
background: white; |
|
|
border: 1px solid #e9ecef; |
|
|
border-radius: 15px; |
|
|
padding: 25px; |
|
|
transition: all 0.3s ease; |
|
|
position: relative; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.model-card:hover { |
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.15); |
|
|
transform: translateY(-5px); |
|
|
border-color: #667eea; |
|
|
} |
|
|
|
|
|
.model-card::before { |
|
|
content: ''; |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 4px; |
|
|
background: linear-gradient(90deg, #667eea, #764ba2); |
|
|
} |
|
|
|
|
|
.model-header { |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
.model-name { |
|
|
font-size: 1.3rem; |
|
|
font-weight: bold; |
|
|
color: #212529; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
|
|
|
.model-id { |
|
|
font-size: 0.85rem; |
|
|
color: #6c757d; |
|
|
font-family: 'Courier New', monospace; |
|
|
} |
|
|
|
|
|
.model-description { |
|
|
color: #495057; |
|
|
line-height: 1.6; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
.model-meta { |
|
|
display: flex; |
|
|
gap: 15px; |
|
|
margin-bottom: 15px; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
|
|
|
.meta-item { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 5px; |
|
|
font-size: 0.9rem; |
|
|
color: #6c757d; |
|
|
} |
|
|
|
|
|
.meta-item .icon { |
|
|
font-size: 1.1rem; |
|
|
} |
|
|
|
|
|
.tags { |
|
|
display: flex; |
|
|
flex-wrap: wrap; |
|
|
gap: 8px; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
|
|
|
.tag { |
|
|
background: #e7f0ff; |
|
|
color: #0056b3; |
|
|
padding: 5px 12px; |
|
|
border-radius: 20px; |
|
|
font-size: 0.85rem; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.category-badge { |
|
|
background: #667eea; |
|
|
color: white; |
|
|
padding: 4px 10px; |
|
|
border-radius: 15px; |
|
|
font-size: 0.8rem; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.performance-bar { |
|
|
margin-top: 15px; |
|
|
} |
|
|
|
|
|
.performance-label { |
|
|
font-size: 0.85rem; |
|
|
color: #6c757d; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
|
|
|
.progress-bar { |
|
|
height: 6px; |
|
|
background: #e9ecef; |
|
|
border-radius: 3px; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.progress-fill { |
|
|
height: 100%; |
|
|
background: linear-gradient(90deg, #667eea, #764ba2); |
|
|
border-radius: 3px; |
|
|
transition: width 0.3s ease; |
|
|
} |
|
|
|
|
|
.model-actions { |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
margin-top: 15px; |
|
|
} |
|
|
|
|
|
.btn { |
|
|
flex: 1; |
|
|
padding: 10px; |
|
|
border: none; |
|
|
border-radius: 8px; |
|
|
cursor: pointer; |
|
|
font-weight: 500; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.btn-primary { |
|
|
background: #667eea; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.btn-primary:hover { |
|
|
background: #5568d3; |
|
|
} |
|
|
|
|
|
.btn-secondary { |
|
|
background: #f8f9fa; |
|
|
color: #495057; |
|
|
border: 1px solid #dee2e6; |
|
|
} |
|
|
|
|
|
.btn-secondary:hover { |
|
|
background: #e9ecef; |
|
|
} |
|
|
|
|
|
.loading { |
|
|
text-align: center; |
|
|
padding: 60px; |
|
|
color: #6c757d; |
|
|
font-size: 1.2rem; |
|
|
} |
|
|
|
|
|
.no-results { |
|
|
text-align: center; |
|
|
padding: 60px; |
|
|
color: #6c757d; |
|
|
} |
|
|
|
|
|
.no-results-icon { |
|
|
font-size: 4rem; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<div class="header"> |
|
|
<h1>🤖 AI Models Catalog</h1> |
|
|
<p>Comprehensive catalog of 25+ AI models for crypto & finance</p> |
|
|
</div> |
|
|
|
|
|
<div class="stats-bar" id="stats-bar"> |
|
|
<div class="stat-card"> |
|
|
<div class="value" id="stat-total">-</div> |
|
|
<div class="label">Total Models</div> |
|
|
</div> |
|
|
<div class="stat-card"> |
|
|
<div class="value" id="stat-free">-</div> |
|
|
<div class="label">Free Models</div> |
|
|
</div> |
|
|
<div class="stat-card"> |
|
|
<div class="value" id="stat-api">-</div> |
|
|
<div class="label">API Compatible</div> |
|
|
</div> |
|
|
<div class="stat-card"> |
|
|
<div class="value" id="stat-performance">-</div> |
|
|
<div class="label">Avg Performance</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="filters"> |
|
|
<div class="filter-row"> |
|
|
<div class="search-box"> |
|
|
<input type="text" id="search-input" placeholder="Search models by name, description, or tags..."> |
|
|
<button onclick="searchModels()">🔍 Search</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="filter-row"> |
|
|
<div class="filter-group"> |
|
|
<label>Category</label> |
|
|
<select id="filter-category" onchange="applyFilters()"> |
|
|
<option value="">All Categories</option> |
|
|
<option value="sentiment">Sentiment</option> |
|
|
<option value="generation">Generation</option> |
|
|
<option value="trading">Trading</option> |
|
|
<option value="summarization">Summarization</option> |
|
|
<option value="ner">NER</option> |
|
|
<option value="question_answering">Q&A</option> |
|
|
<option value="classification">Classification</option> |
|
|
<option value="embedding">Embedding</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="filter-group"> |
|
|
<label>Size</label> |
|
|
<select id="filter-size" onchange="applyFilters()"> |
|
|
<option value="">All Sizes</option> |
|
|
<option value="tiny">Tiny (<100 MB)</option> |
|
|
<option value="small">Small (100-500 MB)</option> |
|
|
<option value="medium">Medium (500MB-1GB)</option> |
|
|
<option value="large">Large (1-3GB)</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="filter-group"> |
|
|
<label>Max Size (MB)</label> |
|
|
<input type="number" id="filter-max-size" placeholder="e.g., 500" onchange="applyFilters()"> |
|
|
</div> |
|
|
<div class="filter-group"> |
|
|
<label>Min Performance</label> |
|
|
<input type="number" id="filter-min-perf" placeholder="0.0-1.0" step="0.1" min="0" max="1" onchange="applyFilters()"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="content"> |
|
|
<div id="loading" class="loading">Loading models...</div> |
|
|
<div id="models-container" class="models-grid" style="display: none;"></div> |
|
|
<div id="no-results" class="no-results" style="display: none;"> |
|
|
<div class="no-results-icon">🔍</div> |
|
|
<h2>No models found</h2> |
|
|
<p>Try adjusting your filters or search query</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
let allModels = []; |
|
|
|
|
|
// Load stats |
|
|
async function loadStats() { |
|
|
try { |
|
|
const response = await fetch('/api/models/stats'); |
|
|
const stats = await response.json(); |
|
|
|
|
|
document.getElementById('stat-total').textContent = stats.total_models; |
|
|
document.getElementById('stat-free').textContent = stats.free_models; |
|
|
document.getElementById('stat-api').textContent = stats.api_compatible; |
|
|
document.getElementById('stat-performance').textContent = stats.avg_performance.toFixed(2); |
|
|
} catch (error) { |
|
|
console.error('Error loading stats:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
// Load models |
|
|
async function loadModels() { |
|
|
try { |
|
|
const response = await fetch('/api/models/catalog?limit=100'); |
|
|
allModels = await response.json(); |
|
|
|
|
|
document.getElementById('loading').style.display = 'none'; |
|
|
displayModels(allModels); |
|
|
} catch (error) { |
|
|
console.error('Error loading models:', error); |
|
|
document.getElementById('loading').innerHTML = '❌ Error loading models'; |
|
|
} |
|
|
} |
|
|
|
|
|
// Display models |
|
|
function displayModels(models) { |
|
|
const container = document.getElementById('models-container'); |
|
|
|
|
|
if (models.length === 0) { |
|
|
container.style.display = 'none'; |
|
|
document.getElementById('no-results').style.display = 'block'; |
|
|
return; |
|
|
} |
|
|
|
|
|
document.getElementById('no-results').style.display = 'none'; |
|
|
container.style.display = 'grid'; |
|
|
|
|
|
container.innerHTML = models.map(model => ` |
|
|
<div class="model-card"> |
|
|
<div class="model-header"> |
|
|
<div class="model-name">${model.name}</div> |
|
|
<div class="model-id">${model.hf_id}</div> |
|
|
</div> |
|
|
|
|
|
<div class="category-badge">${model.category}</div> |
|
|
|
|
|
<p class="model-description">${model.description}</p> |
|
|
|
|
|
<div class="model-meta"> |
|
|
<div class="meta-item"> |
|
|
<span class="icon">💾</span> |
|
|
<span>${model.size_mb} MB</span> |
|
|
</div> |
|
|
<div class="meta-item"> |
|
|
<span class="icon">🌍</span> |
|
|
<span>${model.languages.join(', ')}</span> |
|
|
</div> |
|
|
${model.free ? '<div class="meta-item"><span class="icon">✅</span><span>Free</span></div>' : ''} |
|
|
${model.api_compatible ? '<div class="meta-item"><span class="icon">🔌</span><span>API</span></div>' : ''} |
|
|
</div> |
|
|
|
|
|
<div class="tags"> |
|
|
${model.tags.slice(0, 3).map(tag => `<span class="tag">${tag}</span>`).join('')} |
|
|
</div> |
|
|
|
|
|
<div class="performance-bar"> |
|
|
<div class="performance-label">Performance: ${(model.performance_score * 100).toFixed(0)}%</div> |
|
|
<div class="progress-bar"> |
|
|
<div class="progress-fill" style="width: ${model.performance_score * 100}%"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="model-actions"> |
|
|
<button class="btn btn-primary" onclick="tryModel('${model.id}')"> |
|
|
Try Model |
|
|
</button> |
|
|
<button class="btn btn-secondary" onclick="viewDetails('${model.id}')"> |
|
|
Details |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
`).join(''); |
|
|
} |
|
|
|
|
|
// Apply filters |
|
|
function applyFilters() { |
|
|
const category = document.getElementById('filter-category').value; |
|
|
const size = document.getElementById('filter-size').value; |
|
|
const maxSize = document.getElementById('filter-max-size').value; |
|
|
const minPerf = document.getElementById('filter-min-perf').value; |
|
|
|
|
|
let filtered = allModels; |
|
|
|
|
|
if (category) { |
|
|
filtered = filtered.filter(m => m.category === category); |
|
|
} |
|
|
|
|
|
if (size) { |
|
|
filtered = filtered.filter(m => m.size === size); |
|
|
} |
|
|
|
|
|
if (maxSize) { |
|
|
filtered = filtered.filter(m => m.size_mb <= parseInt(maxSize)); |
|
|
} |
|
|
|
|
|
if (minPerf) { |
|
|
filtered = filtered.filter(m => m.performance_score >= parseFloat(minPerf)); |
|
|
} |
|
|
|
|
|
displayModels(filtered); |
|
|
} |
|
|
|
|
|
// Search models |
|
|
async function searchModels() { |
|
|
const query = document.getElementById('search-input').value; |
|
|
|
|
|
if (!query) { |
|
|
displayModels(allModels); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
const response = await fetch(`/api/models/search?q=${encodeURIComponent(query)}&limit=50`); |
|
|
const data = await response.json(); |
|
|
displayModels(data.results); |
|
|
} catch (error) { |
|
|
console.error('Error searching:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
// Try model |
|
|
function tryModel(modelId) { |
|
|
// Redirect to sentiment analysis page with model pre-selected |
|
|
window.location.href = `/api/ai/sentiment/quick?model=${modelId}`; |
|
|
} |
|
|
|
|
|
// View details |
|
|
async function viewDetails(modelId) { |
|
|
try { |
|
|
const response = await fetch(`/api/models/model/${modelId}`); |
|
|
const model = await response.json(); |
|
|
|
|
|
alert(` |
|
|
Model: ${model.name} |
|
|
HuggingFace ID: ${model.hf_id} |
|
|
Category: ${model.category} |
|
|
Size: ${model.size_mb} MB |
|
|
Description: ${model.description} |
|
|
Use Cases: ${model.use_cases.join(', ')} |
|
|
Performance: ${(model.performance_score * 100).toFixed(0)}% |
|
|
Popularity: ${(model.popularity_score * 100).toFixed(0)}% |
|
|
`.trim()); |
|
|
} catch (error) { |
|
|
console.error('Error loading details:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
// Initialize |
|
|
window.addEventListener('DOMContentLoaded', () => { |
|
|
loadStats(); |
|
|
loadModels(); |
|
|
}); |
|
|
|
|
|
// Enter key for search |
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
document.getElementById('search-input').addEventListener('keypress', (e) => { |
|
|
if (e.key === 'Enter') { |
|
|
searchModels(); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
# در production_server.py: |
|
|
|
|
|
from backend.routers.model_catalog import router as catalog_router |
|
|
|
|
|
app = FastAPI() |
|
|
app.include_router(catalog_router) |
|
|
|
|
|
# حالا در دسترس است: |
|
|
# - GET /api/models/catalog |
|
|
# - GET /api/models/model/{model_id} |
|
|
# - GET /api/models/search?q=... |
|
|
# - GET /api/models/best/{category} |
|
|
# - GET /api/models/recommend?use_case=... |
|
|
# - GET /api/models/stats |
|
|
# - GET /api/models/categories |
|
|
# - GET /api/models/ui (صفحه HTML) |
|
|
""" |
|
|
|