""" ParamHub - 参数百科 AI大模型与硬件参数速查平台 v1.1.0 - 新增智能添加和展示开关功能 """ from flask import Flask, render_template, jsonify, request from flask_cors import CORS import json import requests from pathlib import Path from datetime import datetime import uuid 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' # 大模型配置 LLM_CONFIG = { 'base_url': 'http://192.168.2.17:19007/v1', 'api_key': 'xxxx', 'model': 'auto', } 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') # ============ 大模型智能解析 ============ def parse_with_llm(text, category_type): """ 使用大模型解析文本,提取结构化数据 """ # 根据类型定义字段模板 field_templates = { 'model': { 'name': '模型名称', 'organization': '厂商/组织', 'parameters': '参数量(数字,单位B)', 'context_length': '上下文长度(数字)', 'architecture': '架构类型', 'is_open_source': '是否开源(true/false)', 'mmlu': 'MMLU分数(数字)', 'input_price': '输入价格(数字)', 'output_price': '输出价格(数字)', 'license': '许可证', 'description': '简介描述', }, 'gpu': { 'name': 'GPU名称', 'manufacturer': '厂商', 'architecture': '架构', 'memory_gb': '显存大小(数字,单位GB)', 'cuda_cores': 'CUDA核心数(数字)', 'tensor_cores': 'Tensor核心数(数字)', 'memory_bandwidth_gbs': '显存带宽(数字,单位GB/s)', 'fp16_tflops': 'FP16性能(数字,单位TF)', 'price_usd': '价格(数字)', 'release_year': '发布年份(数字)', 'description': '简介描述', }, 'cpu': { 'name': 'CPU名称', 'manufacturer': '厂商', 'architecture': '架构', 'cores': '核心数(数字)', 'threads': '线程数(数字)', 'base_clock_ghz': '基础频率(数字,单位GHz)', 'boost_clock_ghz': '加速频率(数字,单位GHz)', 'l3_cache_mb': 'L3缓存(数字,单位MB)', 'tdp_watts': 'TDP功耗(数字,单位W)', 'price_usd': '价格(数字)', 'description': '简介描述', }, 'dynamic': { 'name': '名称', 'brand': '品牌', 'price': '价格(数字)', 'year': '年份(数字)', 'specs': '规格参数', 'description': '简介描述', }, } fields = field_templates.get(category_type, field_templates['dynamic']) prompt = f"""请解析以下文本,提取结构化数据。 文本内容: {text} 需要提取的字段: {json.dumps(fields, ensure_ascii=False, indent=2)} 要求: 1. 根据文本内容智能提取各个字段的值 2. 数字字段只返回数字,不带单位 3. 如果某字段在文本中没有提及,返回null 4. 返回JSON格式,不要包含任何其他内容 请直接返回JSON数据:""" try: response = requests.post( f"{LLM_CONFIG['base_url']}/chat/completions", headers={ "Content-Type": "application/json", "Authorization": f"Bearer {LLM_CONFIG['api_key']}" }, json={ "model": LLM_CONFIG['model'], "messages": [ {"role": "system", "content": "你是一个数据提取助手,负责从文本中提取结构化数据。只返回JSON,不要其他内容。"}, {"role": "user", "content": prompt} ], "max_tokens": 1000, "temperature": 0.1 }, timeout=30 ) if response.status_code == 200: data = response.json() content = data['choices'][0]['message']['content'].strip() # 清理可能的markdown包裹 if content.startswith('```'): content = content.split('\n', 1)[1] if '\n' in content else content[3:] content = content.rsplit('```', 1)[0] if '```' in content else content # 解析JSON parsed = json.loads(content) # 清理null值 cleaned = {} for k, v in parsed.items(): if v is not None and v != '' and v != 'null': # 尝试转换数字 if isinstance(v, str): try: if '.' in v: cleaned[k] = float(v) else: cleaned[k] = int(v) except: cleaned[k] = v else: cleaned[k] = v return cleaned except Exception as e: print(f"LLM解析失败: {e}") # 降级处理:返回基本结构 return {'name': text[:50], 'description': text} # ============ 页面路由 ============ @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') @app.route('/category/') def category_page(category_id): """动态分类页面""" categories = load_data(CATEGORIES_FILE) category = next((c for c in categories if c['id'] == category_id), None) if not category: return "分类不存在", 404 return render_template('category.html', category=category) # ============ API路由 ============ @app.route('/api/models') def api_models(): """获取模型列表""" models = load_data(MODELS_FILE) # 过滤隐藏项(前台默认不显示visible=false) hide_hidden = request.args.get('all', '0') == '0' if hide_hidden: models = [m for m in models if m.get('visible', True)] # 搜索过滤 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', 'created_at') reverse = request.args.get('order', 'asc') == 'desc' # 安全排序:处理可能的None/缺失值 def safe_sort_key(x, key): val = x.get(key) if val is None: return 0 if key in ['parameters', 'context_length', 'mmlu'] else '' return val if sort_by in ['name', 'parameters', 'context_length', 'mmlu', 'created_at']: models = sorted(models, key=lambda x: safe_sort_key(x, sort_by), reverse=reverse) return jsonify(models) @app.route('/api/models/') 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) data['id'] = uuid.uuid4().hex[:12] data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') data['visible'] = data.get('visible', True) # 默认显示 models.append(data) save_data(MODELS_FILE, models) return jsonify(data) @app.route('/api/models/', 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/', 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/models//visible', methods=['POST']) def api_toggle_model_visible(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 model['visible'] = not model.get('visible', True) save_data(MODELS_FILE, models) return jsonify({'success': True, 'visible': model['visible']}) # ============ 智能添加API ============ @app.route('/api/models/smart-add', methods=['POST']) def api_smart_add_model(): """智能添加模型(粘贴文本解析)""" data = request.get_json() text = data.get('text', '') if not text: return jsonify({'error': '文本不能为空'}), 400 # 大模型解析 parsed = parse_with_llm(text, 'model') # 补充必要字段 parsed['id'] = uuid.uuid4().hex[:12] parsed['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') parsed['visible'] = True parsed['raw_text'] = text # 保存原始文本 # 保存 models = load_data(MODELS_FILE) models.append(parsed) save_data(MODELS_FILE, models) return jsonify(parsed) @app.route('/api/gpus/smart-add', methods=['POST']) def api_smart_add_gpu(): """智能添加GPU""" data = request.get_json() text = data.get('text', '') if not text: return jsonify({'error': '文本不能为空'}), 400 parsed = parse_with_llm(text, 'gpu') parsed['id'] = uuid.uuid4().hex[:12] parsed['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') parsed['visible'] = True parsed['raw_text'] = text gpus = load_data(GPUS_FILE) gpus.append(parsed) save_data(GPUS_FILE, gpus) return jsonify(parsed) @app.route('/api/cpus/smart-add', methods=['POST']) def api_smart_add_cpu(): """智能添加CPU""" data = request.get_json() text = data.get('text', '') if not text: return jsonify({'error': '文本不能为空'}), 400 parsed = parse_with_llm(text, 'cpu') parsed['id'] = uuid.uuid4().hex[:12] parsed['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') parsed['visible'] = True parsed['raw_text'] = text cpus = load_data(CPUS_FILE) cpus.append(parsed) save_data(CPUS_FILE, cpus) return jsonify(parsed) @app.route('/api/items//smart-add', methods=['POST']) def api_smart_add_item(category_id): """智能添加动态分类数据""" data = request.get_json() text = data.get('text', '') if not text: return jsonify({'error': '文本不能为空'}), 400 parsed = parse_with_llm(text, 'dynamic') parsed['id'] = uuid.uuid4().hex[:12] parsed['category_id'] = category_id parsed['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') parsed['visible'] = True parsed['raw_text'] = text items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) items.append(parsed) save_data(items_file, items) return jsonify(parsed) # ============ GPU API ============ @app.route('/api/gpus') def api_gpus(): """获取GPU列表""" gpus = load_data(GPUS_FILE) # 过滤隐藏项 hide_hidden = request.args.get('all', '0') == '0' if hide_hidden: gpus = [g for g in gpus if g.get('visible', True)] 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/') 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/gpus', methods=['POST']) def api_create_gpu(): """创建新GPU""" data = request.get_json() gpus = load_data(GPUS_FILE) data['id'] = uuid.uuid4().hex[:12] data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') data['visible'] = data.get('visible', True) gpus.append(data) save_data(GPUS_FILE, gpus) return jsonify(data) @app.route('/api/gpus/', 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/', 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/gpus//visible', methods=['POST']) def api_toggle_gpu_visible(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 gpu['visible'] = not gpu.get('visible', True) save_data(GPUS_FILE, gpus) return jsonify({'success': True, 'visible': gpu['visible']}) # ============ CPU API ============ @app.route('/api/cpus') def api_cpus(): """获取CPU列表""" cpus = load_data(CPUS_FILE) # 过滤隐藏项 hide_hidden = request.args.get('all', '0') == '0' if hide_hidden: cpus = [c for c in cpus if c.get('visible', True)] 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/') 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/cpus', methods=['POST']) def api_create_cpu(): """创建新CPU""" data = request.get_json() cpus = load_data(CPUS_FILE) data['id'] = uuid.uuid4().hex[:12] data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') data['visible'] = data.get('visible', True) cpus.append(data) save_data(CPUS_FILE, cpus) return jsonify(data) @app.route('/api/cpus/', 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/', 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/cpus//visible', methods=['POST']) def api_toggle_cpu_visible(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 cpu['visible'] = not cpu.get('visible', True) save_data(CPUS_FILE, cpus) return jsonify({'success': True, 'visible': cpu['visible']}) # ============ 搜索和其他API ============ @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 m.get('visible', True) and (keyword in m.get('name', '').lower() or keyword in m.get('organization', '').lower())], 'gpus': [g for g in gpus if g.get('visible', True) and (keyword in g.get('name', '').lower() or keyword in g.get('manufacturer', '').lower())], 'cpus': [c for c in cpus if c.get('visible', True) and (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) precision = request.args.get('precision', 'fp16', type=str) 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) total_vram = vram_gb * 1.3 gpus = load_data(GPUS_FILE) suitable_gpus = [g for g in gpus if g.get('visible', True) and 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([m for m in models if m.get('visible', True)]), 'gpus_count': len([g for g in gpus if g.get('visible', True)]), 'cpus_count': len([c for c in cpus if c.get('visible', True)]), 'categories_count': len([c for c in categories if c.get('visible', True)]), 'knowledge_count': len(knowledge), 'latest_models': sorted([m for m in models if m.get('visible', True)], key=lambda x: x.get('created_at', ''), reverse=True)[:5] }) # ============ 分类管理API ============ @app.route('/api/categories') def api_categories(): """获取分类列表""" categories = load_data(CATEGORIES_FILE) # 过滤隐藏项 hide_hidden = request.args.get('all', '0') == '0' if hide_hidden: categories = [c for c in categories if c.get('visible', True)] return jsonify(sorted(categories, key=lambda x: x.get('order', 0))) @app.route('/api/categories', methods=['POST']) def api_create_category(): """创建新分类""" data = request.get_json() categories = load_data(CATEGORIES_FILE) data['id'] = uuid.uuid4().hex[:12] data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') data['visible'] = data.get('visible', True) categories.append(data) save_data(CATEGORIES_FILE, categories) return jsonify(data) @app.route('/api/categories/', 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/', 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}) @app.route('/api/categories//visible', methods=['POST']) def api_toggle_category_visible(category_id): """切换分类显示状态""" 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['visible'] = not category.get('visible', True) save_data(CATEGORIES_FILE, categories) return jsonify({'success': True, 'visible': category['visible']}) # ============ 知识库管理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/') 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) 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/', 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/', 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}) # ============ 动态分类数据API ============ @app.route('/api/items/') def api_items(category_id): """获取分类下的数据列表""" items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) # 过滤隐藏项 hide_hidden = request.args.get('all', '0') == '0' if hide_hidden: items = [i for i in items if i.get('visible', True)] return jsonify(items) @app.route('/api/items//') def api_item_detail(category_id, item_id): """获取单个数据详情""" items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) item = next((i for i in items if i['id'] == item_id), None) if not item: return jsonify({'error': 'Item not found'}), 404 return jsonify(item) @app.route('/api/items/', methods=['POST']) def api_create_item(category_id): """创建新数据""" data = request.get_json() items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) data['id'] = uuid.uuid4().hex[:12] data['category_id'] = category_id data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') data['visible'] = data.get('visible', True) items.append(data) save_data(items_file, items) return jsonify(data) @app.route('/api/items//', methods=['PUT']) def api_update_item(category_id, item_id): """更新数据""" data = request.get_json() items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) item = next((i for i in items if i['id'] == item_id), None) if not item: return jsonify({'error': 'Item not found'}), 404 item.update(data) item['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') save_data(items_file, items) return jsonify(item) @app.route('/api/items//', methods=['DELETE']) def api_delete_item(category_id, item_id): """删除数据""" items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) items = [i for i in items if i['id'] != item_id] save_data(items_file, items) return jsonify({'success': True}) @app.route('/api/items///visible', methods=['POST']) def api_toggle_item_visible(category_id, item_id): """切换动态数据显示状态""" items_file = DATA_DIR / f'items_{category_id}.json' items = load_data(items_file) item = next((i for i in items if i['id'] == item_id), None) if not item: return jsonify({'error': 'Item not found'}), 404 item['visible'] = not item.get('visible', True) save_data(items_file, items) return jsonify({'success': True, 'visible': item['visible']}) if __name__ == '__main__': print("=" * 50) print("ParamHub - 参数百科 v1.1.0") print("=" * 50) print(f"访问地址: http://localhost:19010") print(f"后台管理: http://localhost:19010/admin") print("=" * 50) app.run(host='0.0.0.0', port=19010, debug=True)