Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a9cbd1b2ba | |||
| 685582b7e6 |
4
app.py
4
app.py
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
ParamHub - 参数百科
|
||||
AI大模型与硬件参数速查平台
|
||||
v1.6.0 - 后台管理添加大模型接口配置功能
|
||||
v1.7.0 - 支持子类别配置和关键特性显示
|
||||
"""
|
||||
|
||||
from flask import Flask, render_template, jsonify, request
|
||||
@@ -1402,7 +1402,7 @@ def api_delete_image(filename):
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("=" * 50)
|
||||
print("ParamHub - 参数百科 v1.6.0")
|
||||
print("ParamHub - 参数百科 v1.7.0")
|
||||
print("=" * 50)
|
||||
print(f"访问地址: http://localhost:19010")
|
||||
print(f"后台管理: http://localhost:19010/admin")
|
||||
|
||||
@@ -5,7 +5,54 @@
|
||||
"icon": "ri-robot-line",
|
||||
"color": "blue",
|
||||
"description": "大语言模型、图像模型等AI模型参数",
|
||||
"order": 1
|
||||
"order": 1,
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "chat",
|
||||
"name": "对话模型",
|
||||
"icon": "ri-chat-3-line",
|
||||
"key_features": ["context_length", "mmlu", "input_price", "output_price"],
|
||||
"feature_labels": {
|
||||
"context_length": "上下文",
|
||||
"mmlu": "MMLU",
|
||||
"input_price": "输入价",
|
||||
"output_price": "输出价"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "code",
|
||||
"name": "代码模型",
|
||||
"icon": "ri-code-line",
|
||||
"key_features": ["humaneval", "context_length", "input_price"],
|
||||
"feature_labels": {
|
||||
"humaneval": "HumanEval",
|
||||
"context_length": "上下文",
|
||||
"input_price": "输入价"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "reasoning",
|
||||
"name": "推理模型",
|
||||
"icon": "ri-lightbulb-line",
|
||||
"key_features": ["reasoning_capability", "mmlu", "context_length"],
|
||||
"feature_labels": {
|
||||
"reasoning_capability": "推理能力",
|
||||
"mmlu": "MMLU",
|
||||
"context_length": "上下文"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vision",
|
||||
"name": "视觉模型",
|
||||
"icon": "ri-image-line",
|
||||
"key_features": ["vision_capability", "multimodal", "context_length"],
|
||||
"feature_labels": {
|
||||
"vision_capability": "视觉能力",
|
||||
"multimodal": "多模态",
|
||||
"context_length": "上下文"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gpus",
|
||||
@@ -13,7 +60,45 @@
|
||||
"icon": "ri-cpu-line",
|
||||
"color": "green",
|
||||
"description": "NVIDIA、AMD等GPU显卡规格参数",
|
||||
"order": 2
|
||||
"order": 2,
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "gaming",
|
||||
"name": "游戏显卡",
|
||||
"icon": "ri-gamepad-line",
|
||||
"key_features": ["memory_gb", "cuda_cores", "price_usd", "fp16_tflops"],
|
||||
"feature_labels": {
|
||||
"memory_gb": "显存",
|
||||
"cuda_cores": "CUDA核心",
|
||||
"price_usd": "价格",
|
||||
"fp16_tflops": "FP16性能"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "professional",
|
||||
"name": "专业显卡",
|
||||
"icon": "ri-building-line",
|
||||
"key_features": ["memory_gb", "tensor_cores", "memory_bandwidth_gbs", "price_usd"],
|
||||
"feature_labels": {
|
||||
"memory_gb": "显存",
|
||||
"tensor_cores": "Tensor核心",
|
||||
"memory_bandwidth_gbs": "带宽",
|
||||
"price_usd": "价格"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "datacenter",
|
||||
"name": "数据中心",
|
||||
"icon": "ri-server-line",
|
||||
"key_features": ["memory_gb", "tensor_cores", "memory_bandwidth_gbs", "fp16_tflops"],
|
||||
"feature_labels": {
|
||||
"memory_gb": "显存",
|
||||
"tensor_cores": "Tensor核心",
|
||||
"memory_bandwidth_gbs": "带宽",
|
||||
"fp16_tflops": "FP16性能"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cpus",
|
||||
@@ -21,7 +106,45 @@
|
||||
"icon": "ri-cpu-line",
|
||||
"color": "purple",
|
||||
"description": "Intel、AMD等CPU处理器参数",
|
||||
"order": 3
|
||||
"order": 3,
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "desktop",
|
||||
"name": "桌面CPU",
|
||||
"icon": "ri-computer-line",
|
||||
"key_features": ["cores", "threads", "boost_clock_ghz", "price_usd"],
|
||||
"feature_labels": {
|
||||
"cores": "核心",
|
||||
"threads": "线程",
|
||||
"boost_clock_ghz": "加速频率",
|
||||
"price_usd": "价格"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"name": "服务器CPU",
|
||||
"icon": "ri-server-line",
|
||||
"key_features": ["cores", "threads", "l3_cache_mb", "tdp_watts"],
|
||||
"feature_labels": {
|
||||
"cores": "核心",
|
||||
"threads": "线程",
|
||||
"l3_cache_mb": "L3缓存",
|
||||
"tdp_watts": "功耗"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mobile",
|
||||
"name": "移动CPU",
|
||||
"icon": "ri-smartphone-line",
|
||||
"key_features": ["cores", "threads", "base_clock_ghz", "tdp_watts"],
|
||||
"feature_labels": {
|
||||
"cores": "核心",
|
||||
"threads": "线程",
|
||||
"base_clock_ghz": "基础频率",
|
||||
"tdp_watts": "功耗"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "phones",
|
||||
@@ -30,7 +153,33 @@
|
||||
"color": "orange",
|
||||
"description": "各品牌手机参数规格",
|
||||
"order": 4,
|
||||
"visible": false
|
||||
"visible": true,
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "flagship",
|
||||
"name": "旗舰手机",
|
||||
"icon": "ri-star-line",
|
||||
"key_features": ["processor", "ram_gb", "storage_gb", "price"],
|
||||
"feature_labels": {
|
||||
"processor": "处理器",
|
||||
"ram_gb": "内存",
|
||||
"storage_gb": "存储",
|
||||
"price": "价格"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "midrange",
|
||||
"name": "中端手机",
|
||||
"icon": "ri-price-tag-3-line",
|
||||
"key_features": ["processor", "ram_gb", "battery_mah", "price"],
|
||||
"feature_labels": {
|
||||
"processor": "处理器",
|
||||
"ram_gb": "内存",
|
||||
"battery_mah": "电池",
|
||||
"price": "价格"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "laptops",
|
||||
@@ -38,7 +187,33 @@
|
||||
"icon": "ri-macbook-line",
|
||||
"color": "teal",
|
||||
"description": "笔记本电脑、台式机参数",
|
||||
"order": 5
|
||||
"order": 5,
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "gaming-laptop",
|
||||
"name": "游戏笔记本",
|
||||
"icon": "ri-gamepad-line",
|
||||
"key_features": ["processor", "gpu", "ram_gb", "price"],
|
||||
"feature_labels": {
|
||||
"processor": "处理器",
|
||||
"gpu": "显卡",
|
||||
"ram_gb": "内存",
|
||||
"price": "价格"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "business-laptop",
|
||||
"name": "商务笔记本",
|
||||
"icon": "ri-briefcase-line",
|
||||
"key_features": ["processor", "ram_gb", "weight_kg", "price"],
|
||||
"feature_labels": {
|
||||
"processor": "处理器",
|
||||
"ram_gb": "内存",
|
||||
"weight_kg": "重量",
|
||||
"price": "价格"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "021dc76d36be",
|
||||
@@ -47,6 +222,66 @@
|
||||
"color": "red",
|
||||
"order": 6,
|
||||
"description": "汽车方面",
|
||||
"created_at": "2026-04-09 10:09:01"
|
||||
"created_at": "2026-04-09 10:09:01",
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "sedan",
|
||||
"name": "轿车",
|
||||
"icon": "ri-car-line",
|
||||
"key_features": ["engine", "power_kw", "price"],
|
||||
"feature_labels": {
|
||||
"engine": "发动机",
|
||||
"power_kw": "功率",
|
||||
"price": "价格"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "suv",
|
||||
"name": "SUV",
|
||||
"icon": "ri-truck-line",
|
||||
"key_features": ["engine", "seats", "price"],
|
||||
"feature_labels": {
|
||||
"engine": "发动机",
|
||||
"seats": "座位数",
|
||||
"price": "价格"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "71fa2b4d818f",
|
||||
"name": "摄像",
|
||||
"icon": "ri-camera-line",
|
||||
"color": "blue",
|
||||
"order": 0,
|
||||
"visible": true,
|
||||
"description": "相机、摄像机等",
|
||||
"created_at": "2026-04-25 16:38:47",
|
||||
"subcategories": [
|
||||
{
|
||||
"id": "mirrorless",
|
||||
"name": "无反相机",
|
||||
"icon": "ri-camera-line",
|
||||
"key_features": ["sensor", "megapixels", "video_resolution", "price"],
|
||||
"feature_labels": {
|
||||
"sensor": "传感器",
|
||||
"megapixels": "像素",
|
||||
"video_resolution": "视频",
|
||||
"price": "价格"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dslr",
|
||||
"name": "单反相机",
|
||||
"icon": "ri-camera-2-line",
|
||||
"key_features": ["sensor", "megapixels", "lens_mount", "price"],
|
||||
"feature_labels": {
|
||||
"sensor": "传感器",
|
||||
"megapixels": "像素",
|
||||
"lens_mount": "卡口",
|
||||
"price": "价格"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -922,6 +922,13 @@
|
||||
else if (key === 'images') {
|
||||
try { data[key] = JSON.parse(value); } catch { data[key] = []; }
|
||||
}
|
||||
else if (key === 'subcategories') {
|
||||
// 解析子类别JSON
|
||||
try { data[key] = JSON.parse(value); } catch {
|
||||
alert('子类别JSON格式错误,请检查格式');
|
||||
return;
|
||||
}
|
||||
}
|
||||
else data[key] = value;
|
||||
}
|
||||
});
|
||||
@@ -1096,6 +1103,9 @@
|
||||
|
||||
// 表单模板
|
||||
function getCategoryForm(data = {}) {
|
||||
const subcategories = data.subcategories || [];
|
||||
const subcategoriesJson = JSON.stringify(subcategories, null, 2);
|
||||
|
||||
return `<form id="itemForm" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">ID *</label><input type="text" name="id" value="${data.id || ''}" ${data.id ? 'readonly' : ''} required class="w-full px-3 py-2 border rounded-lg ${data.id ? 'bg-gray-100' : ''}"></div>
|
||||
@@ -1116,6 +1126,24 @@
|
||||
</select></div>
|
||||
</div>
|
||||
<div><label class="text-sm text-gray-600 mb-1 block">描述</label><textarea name="description" rows="2" class="w-full px-3 py-2 border rounded-lg">${data.description || ''}</textarea></div>
|
||||
|
||||
<!-- 子类别配置 -->
|
||||
<div class="border-t pt-4">
|
||||
<label class="text-sm text-gray-600 mb-2 block"><i class="ri-folder-line mr-1"></i>子类别配置(JSON格式)</label>
|
||||
<div class="bg-blue-50 rounded-lg p-3 mb-2 text-xs text-blue-700">
|
||||
<p class="mb-2">子类别配置示例:</p>
|
||||
<pre class="bg-blue-100 p-2 rounded overflow-x-auto">[
|
||||
{
|
||||
"id": "chat",
|
||||
"name": "对话模型",
|
||||
"icon": "ri-chat-3-line",
|
||||
"key_features": ["context_length", "mmlu"],
|
||||
"feature_labels": {"context_length": "上下文", "mmlu": "MMLU"}
|
||||
}
|
||||
]</pre>
|
||||
</div>
|
||||
<textarea name="subcategories" rows="8" class="w-full px-3 py-2 border rounded-lg font-mono text-sm" placeholder='[]'>${subcategoriesJson}</textarea>
|
||||
</div>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,19 @@
|
||||
<p class="text-gray-500 mt-1">AI大模型参数规格一览</p>
|
||||
</div>
|
||||
|
||||
<!-- 子类别选择器 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="text-sm text-gray-600"><i class="ri-folder-line mr-1"></i>子类别:</span>
|
||||
</div>
|
||||
<div class="flex gap-2" id="subcategoryTabs">
|
||||
<button onclick="selectSubcategory('')" class="px-4 py-2 bg-indigo-600 text-white rounded-lg text-sm" id="subcat-all">
|
||||
<i class="ri-apps-line mr-1"></i>全部
|
||||
</button>
|
||||
<!-- 动态加载子类别 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和筛选 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-6">
|
||||
<div class="flex gap-4 items-center">
|
||||
@@ -101,12 +114,38 @@
|
||||
<script>
|
||||
let allModels = [];
|
||||
let categories = [];
|
||||
let currentCategory = null;
|
||||
let currentSubcategory = '';
|
||||
|
||||
// 子类别默认特性配置
|
||||
const DEFAULT_KEY_FEATURES = {
|
||||
'chat': ['context_length', 'mmlu', 'input_price', 'output_price'],
|
||||
'code': ['humaneval', 'context_length', 'input_price'],
|
||||
'reasoning': ['mmlu', 'context_length', 'parameters'],
|
||||
'vision': ['context_length', 'mmlu', 'input_price']
|
||||
};
|
||||
|
||||
const FEATURE_LABELS = {
|
||||
'context_length': '上下文',
|
||||
'mmlu': 'MMLU',
|
||||
'humaneval': 'HumanEval',
|
||||
'input_price': '输入价',
|
||||
'output_price': '输出价',
|
||||
'parameters': '参数量',
|
||||
'reasoning_capability': '推理',
|
||||
'vision_capability': '视觉',
|
||||
'multimodal': '多模态'
|
||||
};
|
||||
|
||||
// 加载导航栏
|
||||
async function loadNav() {
|
||||
const res = await fetch('/api/categories');
|
||||
categories = await res.json();
|
||||
|
||||
// 获取当前类别的子类别
|
||||
currentCategory = categories.find(c => c.id === 'ai-models');
|
||||
renderSubcategoryTabs();
|
||||
|
||||
const builtinPages = [
|
||||
{name: '首页', href: '/'},
|
||||
{name: '工具', href: '/tools'},
|
||||
@@ -134,6 +173,49 @@
|
||||
|
||||
document.getElementById('topNav').innerHTML = navHtml;
|
||||
}
|
||||
|
||||
// 渲染子类别选择器
|
||||
function renderSubcategoryTabs() {
|
||||
const container = document.getElementById('subcategoryTabs');
|
||||
if (!currentCategory || !currentCategory.subcategories) {
|
||||
container.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `<button onclick="selectSubcategory('')" class="px-4 py-2 ${currentSubcategory === '' ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'} rounded-lg text-sm" id="subcat-all">
|
||||
<i class="ri-apps-line mr-1"></i>全部
|
||||
</button>`;
|
||||
|
||||
currentCategory.subcategories.forEach(sub => {
|
||||
const isActive = currentSubcategory === sub.id;
|
||||
html += `<button onclick="selectSubcategory('${sub.id}')" class="px-4 py-2 ${isActive ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'} rounded-lg text-sm" id="subcat-${sub.id}">
|
||||
<i class="${sub.icon || 'ri-folder-line'} mr-1"></i>${sub.name}
|
||||
</button>`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 选择子类别
|
||||
function selectSubcategory(subId) {
|
||||
currentSubcategory = subId;
|
||||
renderSubcategoryTabs();
|
||||
loadModels();
|
||||
}
|
||||
|
||||
// 获取当前子类别的关键特性
|
||||
function getKeyFeatures() {
|
||||
if (!currentSubcategory || !currentCategory || !currentCategory.subcategories) {
|
||||
return ['parameters', 'context_length', 'mmlu', 'input_price'];
|
||||
}
|
||||
|
||||
const subcat = currentCategory.subcategories.find(s => s.id === currentSubcategory);
|
||||
if (subcat && subcat.key_features) {
|
||||
return subcat.key_features;
|
||||
}
|
||||
|
||||
return ['parameters', 'context_length', 'mmlu', 'input_price'];
|
||||
}
|
||||
|
||||
async function loadModels() {
|
||||
const keyword = document.getElementById('searchInput').value.trim();
|
||||
@@ -155,6 +237,21 @@
|
||||
models = models.filter(m => !m.is_open_source);
|
||||
}
|
||||
|
||||
// 子类别过滤(通过模型名称/描述中的关键词判断)
|
||||
if (currentSubcategory && currentCategory && currentCategory.subcategories) {
|
||||
const subcat = currentCategory.subcategories.find(s => s.id === currentSubcategory);
|
||||
if (subcat) {
|
||||
// 简化过滤:根据子类别关键词匹配
|
||||
// 实际应该有 subcategory_id 字段,这里暂时用名称匹配
|
||||
// 用户可以在后台编辑时指定子类别
|
||||
models = models.filter(m => {
|
||||
const subcatField = m.subcategory || m.subcategory_id;
|
||||
if (subcatField) return subcatField === currentSubcategory;
|
||||
return true; // 暂时显示全部,等后台支持子类别字段后再过滤
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderModels(models);
|
||||
}
|
||||
|
||||
@@ -166,7 +263,31 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const html = models.map(m => `
|
||||
// 动态获取关键特性
|
||||
const keyFeatures = getKeyFeatures();
|
||||
|
||||
// 动态表头
|
||||
let headerHtml = `
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-sm font-medium text-gray-600">模型名称</th>
|
||||
<th class="px-4 py-3 text-left text-sm font-medium text-gray-600">厂商</th>
|
||||
`;
|
||||
|
||||
keyFeatures.forEach(f => {
|
||||
headerHtml += `<th class="px-4 py-3 text-left text-sm font-medium text-gray-600">${FEATURE_LABELS[f] || f}</th>`;
|
||||
});
|
||||
|
||||
headerHtml += `
|
||||
<th class="px-4 py-3 text-left text-sm font-medium text-gray-600">类型</th>
|
||||
<th class="px-4 py-3 text-center text-sm font-medium text-gray-600">操作</th>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
document.querySelector('#modelsTable thead').innerHTML = headerHtml;
|
||||
|
||||
// 动态内容
|
||||
const html = models.map(m => {
|
||||
let rowHtml = `
|
||||
<tr class="border-b hover:bg-gray-50 transition ${m.is_pinned ? 'bg-yellow-50' : ''}">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -178,31 +299,57 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-gray-600">${m.organization}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded text-sm">${m.parameters}B</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-gray-600">${formatContext(m.context_length)}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="px-2 py-1 bg-green-100 text-green-700 rounded text-sm">${m.mmlu || '-'}%</span>
|
||||
</td>
|
||||
`;
|
||||
|
||||
// 关键特性列
|
||||
keyFeatures.forEach(f => {
|
||||
const value = formatFeatureValue(f, m);
|
||||
rowHtml += `<td class="px-4 py-3">${value}</td>`;
|
||||
});
|
||||
|
||||
rowHtml += `
|
||||
<td class="px-4 py-3">
|
||||
${m.is_open_source
|
||||
? '<span class="px-2 py-1 bg-emerald-100 text-emerald-700 rounded text-sm">开源</span>'
|
||||
: '<span class="px-2 py-1 bg-gray-100 text-gray-700 rounded text-sm">商业</span>'}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-gray-600">
|
||||
${m.input_price ? `$${m.input_price}/$${m.output_price}` : '免费'}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<button onclick="showDetail('${m.id}')" class="text-indigo-600 hover:text-indigo-800 text-sm">
|
||||
<i class="ri-eye-line mr-1"></i>详情
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
`;
|
||||
|
||||
return rowHtml;
|
||||
}).join('');
|
||||
|
||||
document.getElementById('modelsTable').innerHTML = html;
|
||||
}
|
||||
|
||||
// 格式化特性值
|
||||
function formatFeatureValue(feature, model) {
|
||||
const value = model[feature];
|
||||
|
||||
if (value === null || value === undefined) return '<span class="text-gray-400">-</span>';
|
||||
|
||||
switch (feature) {
|
||||
case 'parameters':
|
||||
return `<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded text-sm">${value}B</span>`;
|
||||
case 'context_length':
|
||||
return `<span class="text-gray-600">${formatContext(value)}</span>`;
|
||||
case 'mmlu':
|
||||
return `<span class="px-2 py-1 bg-green-100 text-green-700 rounded text-sm">${value}%</span>`;
|
||||
case 'humaneval':
|
||||
return `<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded text-sm">${value}%</span>`;
|
||||
case 'input_price':
|
||||
return `<span class="text-sm text-gray-600">$${value || 0}</span>`;
|
||||
case 'output_price':
|
||||
return `<span class="text-sm text-gray-600">$${value || 0}</span>`;
|
||||
default:
|
||||
return `<span class="text-gray-600">${value}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatContext(len) {
|
||||
if (!len) return '-';
|
||||
|
||||
Reference in New Issue
Block a user