483 lines
14 KiB
Python
483 lines
14 KiB
Python
"""
|
||
ParamHub - 参数百科
|
||
AI大模型与硬件参数速查平台
|
||
"""
|
||
|
||
from flask import Flask, render_template, jsonify, request
|
||
from flask_cors import CORS
|
||
import json
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
|
||
app = Flask(__name__, static_folder='static', static_url_path='/static')
|
||
CORS(app)
|
||
|
||
# 数据目录
|
||
DATA_DIR = Path(__file__).parent / 'data'
|
||
DATA_DIR.mkdir(exist_ok=True)
|
||
|
||
# 数据文件
|
||
MODELS_FILE = DATA_DIR / 'models.json'
|
||
GPUS_FILE = DATA_DIR / 'gpus.json'
|
||
CPUS_FILE = DATA_DIR / 'cpus.json'
|
||
CATEGORIES_FILE = DATA_DIR / 'categories.json'
|
||
KNOWLEDGE_FILE = DATA_DIR / 'knowledge.json'
|
||
|
||
def load_data(file_path):
|
||
"""加载JSON数据"""
|
||
if file_path.exists():
|
||
return json.loads(file_path.read_text(encoding='utf-8'))
|
||
return []
|
||
|
||
def save_data(file_path, data):
|
||
"""保存JSON数据"""
|
||
file_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
|
||
# ============ 页面路由 ============
|
||
|
||
@app.route('/')
|
||
def index():
|
||
"""首页"""
|
||
return render_template('index.html')
|
||
|
||
@app.route('/models')
|
||
def models_page():
|
||
"""模型数据库页面"""
|
||
return render_template('models.html')
|
||
|
||
@app.route('/gpus')
|
||
def gpus_page():
|
||
"""GPU数据库页面"""
|
||
return render_template('gpus.html')
|
||
|
||
@app.route('/cpus')
|
||
def cpus_page():
|
||
"""CPU数据库页面"""
|
||
return render_template('cpus.html')
|
||
|
||
@app.route('/tools')
|
||
def tools_page():
|
||
"""工具页面"""
|
||
return render_template('tools.html')
|
||
|
||
@app.route('/compare')
|
||
def compare_page():
|
||
"""对比页面"""
|
||
return render_template('compare.html')
|
||
|
||
@app.route('/knowledge')
|
||
def knowledge_page():
|
||
"""知识库页面"""
|
||
return render_template('knowledge.html')
|
||
|
||
@app.route('/admin')
|
||
def admin_page():
|
||
"""后台管理页面"""
|
||
return render_template('admin.html')
|
||
|
||
# ============ API路由 ============
|
||
|
||
@app.route('/api/models')
|
||
def api_models():
|
||
"""获取模型列表"""
|
||
models = load_data(MODELS_FILE)
|
||
|
||
# 搜索过滤
|
||
keyword = request.args.get('q', '').strip().lower()
|
||
if keyword:
|
||
models = [m for m in models if keyword in m.get('name', '').lower() or
|
||
keyword in m.get('organization', '').lower()]
|
||
|
||
# 排序
|
||
sort_by = request.args.get('sort', 'name')
|
||
reverse = request.args.get('order', 'asc') == 'desc'
|
||
|
||
if sort_by in ['name', 'parameters', 'context_length', 'mmlu']:
|
||
models = sorted(models, key=lambda x: x.get(sort_by, 0) or 0, reverse=reverse)
|
||
|
||
return jsonify(models)
|
||
|
||
@app.route('/api/models/<model_id>')
|
||
def api_model_detail(model_id):
|
||
"""获取单个模型详情"""
|
||
models = load_data(MODELS_FILE)
|
||
model = next((m for m in models if m['id'] == model_id), None)
|
||
|
||
if not model:
|
||
return jsonify({'error': 'Model not found'}), 404
|
||
|
||
return jsonify(model)
|
||
|
||
@app.route('/api/models', methods=['POST'])
|
||
def api_create_model():
|
||
"""创建新模型"""
|
||
data = request.get_json()
|
||
models = load_data(MODELS_FILE)
|
||
|
||
# 生成ID
|
||
import uuid
|
||
data['id'] = uuid.uuid4().hex[:12]
|
||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
models.append(data)
|
||
save_data(MODELS_FILE, models)
|
||
|
||
return jsonify(data)
|
||
|
||
@app.route('/api/models/<model_id>', methods=['PUT'])
|
||
def api_update_model(model_id):
|
||
"""更新模型"""
|
||
data = request.get_json()
|
||
models = load_data(MODELS_FILE)
|
||
|
||
model = next((m for m in models if m['id'] == model_id), None)
|
||
if not model:
|
||
return jsonify({'error': 'Model not found'}), 404
|
||
|
||
model.update(data)
|
||
model['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_data(MODELS_FILE, models)
|
||
|
||
return jsonify(model)
|
||
|
||
@app.route('/api/models/<model_id>', methods=['DELETE'])
|
||
def api_delete_model(model_id):
|
||
"""删除模型"""
|
||
models = load_data(MODELS_FILE)
|
||
models = [m for m in models if m['id'] != model_id]
|
||
save_data(MODELS_FILE, models)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
@app.route('/api/gpus')
|
||
def api_gpus():
|
||
"""获取GPU列表"""
|
||
gpus = load_data(GPUS_FILE)
|
||
|
||
keyword = request.args.get('q', '').strip().lower()
|
||
if keyword:
|
||
gpus = [g for g in gpus if keyword in g.get('name', '').lower() or
|
||
keyword in g.get('manufacturer', '').lower()]
|
||
|
||
return jsonify(gpus)
|
||
|
||
@app.route('/api/gpus/<gpu_id>')
|
||
def api_gpu_detail(gpu_id):
|
||
"""获取单个GPU详情"""
|
||
gpus = load_data(GPUS_FILE)
|
||
gpu = next((g for g in gpus if g['id'] == gpu_id), None)
|
||
|
||
if not gpu:
|
||
return jsonify({'error': 'GPU not found'}), 404
|
||
|
||
return jsonify(gpu)
|
||
|
||
@app.route('/api/cpus')
|
||
def api_cpus():
|
||
"""获取CPU列表"""
|
||
cpus = load_data(CPUS_FILE)
|
||
|
||
keyword = request.args.get('q', '').strip().lower()
|
||
if keyword:
|
||
cpus = [c for c in cpus if keyword in c.get('name', '').lower() or
|
||
keyword in c.get('manufacturer', '').lower()]
|
||
|
||
return jsonify(cpus)
|
||
|
||
@app.route('/api/cpus/<cpu_id>')
|
||
def api_cpu_detail(cpu_id):
|
||
"""获取单个CPU详情"""
|
||
cpus = load_data(CPUS_FILE)
|
||
cpu = next((c for c in cpus if c['id'] == cpu_id), None)
|
||
|
||
if not cpu:
|
||
return jsonify({'error': 'CPU not found'}), 404
|
||
|
||
return jsonify(cpu)
|
||
|
||
@app.route('/api/gpus', methods=['POST'])
|
||
def api_create_gpu():
|
||
"""创建新GPU"""
|
||
data = request.get_json()
|
||
gpus = load_data(GPUS_FILE)
|
||
|
||
import uuid
|
||
data['id'] = uuid.uuid4().hex[:12]
|
||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
gpus.append(data)
|
||
save_data(GPUS_FILE, gpus)
|
||
|
||
return jsonify(data)
|
||
|
||
@app.route('/api/gpus/<gpu_id>', methods=['PUT'])
|
||
def api_update_gpu(gpu_id):
|
||
"""更新GPU"""
|
||
data = request.get_json()
|
||
gpus = load_data(GPUS_FILE)
|
||
|
||
gpu = next((g for g in gpus if g['id'] == gpu_id), None)
|
||
if not gpu:
|
||
return jsonify({'error': 'GPU not found'}), 404
|
||
|
||
gpu.update(data)
|
||
gpu['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_data(GPUS_FILE, gpus)
|
||
|
||
return jsonify(gpu)
|
||
|
||
@app.route('/api/gpus/<gpu_id>', methods=['DELETE'])
|
||
def api_delete_gpu(gpu_id):
|
||
"""删除GPU"""
|
||
gpus = load_data(GPUS_FILE)
|
||
gpus = [g for g in gpus if g['id'] != gpu_id]
|
||
save_data(GPUS_FILE, gpus)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
@app.route('/api/cpus', methods=['POST'])
|
||
def api_create_cpu():
|
||
"""创建新CPU"""
|
||
data = request.get_json()
|
||
cpus = load_data(CPUS_FILE)
|
||
|
||
import uuid
|
||
data['id'] = uuid.uuid4().hex[:12]
|
||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
cpus.append(data)
|
||
save_data(CPUS_FILE, cpus)
|
||
|
||
return jsonify(data)
|
||
|
||
@app.route('/api/cpus/<cpu_id>', methods=['PUT'])
|
||
def api_update_cpu(cpu_id):
|
||
"""更新CPU"""
|
||
data = request.get_json()
|
||
cpus = load_data(CPUS_FILE)
|
||
|
||
cpu = next((c for c in cpus if c['id'] == cpu_id), None)
|
||
if not cpu:
|
||
return jsonify({'error': 'CPU not found'}), 404
|
||
|
||
cpu.update(data)
|
||
cpu['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_data(CPUS_FILE, cpus)
|
||
|
||
return jsonify(cpu)
|
||
|
||
@app.route('/api/cpus/<cpu_id>', methods=['DELETE'])
|
||
def api_delete_cpu(cpu_id):
|
||
"""删除CPU"""
|
||
cpus = load_data(CPUS_FILE)
|
||
cpus = [c for c in cpus if c['id'] != cpu_id]
|
||
save_data(CPUS_FILE, cpus)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
@app.route('/api/search')
|
||
def api_search():
|
||
"""全局搜索"""
|
||
keyword = request.args.get('q', '').strip().lower()
|
||
|
||
if not keyword:
|
||
return jsonify({'models': [], 'gpus': [], 'cpus': []})
|
||
|
||
models = load_data(MODELS_FILE)
|
||
gpus = load_data(GPUS_FILE)
|
||
cpus = load_data(CPUS_FILE)
|
||
|
||
result = {
|
||
'models': [m for m in models if keyword in m.get('name', '').lower() or
|
||
keyword in m.get('organization', '').lower()],
|
||
'gpus': [g for g in gpus if keyword in g.get('name', '').lower() or
|
||
keyword in g.get('manufacturer', '').lower()],
|
||
'cpus': [c for c in cpus if keyword in c.get('name', '').lower() or
|
||
keyword in c.get('manufacturer', '').lower()]
|
||
}
|
||
|
||
return jsonify(result)
|
||
|
||
@app.route('/api/calculate/vram')
|
||
def api_calculate_vram():
|
||
"""显存计算"""
|
||
params = request.args.get('params', '7', type=float) # 参数量(B)
|
||
precision = request.args.get('precision', 'fp16', type=str) # 精度
|
||
|
||
# 计算公式
|
||
# FP32: 参数 * 4字节
|
||
# FP16: 参数 * 2字节
|
||
# INT8: 参数 * 1字节
|
||
# INT4: 参数 * 0.5字节
|
||
|
||
bytes_per_param = {
|
||
'fp32': 4,
|
||
'fp16': 2,
|
||
'int8': 1,
|
||
'int4': 0.5
|
||
}
|
||
|
||
multiplier = bytes_per_param.get(precision, 2)
|
||
vram_gb = params * multiplier * 1e9 / (1024**3) # 转换为GB
|
||
|
||
# 加上KV cache和激活值估算(约30%额外开销)
|
||
total_vram = vram_gb * 1.3
|
||
|
||
# 推荐GPU
|
||
gpus = load_data(GPUS_FILE)
|
||
suitable_gpus = [g for g in gpus if g.get('memory_gb', 0) >= total_vram]
|
||
|
||
return jsonify({
|
||
'model_vram': round(vram_gb, 2),
|
||
'total_vram': round(total_vram, 2),
|
||
'suitable_gpus': suitable_gpus
|
||
})
|
||
|
||
@app.route('/api/stats')
|
||
def api_stats():
|
||
"""统计数据"""
|
||
models = load_data(MODELS_FILE)
|
||
gpus = load_data(GPUS_FILE)
|
||
cpus = load_data(CPUS_FILE)
|
||
categories = load_data(CATEGORIES_FILE)
|
||
knowledge = load_data(KNOWLEDGE_FILE)
|
||
|
||
return jsonify({
|
||
'models_count': len(models),
|
||
'gpus_count': len(gpus),
|
||
'cpus_count': len(cpus),
|
||
'categories_count': len(categories),
|
||
'knowledge_count': len(knowledge),
|
||
'latest_models': sorted(models, key=lambda x: x.get('created_at', ''), reverse=True)[:5]
|
||
})
|
||
|
||
# ============ 分类管理API ============
|
||
|
||
@app.route('/api/categories')
|
||
def api_categories():
|
||
"""获取分类列表"""
|
||
categories = load_data(CATEGORIES_FILE)
|
||
return jsonify(categories)
|
||
|
||
@app.route('/api/categories', methods=['POST'])
|
||
def api_create_category():
|
||
"""创建新分类"""
|
||
data = request.get_json()
|
||
categories = load_data(CATEGORIES_FILE)
|
||
|
||
import uuid
|
||
data['id'] = uuid.uuid4().hex[:12]
|
||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
categories.append(data)
|
||
save_data(CATEGORIES_FILE, categories)
|
||
|
||
return jsonify(data)
|
||
|
||
@app.route('/api/categories/<category_id>', methods=['PUT'])
|
||
def api_update_category(category_id):
|
||
"""更新分类"""
|
||
data = request.get_json()
|
||
categories = load_data(CATEGORIES_FILE)
|
||
|
||
category = next((c for c in categories if c['id'] == category_id), None)
|
||
if not category:
|
||
return jsonify({'error': 'Category not found'}), 404
|
||
|
||
category.update(data)
|
||
category['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_data(CATEGORIES_FILE, categories)
|
||
|
||
return jsonify(category)
|
||
|
||
@app.route('/api/categories/<category_id>', methods=['DELETE'])
|
||
def api_delete_category(category_id):
|
||
"""删除分类"""
|
||
categories = load_data(CATEGORIES_FILE)
|
||
categories = [c for c in categories if c['id'] != category_id]
|
||
save_data(CATEGORIES_FILE, categories)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
# ============ 知识库管理API ============
|
||
|
||
@app.route('/api/knowledge')
|
||
def api_knowledge():
|
||
"""获取知识列表"""
|
||
knowledge = load_data(KNOWLEDGE_FILE)
|
||
|
||
# 搜索过滤
|
||
keyword = request.args.get('q', '').strip().lower()
|
||
if keyword:
|
||
knowledge = [k for k in knowledge if keyword in k.get('title', '').lower() or
|
||
keyword in k.get('content', '').lower()]
|
||
|
||
# 分类过滤
|
||
category = request.args.get('category', '')
|
||
if category:
|
||
knowledge = [k for k in knowledge if k.get('category') == category]
|
||
|
||
return jsonify(sorted(knowledge, key=lambda x: x.get('order', 0)))
|
||
|
||
@app.route('/api/knowledge/<knowledge_id>')
|
||
def api_knowledge_detail(knowledge_id):
|
||
"""获取单个知识详情"""
|
||
knowledge = load_data(KNOWLEDGE_FILE)
|
||
item = next((k for k in knowledge if k['id'] == knowledge_id), None)
|
||
|
||
if not item:
|
||
return jsonify({'error': 'Knowledge not found'}), 404
|
||
|
||
return jsonify(item)
|
||
|
||
@app.route('/api/knowledge', methods=['POST'])
|
||
def api_create_knowledge():
|
||
"""创建新知识"""
|
||
data = request.get_json()
|
||
knowledge = load_data(KNOWLEDGE_FILE)
|
||
|
||
import uuid
|
||
data['id'] = uuid.uuid4().hex[:12]
|
||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
if 'order' not in data:
|
||
data['order'] = len(knowledge)
|
||
|
||
knowledge.append(data)
|
||
save_data(KNOWLEDGE_FILE, knowledge)
|
||
|
||
return jsonify(data)
|
||
|
||
@app.route('/api/knowledge/<knowledge_id>', methods=['PUT'])
|
||
def api_update_knowledge(knowledge_id):
|
||
"""更新知识"""
|
||
data = request.get_json()
|
||
knowledge = load_data(KNOWLEDGE_FILE)
|
||
|
||
item = next((k for k in knowledge if k['id'] == knowledge_id), None)
|
||
if not item:
|
||
return jsonify({'error': 'Knowledge not found'}), 404
|
||
|
||
item.update(data)
|
||
item['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
save_data(KNOWLEDGE_FILE, knowledge)
|
||
|
||
return jsonify(item)
|
||
|
||
@app.route('/api/knowledge/<knowledge_id>', methods=['DELETE'])
|
||
def api_delete_knowledge(knowledge_id):
|
||
"""删除知识"""
|
||
knowledge = load_data(KNOWLEDGE_FILE)
|
||
knowledge = [k for k in knowledge if k['id'] != knowledge_id]
|
||
save_data(KNOWLEDGE_FILE, knowledge)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
if __name__ == '__main__':
|
||
print("=" * 50)
|
||
print("ParamHub - 参数百科")
|
||
print("=" * 50)
|
||
print(f"访问地址: http://localhost:19010")
|
||
print("=" * 50)
|
||
|
||
app.run(host='0.0.0.0', port=19010, debug=True) |