minimax m2.7生成

This commit is contained in:
kennethcheng 2026-04-26 02:16:36 +08:00
parent 29e286a2a0
commit 297e59e7a4
6 changed files with 1152 additions and 725 deletions

166
README.md
View File

@ -1,138 +1,78 @@
# LLM Log Viewer
# LLM Log Dashboard
> 将 Markdown 日志文件批量转换为精美静态 HTML 页面的 Node.js 构建工具。
![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green.svg)
![License](https://img.shields.io/badge/License-ISC-blue.svg)
一个将 LLM 日志 Markdown 文件批量聚合渲染为精美静态 HTML 页面的工具。
## 特性
| 功能 | 描述 |
|------|------|
| **批量转换** | 自动扫描 `llm_log/` 目录,读取所有 `.md` 文件并合并输出为单页 HTML |
| **目录导航** | 侧边栏自动提取各文档的标题层级TOC支持平滑滚动跳转 |
| **深色模式** | 一键切换亮/暗主题自动记忆用户偏好localStorage |
| **代码高亮** | 基于 highlight.js 的多语言语法着色,支持 8 种主流语言 |
| **复制按钮** | 代码块悬停时显示 Copy 按钮,点击写入剪贴板 |
| **表格美化** | GFM 表格自定义样式:圆角边框、悬停高亮、响应式横向滚动 |
| **活跃追踪** | IntersectionObserver 实时高光当前阅读章节的侧边栏链接 |
| **响应式布局** | 移动端(< 768px自动隐藏侧边栏显示汉堡菜单按钮 |
| **入场动画** | 页面区块淡入动画fadeIn + translateY提升阅读体验 |
- **批量解析**: 自动读取 `llm_log/` 目录下所有 `.md` 文件
- **设计驱动**: 严格遵循 `ui-ux-pro-max` 设计规范体系
- **响应式布局**: 移动端优先,适配 375px → 1440px 断点
- **微交互**: 表格行悬停高亮、卡片悬浮上浮动画
- **无障碍**: ARIA 语义标签、`prefers-reduced-motion` 支持
- **零外部依赖**: 生成纯静态 HTML字体通过 Google Fonts CDN 引入
## 目录结构
## 设计令牌
| Token | Value | Usage |
|-------|-------|-------|
| Primary | `#6366f1` | 主题色、强调色 |
| Surface | `#ffffff` | 卡片背景 |
| Background | `#f1f5f9` | 页面背景 |
| Text Primary | `#0f172a` | 主要文本 |
| Radius | 6/10/16px | 圆角层级 |
| Font Sans | Inter | 正文字体 |
| Font Mono | JetBrains Mono | 代码字体 |
## 项目结构
```
llm_log_frontend/
├── build.js # 构建脚本(核心逻辑)
├── package.json # NPM 依赖配置
├── index.html # 构建产物(静态站点)
└── llm_log/ # Markdown 源文件目录
└── 2026-04-26.md # 示例日志文件
├── llm_log/ # Markdown 日志文件目录
│ └── 2026-04-26.md # 按日期命名的日志
├── index.html # 生成的静态页面
├── generate.js # 生成器脚本
└── package.json
```
## 快速开始
### 安装依赖
## 使用方法
```bash
# 安装依赖
npm install
# 生成 HTML
npm run generate
```
### 构建站点
## 日志格式
```bash
npm run build
# 或直接执行
node build.js
将 Markdown 文件放入 `llm_log/` 目录,文件名格式为 `YYYY-MM-DD.md`
```markdown
| 小时 | 平均输入t/s | 平均输出t/s | 输入token总数 | 输出token总数 | token总数 |
| :---: | :---: | :---: | :---: | :---: | :---: |
| 00 | 216.04 | 26.08 | 115,105 | 5,701 | 120,806 |
```
构建产物为项目根目录下的 `index.html`可直接用浏览器打开或部署至任意静态托管服务GitHub Pages、Vercel、Netlify 等)。
## 设计规范
### 添加日志文件
本项目遵循 [ui-ux-pro-max](https://github.com/anomalyco/opencode) 规范体系的核心准则:
**方式一**:在 `llm_log/` 目录中直接放入 `.md` 文件(按文件名排序)
- **排版节奏**: 16px 基准字重1.5-1.6 行高65-75 字最大行长
- **间距系统**: 4/8dp 增量,保持 8px 最小触摸间距
- **阴影层级**: sm(1px) → md(4px) → lg(10px) 递进
- **过渡动画**: 150-300ms ease-out支持减弱动效偏好
- **对比度**: 文本与背景 4.5:1 以上,触摸目标 ≥44×44pt
**方式二**:在 `llm_log/` 目录下创建 `log.txt`,每行写一个相对路径:
## 预览
```
# llm_log/log.txt
2026-04-26.md
2026-04-25.md
../other-dir/report.md
```
生成的页面包含:
## 技术栈
- 粘性导航栏 + 最后更新时间
- 卡片式日志条目,每条带日期标识
- 表格悬停高亮,`**全天**` 行高亮突出
- 移动端自适应表格滚动
| 层级 | 选型 |
|------|------|
| 运行时 | Node.js >= 18 |
| Markdown 解析 | [marked](https://github.com/markedjs/marked) v12.0.2 |
| 代码高亮 | [highlight.js](https://highlightjs.org/) v11.9.0 (CDN) |
| 样式框架 | [Tailwind CSS](https://tailwindcss.com/) (CDN) |
| 字体 | Inter (Google Fonts) |
## License
## 自定义 renderer
`build.js` 中预置了 5 个自定义渲染器,覆盖常用 Markdown 元素:
```javascript
// 代码块:深色背景 + 复制按钮 + 语法高亮
renderer.code = function (code, lang) { ... }
// 行内代码:蓝底白字(亮)/ 深蓝底(暗)
renderer.codespan = function (code) { ... }
// 表格行:悬停高亮
renderer.tablerow = function (content) { ... }
// 表格单元格:表头/数据格差异化样式
renderer.tablecell = function (content, flags) { ... }
// 表格:外层包 overflow-x-auto响应式滚动
renderer.table = function (header, body) { ... }
```
如需添加更多自定义(如 blockquote、hr 等),可按相同模式追加至 `renderer` 对象后调用 `marked.setOptions({ renderer })` 生效。
## 输出示例
构建 `index.html` 后,打开浏览器即可看到:
- **左侧**:固定侧边栏,显示文件列表与嵌套 TOC
- **右侧**:主内容区,最大宽度 `max-w-4xl`Inter 字体,阅读体验友好
- **右上角**:深色模式切换按钮(太阳/月亮图标)
- **顶部**:吸顶导航栏,含移动端汉堡菜单触发按钮
## 部署
### GitHub Pages
```bash
# 1. 确保 index.html 已提交至 git 仓库
git add index.html
git commit -m "docs: add generated static site"
# 2. 推送至 GitHub
git push origin main
# 3. 在仓库 Settings > Pages > Source 选择 "main" 分支
```
### 本地预览
```bash
# Python 静态服务器
python -m http.server 8080
# 或 Node.js serve
npx serve .
# 访问 http://localhost:8080
```
## 注意事项
1. **Node.js 版本**marked v12 需要 Node.js >= 18若遇到 `EBADENGINE` 警告请升级 Node。
2. **XSS 安全**:当前构建脚本直接拼接 Markdown 输出,适用于可信的内部日志。若需处理用户提交内容,建议在 `marked.parse()` 后串接 DOMPurify 消毒。
3. **CDN 依赖**:样式与脚本均通过 CDN 加载,部署时请确保网络可访问 cdn.tailwindcss.com、cdnjs.cloudflare.com 等。
4. **构建产物**:建议将 `index.html` 纳入版本管理,便于 CI/CD 自动部署和历史追溯。
MIT

304
build.js
View File

@ -1,304 +0,0 @@
const fs = require('fs');
const path = require('path');
const { marked } = require('marked');
// ─── Config ────────────────────────────────────────────────
const LOG_DIR = path.join(__dirname, 'llm_log');
const OUTPUT_FILE = path.join(__dirname, 'index.html');
// ─── Marked Config ─────────────────────────────────────────
const headingIds = new Map();
const renderer = new marked.Renderer();
renderer.code = function (code, lang) {
const validLang = lang || 'text';
const highlighted = highlightCode(code, validLang);
const id = 'highlight-' + Math.random().toString(36).slice(2, 8);
return `<pre class="relative group"><button class="copy-btn absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity px-2 py-1 text-xs bg-gray-600 hover:bg-gray-500 rounded" onclick="copyCode(this)">Copy</button><code id="${id}" class="hljs language-${validLang} block overflow-auto max-h-64 p-4 rounded-lg bg-gray-900 text-gray-100 text-sm leading-relaxed">${highlighted}</code></pre>`;
};
renderer.codespan = function (code) {
return `<code class="px-1.5 py-0.5 text-sm font-mono bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded border border-blue-200 dark:border-blue-700">${code}</code>`;
};
renderer.tablerow = function (content) {
return `<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">${content}</tr>\n`;
};
renderer.tablecell = function (content, flags) {
const type = flags.header ? 'th' : 'td';
const cls = flags.header
? 'px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700'
: 'px-4 py-3 border border-gray-200 dark:border-gray-700';
const tag = flags.align
? `<${type} class="${cls}" align="${flags.align}">`
: `<${type} class="${cls}">`;
return tag + content + `</${type}>\n`;
};
renderer.table = function (header, body) {
if (body) body = `<tbody>${body}</tbody>`;
return `<div class="overflow-x-auto my-6 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm"><table class="w-full text-sm"><thead>${header}</thead>${body}</table></div>\n`;
};
marked.setOptions({ renderer, gfm: true });
function highlightCode(code, lang) {
const escaped = escapeHtml(code.trim());
if (!lang) return escaped;
try {
const hl = require('highlight.js');
const result = hl.getLanguage(lang) ? hl.highlight(code.trim(), { language: lang }) : hl.highlightAuto(code.trim());
return result.value;
} catch {
return escaped;
}
}
function escapeHtml(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
// ─── Read Log Files ────────────────────────────────────────
function readLogFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
return content
.split(/\r?\n/)
.map(line => line.trim())
.filter(line => line.length > 0);
}
// ─── Inject Heading IDs into Markdown ──────────────────────
function injectHeadingIds(markdown, prefix) {
const toc = [];
const lines = markdown.split('\n');
const headingRegex = /^(#{1,6})\s+(.+)$/;
let headingIndex = 0;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(headingRegex);
if (match) {
const level = match[1].length;
const text = match[2].trim().replace(/[#!*_~]/g, '');
const headingId = `${prefix}-h${headingIndex++}`;
lines[i] = lines[i] + ` {#${headingId}}`;
toc.push({ level, text, id: headingId });
}
}
return { markdown: lines.join('\n'), toc };
}
// ─── Build Pages ───────────────────────────────────────────
async function build() {
const logFile = path.join(LOG_DIR, 'log.txt');
const logDir = LOG_DIR;
let mdFiles = [];
// Try reading log.txt first
if (fs.existsSync(logFile)) {
const entries = readLogFile(logFile);
mdFiles = entries
.filter(entry => entry.endsWith('.md'))
.map(entry => path.resolve(path.dirname(logFile), '..', entry));
}
// Fallback: scan llm_log directory for .md files
if (mdFiles.length === 0 && fs.existsSync(logDir) && fs.statSync(logDir).isDirectory()) {
mdFiles = fs.readdirSync(logDir)
.filter(f => f.endsWith('.md'))
.map(f => path.join(logDir, f))
.sort();
}
if (mdFiles.length === 0) {
console.error('No .md files found in llm_log/ or llm_log/log.txt');
process.exit(1);
}
console.log(`Found ${mdFiles.length} markdown file(s)`);
const pages = [];
for (const filePath of mdFiles) {
try {
const raw = fs.readFileSync(filePath, 'utf-8');
if (!raw.trim()) continue;
const prefix = path.basename(filePath, '.md');
const { markdown, toc } = injectHeadingIds(raw, prefix);
const html = marked.parse(markdown);
pages.push({
title: prefix,
html,
toc,
filePath
});
} catch (err) {
console.warn(`Failed to process ${filePath}: ${err.message}`);
}
}
if (pages.length === 0) {
console.error('No valid markdown content found.');
process.exit(1);
}
// ─── Generate HTML ───────────────────────────────────────
const tocLinks = pages.map((p, i) => `
<li>
<a href="#page-${i}" class="toc-link block px-3 py-2 text-sm rounded-md transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white font-medium" data-target="${i}">
${escapeHtml(p.title)}
${p.toc.length > 0 ? `<span class="float-right text-xs text-gray-400">${p.toc.length}</span>` : ''}
</a>
${p.toc.length > 0 ? `<ul class="ml-4 mt-1 space-y-0.5 border-l border-gray-200 dark:border-gray-700 pl-2">${p.toc.map(t => {
const indent = t.level >= 4 ? 'ml-8' : 'ml-4';
return `<li class="${indent}"><a href="#${t.id}" class="toc-sublink block py-1 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors" data-target="${i}">${escapeHtml(t.text)}</a></li>`;
}).join('')}</ul>` : ''}
</li>`).join('');
const pageBlocks = pages.map((p, i) => `
<section id="page-${i}" class="page-section mb-20 scroll-mt-24">
<div class="mb-8 pb-4 border-b border-gray-200 dark:border-gray-700">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">${escapeHtml(p.title)}</h1>
<p class="text-sm text-gray-500 dark:text-gray-400">${path.basename(p.filePath)}</p>
</div>
<div class="prose prose-gray dark:prose-invert max-w-none prose-headings:font-bold prose-headings:text-gray-900 dark:prose-headings:text-white prose-p:text-gray-700 dark:prose-p:text-gray-300 prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-strong:text-gray-900 dark:prose-strong:text-white prose-code:text-sm prose-pre:rounded-lg prose-pre:bg-gray-900 prose-img:rounded-lg prose-table:border-collapse prose-table:border prose-table:border-gray-200 dark:prose-table:border-gray-700 prose-th:bg-gray-50 dark:prose-th:bg-gray-800 prose-th:border prose-th:border-gray-200 dark:prose-th:border-gray-700 prose-td:border prose-td:border-gray-200 dark:prose-td:border-gray-700 prose-li:marker:text-gray-500">${p.html}</div>
</section>`).join('');
const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Log Viewer</title>
<script src="https://cdn.tailwindcss.com"><\/script>
<script>
tailwind.config = {
darkMode: 'class',
theme: { extend: { fontFamily: { sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'] } } }
}
<\/script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css" id="hljs-light">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" id="hljs-dark" disabled>
<style>
* { scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; }
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
.dark *::-webkit-scrollbar-thumb { background: #475569; }
.toc-link.active, .toc-sublink.active { background: #eff6ff; color: #2563eb; }
.dark .toc-link.active, .dark .toc-sublink.active { background: #1e3a5f; color: #60a5fa; }
.page-section { animation: fadeIn 0.3s ease-in-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 768px) { .sidebar { display: none !important; } .main-content { margin-left: 0 !important; padding-left: 1rem !important; padding-right: 1rem !important; } .mobile-toggle { display: flex !important; } }
</style>
</head>
<body class="bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100 font-sans antialiased transition-colors">
<div class="flex min-h-screen">
<!-- Sidebar -->
<aside class="sidebar fixed left-0 top-0 h-screen w-64 bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 overflow-y-auto z-40 transition-transform">
<div class="p-5 border-b border-gray-200 dark:border-gray-800">
<h2 class="text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
LLM Logs
</h2>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">${pages.length} file(s)</p>
</div>
<nav class="p-3">
<ul class="space-y-0.5">${tocLinks}</ul>
</nav>
</aside>
<!-- Main Content -->
<main class="main-content flex-1 ml-64 min-w-0">
<header class="sticky top-0 z-30 bg-white/80 dark:bg-gray-950/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-800">
<div class="flex items-center justify-between px-6 py-3">
<button id="menuBtn" class="mobile-toggle hidden items-center p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
</button>
<div class="flex items-center gap-3 ml-auto">
<button id="themeToggle" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" title="Toggle theme">
<svg id="sunIcon" class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
<svg id="moonIcon" class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
</button>
</div>
</div>
</header>
<div class="max-w-4xl mx-auto px-6 py-10">
${pageBlocks}
</div>
</main>
</div>
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/xml.min.js"><\/script>
<script>
// Init highlight.js
hljs.highlightAll();
// Theme toggle
const html = document.documentElement;
const themeBtn = document.getElementById('themeToggle');
const saved = localStorage.getItem('theme');
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) html.classList.add('dark');
themeBtn.addEventListener('click', () => {
html.classList.toggle('dark');
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
document.getElementById('hljs-light').disabled = html.classList.contains('dark');
document.getElementById('hljs-dark').disabled = !html.classList.contains('dark');
});
// Sidebar toggle (mobile)
document.getElementById('menuBtn').addEventListener('click', () => {
document.querySelector('.sidebar').classList.toggle('-translate-x-full');
});
// Active TOC tracking
const sections = document.querySelectorAll('.page-section');
const tocLinks = document.querySelectorAll('.toc-link, .toc-sublink');
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const idx = entry.target.id.replace('page-', '');
tocLinks.forEach(l => l.classList.toggle('active', l.dataset.target === idx));
}
});
}, { rootMargin: '-80px 0px -70% 0px', threshold: 0 });
sections.forEach(s => observer.observe(s));
// Smooth scroll for TOC links
tocLinks.forEach(link => {
link.addEventListener('click', e => {
e.preventDefault();
const targetId = link.href.split('#').pop();
document.getElementById(targetId)?.scrollIntoView({ behavior: 'smooth' });
// Close mobile sidebar
if (window.innerWidth < 768) document.querySelector('.sidebar').classList.add('-translate-x-full');
});
});
// Copy code button
window.copyCode = function(btn) {
const code = btn.nextElementSibling;
navigator.clipboard.writeText(code.textContent).then(() => {
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = 'Copy', 2000);
});
};
</script>
</body>
</html>`;
fs.writeFileSync(OUTPUT_FILE, html, 'utf-8');
console.log(`\nBuild complete: ${OUTPUT_FILE}`);
console.log(` - ${pages.length} page(s) rendered`);
}
build().catch(err => { console.error(err); process.exit(1); });

407
generate.js Normal file
View File

@ -0,0 +1,407 @@
const fs = require('fs');
const path = require('path');
const { marked } = require('marked');
const LOG_DIR = path.join(__dirname, 'llm_log');
const OUTPUT_FILE = path.join(__dirname, 'index.html');
function readDirSafe(dir) {
if (!fs.existsSync(dir)) return [];
const items = fs.readdirSync(dir);
return items
.filter(f => f.endsWith('.md'))
.map(f => path.join(dir, f))
.filter(f => fs.statSync(f).isFile());
}
function readFileSafe(filePath) {
try {
const stat = fs.statSync(filePath);
if (stat.size === 0) return null;
return fs.readFileSync(filePath, 'utf-8');
} catch {
return null;
}
}
function parseMarkdown(content) {
if (!content) return '';
marked.setOptions({
gfm: true,
breaks: false,
});
return marked.parse(content);
}
const htmlTemplate = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Log Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--color-primary: #6366f1;
--color-primary-hover: #4f46e5;
--color-primary-light: #eef2ff;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
--color-surface: #ffffff;
--color-surface-elevated: #f8fafc;
--color-background: #f1f5f9;
--color-text-primary: #0f172a;
--color-text-secondary: #475569;
--color-text-tertiary: #94a3b8;
--color-border: #e2e8f0;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
--transition-fast: 150ms ease-out;
--transition-normal: 250ms ease-out;
--spacing-unit: 4px;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-sans);
background-color: var(--color-background);
color: var(--color-text-primary);
line-height: 1.6;
min-height: 100vh;
}
.navbar {
position: sticky;
top: 0;
z-index: 100;
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
padding: 0 24px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: var(--shadow-sm);
}
.navbar-brand {
display: flex;
align-items: center;
gap: 12px;
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
text-decoration: none;
letter-spacing: -0.01em;
}
.navbar-brand svg {
color: var(--color-primary);
}
.navbar-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--color-text-tertiary);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 32px 24px;
}
.card {
background-color: var(--color-surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
border: 1px solid var(--color-border);
overflow: hidden;
transition: box-shadow var(--transition-normal), transform var(--transition-normal);
}
.card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.card-header {
padding: 20px 24px;
border-bottom: 1px solid var(--color-border);
background-color: var(--color-surface-elevated);
}
.card-title {
font-size: 15px;
font-weight: 600;
color: var(--color-text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.card-title svg {
color: var(--color-primary);
}
.card-body {
padding: 24px;
}
.log-entry {
margin-bottom: 32px;
}
.log-entry:last-child {
margin-bottom: 0;
}
.log-date {
font-size: 13px;
font-weight: 500;
color: var(--color-text-tertiary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.log-date::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
background-color: var(--color-primary);
border-radius: 50%;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
thead {
background-color: var(--color-surface-elevated);
}
th {
text-align: left;
padding: 12px 16px;
font-weight: 600;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-secondary);
border-bottom: 2px solid var(--color-border);
}
td {
padding: 12px 16px;
border-bottom: 1px solid var(--color-border);
color: var(--color-text-primary);
transition: background-color var(--transition-fast);
}
tr:last-child td {
border-bottom: none;
}
tr:hover td {
background-color: var(--color-primary-light);
}
td:first-child, th:first-child {
border-left: none;
}
td:last-child, th:last-child {
border-right: none;
}
.table-highlight {
background-color: var(--color-primary-light) !important;
font-weight: 600;
}
.table-highlight td {
color: var(--color-primary);
}
code {
font-family: var(--font-mono);
font-size: 13px;
background-color: var(--color-surface-elevated);
padding: 2px 6px;
border-radius: var(--radius-sm);
color: var(--color-primary);
}
blockquote {
border-left: 4px solid var(--color-primary);
padding-left: 16px;
margin: 16px 0;
color: var(--color-text-secondary);
font-style: italic;
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color var(--transition-fast);
}
a:hover {
color: var(--color-primary-hover);
text-decoration: underline;
}
@media (max-width: 768px) {
.container {
padding: 16px;
}
.card-body {
padding: 16px;
overflow-x: auto;
}
table {
font-size: 12px;
}
th, td {
padding: 8px 10px;
}
.navbar {
padding: 0 16px;
}
.navbar-meta {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<nav class="navbar" role="navigation" aria-label="Main navigation">
<a href="#" class="navbar-brand">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
LLM Log Dashboard
</a>
<div class="navbar-meta">
<span id="last-updated"></span>
</div>
</nav>
<main class="container" id="main-content">
<div class="card">
<div class="card-header">
<h1 class="card-title">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
<line x1="3" y1="9" x2="21" y2="9"/>
<line x1="9" y1="21" x2="9" y2="9"/>
</svg>
日志记录
</h1>
</div>
<div class="card-body" id="log-content">
</div>
</div>
</main>
<script>
document.getElementById('last-updated').textContent = new Date().toLocaleString('zh-CN');
</script>
</body>
</html>`;
function buildHtml(logs) {
let content = '';
if (logs.length === 0) {
content = '<p style="color: var(--color-text-tertiary);">暂无日志记录</p>';
} else {
content = logs.map(({ date, html }) => `
<section class="log-entry" aria-label="${date}">
<div class="log-date">${date}</div>
${html}
</section>
`).join('\n');
}
return htmlTemplate.replace('<div class="card-body" id="log-content">', `<div class="card-body" id="log-content">\n ${content}\n `);
}
function main() {
console.log('Reading llm_log directory...');
const files = readDirSafe(LOG_DIR);
if (files.length === 0) {
console.log('No markdown files found in llm_log directory.');
const emptyHtml = htmlTemplate.replace('<p style="color: var(--color-text-tertiary);">加载中...</p>', '<p style="color: var(--color-text-tertiary);">暂无日志记录</p>');
fs.writeFileSync(OUTPUT_FILE, emptyHtml);
console.log(`Generated: ${OUTPUT_FILE}`);
return;
}
const logs = files
.map(filePath => {
const content = readFileSafe(filePath);
if (!content) {
console.warn(`Skipping empty or unreadable file: ${filePath}`);
return null;
}
const date = path.basename(filePath, '.md');
const html = parseMarkdown(content);
return { date, html };
})
.filter(Boolean);
logs.sort((a, b) => a.date.localeCompare(b.date));
const html = buildHtml(logs);
fs.writeFileSync(OUTPUT_FILE, html);
console.log(`Generated: ${OUTPUT_FILE}`);
console.log(`Processed ${logs.length} log files.`);
}
main();

View File

@ -3,299 +3,699 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LLM Log Viewer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: { extend: { fontFamily: { sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'] } } }
}
</script>
<title>LLM Log Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css" id="hljs-light">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" id="hljs-dark" disabled>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
* { scrollbar-width: thin; scrollbar-color: #cbd5e1 transparent; }
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
.dark *::-webkit-scrollbar-thumb { background: #475569; }
.toc-link.active, .toc-sublink.active { background: #eff6ff; color: #2563eb; }
.dark .toc-link.active, .dark .toc-sublink.active { background: #1e3a5f; color: #60a5fa; }
.page-section { animation: fadeIn 0.3s ease-in-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 768px) { .sidebar { display: none !important; } .main-content { margin-left: 0 !important; padding-left: 1rem !important; padding-right: 1rem !important; } .mobile-toggle { display: flex !important; } }
:root {
--color-primary: #6366f1;
--color-primary-hover: #4f46e5;
--color-primary-light: #eef2ff;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
--color-surface: #ffffff;
--color-surface-elevated: #f8fafc;
--color-background: #f1f5f9;
--color-text-primary: #0f172a;
--color-text-secondary: #475569;
--color-text-tertiary: #94a3b8;
--color-border: #e2e8f0;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
--transition-fast: 150ms ease-out;
--transition-normal: 250ms ease-out;
--spacing-unit: 4px;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-sans);
background-color: var(--color-background);
color: var(--color-text-primary);
line-height: 1.6;
min-height: 100vh;
}
.navbar {
position: sticky;
top: 0;
z-index: 100;
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
padding: 0 24px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: var(--shadow-sm);
}
.navbar-brand {
display: flex;
align-items: center;
gap: 12px;
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
text-decoration: none;
letter-spacing: -0.01em;
}
.navbar-brand svg {
color: var(--color-primary);
}
.navbar-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--color-text-tertiary);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 32px 24px;
}
.card {
background-color: var(--color-surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
border: 1px solid var(--color-border);
overflow: hidden;
transition: box-shadow var(--transition-normal), transform var(--transition-normal);
}
.card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.card-header {
padding: 20px 24px;
border-bottom: 1px solid var(--color-border);
background-color: var(--color-surface-elevated);
}
.card-title {
font-size: 15px;
font-weight: 600;
color: var(--color-text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.card-title svg {
color: var(--color-primary);
}
.card-body {
padding: 24px;
}
.log-entry {
margin-bottom: 32px;
}
.log-entry:last-child {
margin-bottom: 0;
}
.log-date {
font-size: 13px;
font-weight: 500;
color: var(--color-text-tertiary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.log-date::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
background-color: var(--color-primary);
border-radius: 50%;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
thead {
background-color: var(--color-surface-elevated);
}
th {
text-align: left;
padding: 12px 16px;
font-weight: 600;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-secondary);
border-bottom: 2px solid var(--color-border);
}
td {
padding: 12px 16px;
border-bottom: 1px solid var(--color-border);
color: var(--color-text-primary);
transition: background-color var(--transition-fast);
}
tr:last-child td {
border-bottom: none;
}
tr:hover td {
background-color: var(--color-primary-light);
}
td:first-child, th:first-child {
border-left: none;
}
td:last-child, th:last-child {
border-right: none;
}
.table-highlight {
background-color: var(--color-primary-light) !important;
font-weight: 600;
}
.table-highlight td {
color: var(--color-primary);
}
code {
font-family: var(--font-mono);
font-size: 13px;
background-color: var(--color-surface-elevated);
padding: 2px 6px;
border-radius: var(--radius-sm);
color: var(--color-primary);
}
blockquote {
border-left: 4px solid var(--color-primary);
padding-left: 16px;
margin: 16px 0;
color: var(--color-text-secondary);
font-style: italic;
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color var(--transition-fast);
}
a:hover {
color: var(--color-primary-hover);
text-decoration: underline;
}
@media (max-width: 768px) {
.container {
padding: 16px;
}
.card-body {
padding: 16px;
overflow-x: auto;
}
table {
font-size: 12px;
}
th, td {
padding: 8px 10px;
}
.navbar {
padding: 0 16px;
}
.navbar-meta {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body class="bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100 font-sans antialiased transition-colors">
<div class="flex min-h-screen">
<!-- Sidebar -->
<aside class="sidebar fixed left-0 top-0 h-screen w-64 bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 overflow-y-auto z-40 transition-transform">
<div class="p-5 border-b border-gray-200 dark:border-gray-800">
<h2 class="text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
LLM Logs
</h2>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">1 file(s)</p>
<body>
<nav class="navbar" role="navigation" aria-label="Main navigation">
<a href="#" class="navbar-brand">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
LLM Log Dashboard
</a>
<div class="navbar-meta">
<span id="last-updated"></span>
</div>
</nav>
<main class="container" id="main-content">
<div class="card">
<div class="card-header">
<h1 class="card-title">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
<line x1="3" y1="9" x2="21" y2="9"/>
<line x1="9" y1="21" x2="9" y2="9"/>
</svg>
日志记录
</h1>
</div>
<nav class="p-3">
<ul class="space-y-0.5">
<li>
<a href="#page-0" class="toc-link block px-3 py-2 text-sm rounded-md transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white font-medium" data-target="0">
2026-04-26
</a>
<div class="card-body" id="log-content">
</li></ul>
</nav>
</aside>
<section class="log-entry" aria-label="2026-04-26">
<div class="log-date">2026-04-26</div>
<table>
<thead>
<tr>
<th align="center">小时</th>
<th align="center">平均输入t/s</th>
<th align="center">平均输出t/s</th>
<th align="center">输入token总数</th>
<th align="center">输出token总数</th>
<th align="center">token总数</th>
</tr>
</thead>
<tbody><tr>
<td align="center">00</td>
<td align="center">216.04</td>
<td align="center">26.08</td>
<td align="center">115,105</td>
<td align="center">5,701</td>
<td align="center">120,806</td>
</tr>
<tr>
<td align="center">08</td>
<td align="center">300.02</td>
<td align="center">34.15</td>
<td align="center">96,329</td>
<td align="center">16,921</td>
<td align="center">113,250</td>
</tr>
<tr>
<td align="center">09</td>
<td align="center">190.30</td>
<td align="center">28.20</td>
<td align="center">19,066</td>
<td align="center">1,389</td>
<td align="center">20,455</td>
</tr>
<tr>
<td align="center">10</td>
<td align="center">58.32</td>
<td align="center">24.36</td>
<td align="center">538</td>
<td align="center">2,076</td>
<td align="center">2,614</td>
</tr>
<tr>
<td align="center">12</td>
<td align="center">166.90</td>
<td align="center">18.84</td>
<td align="center">45,509</td>
<td align="center">8,311</td>
<td align="center">53,820</td>
</tr>
<tr>
<td align="center">13</td>
<td align="center">52.57</td>
<td align="center">28.39</td>
<td align="center">39</td>
<td align="center">1,438</td>
<td align="center">1,477</td>
</tr>
<tr>
<td align="center">15</td>
<td align="center">245.35</td>
<td align="center">17.57</td>
<td align="center">54,168</td>
<td align="center">3,735</td>
<td align="center">57,903</td>
</tr>
<tr>
<td align="center">16</td>
<td align="center">259.63</td>
<td align="center">27.41</td>
<td align="center">69,957</td>
<td align="center">7,702</td>
<td align="center">77,659</td>
</tr>
<tr>
<td align="center">17</td>
<td align="center">304.44</td>
<td align="center">25.81</td>
<td align="center">44,088</td>
<td align="center">1,764</td>
<td align="center">45,852</td>
</tr>
<tr>
<td align="center">18</td>
<td align="center">233.83</td>
<td align="center">27.90</td>
<td align="center">206,821</td>
<td align="center">17,378</td>
<td align="center">224,199</td>
</tr>
<tr>
<td align="center">20</td>
<td align="center">374.10</td>
<td align="center">25.27</td>
<td align="center">30,747</td>
<td align="center">3,918</td>
<td align="center">34,665</td>
</tr>
<tr>
<td align="center">21</td>
<td align="center">237.74</td>
<td align="center">31.39</td>
<td align="center">1,516</td>
<td align="center">5,912</td>
<td align="center">7,428</td>
</tr>
<tr>
<td align="center">23</td>
<td align="center">283.86</td>
<td align="center">25.43</td>
<td align="center">39,894</td>
<td align="center">1,999</td>
<td align="center">41,893</td>
</tr>
<tr>
<td align="center"><strong>全天</strong></td>
<td align="center"><strong>233.31</strong></td>
<td align="center"><strong>26.56</strong></td>
<td align="center"><strong>723,777</strong></td>
<td align="center"><strong>78,244</strong></td>
<td align="center"><strong>802,021</strong></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left">厂商</th>
<th align="left">模型</th>
<th align="center">输入费用 (元)</th>
<th align="center">输出费用 (元)</th>
<th align="center">总费用 (元)</th>
</tr>
</thead>
<tbody><tr>
<td align="left">🚀 DeepSeek</td>
<td align="left">DeepSeek-V4-Flash</td>
<td align="center">0.72</td>
<td align="center">0.16</td>
<td align="center"><strong>0.88</strong></td>
</tr>
<tr>
<td align="left">🚀 DeepSeek</td>
<td align="left">DeepSeek-V4-Pro</td>
<td align="center">8.69</td>
<td align="center">1.88</td>
<td align="center"><strong>10.57</strong></td>
</tr>
<tr>
<td align="left">☁️ 千问 (Qwen)</td>
<td align="left">Qwen3.6-Plus</td>
<td align="center">1.45</td>
<td align="center">0.94</td>
<td align="center"><strong>2.39</strong></td>
</tr>
<tr>
<td align="left">🌐 MiniMax</td>
<td align="left">MiniMax-M2.7</td>
<td align="center">1.52</td>
<td align="center">0.66</td>
<td align="center"><strong>2.18</strong></td>
</tr>
<tr>
<td align="left">🌐 MiniMax</td>
<td align="left">MiniMax-M2.7-highspeed</td>
<td align="center">3.04</td>
<td align="center">1.31</td>
<td align="center"><strong>4.35</strong></td>
</tr>
<tr>
<td align="left">🤖 智谱 (GLM)</td>
<td align="left">GLM-5.1</td>
<td align="center">4.34</td>
<td align="center">1.88</td>
<td align="center"><strong>6.22</strong></td>
</tr>
<tr>
<td align="left">🏠 小米 (MiMo)</td>
<td align="left">MiMo-V2-Pro (256K 档)</td>
<td align="center">2.03</td>
<td align="center">1.10</td>
<td align="center"><strong>3.13</strong></td>
</tr>
</tbody></table>
<!-- Main Content -->
<main class="main-content flex-1 ml-64 min-w-0">
<header class="sticky top-0 z-30 bg-white/80 dark:bg-gray-950/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-800">
<div class="flex items-center justify-between px-6 py-3">
<button id="menuBtn" class="mobile-toggle hidden items-center p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
</button>
<div class="flex items-center gap-3 ml-auto">
<button id="themeToggle" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" title="Toggle theme">
<svg id="sunIcon" class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
<svg id="moonIcon" class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
</button>
</div>
</div>
</header>
<div class="max-w-4xl mx-auto px-6 py-10">
<section id="page-0" class="page-section mb-20 scroll-mt-24">
<div class="mb-8 pb-4 border-b border-gray-200 dark:border-gray-700">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">2026-04-26</h1>
<p class="text-sm text-gray-500 dark:text-gray-400">2026-04-26.md</p>
</div>
<div class="prose prose-gray dark:prose-invert max-w-none prose-headings:font-bold prose-headings:text-gray-900 dark:prose-headings:text-white prose-p:text-gray-700 dark:prose-p:text-gray-300 prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-strong:text-gray-900 dark:prose-strong:text-white prose-code:text-sm prose-pre:rounded-lg prose-pre:bg-gray-900 prose-img:rounded-lg prose-table:border-collapse prose-table:border prose-table:border-gray-200 dark:prose-table:border-gray-700 prose-th:bg-gray-50 dark:prose-th:bg-gray-800 prose-th:border prose-th:border-gray-200 dark:prose-th:border-gray-700 prose-td:border prose-td:border-gray-200 dark:prose-td:border-gray-700 prose-li:marker:text-gray-500"><div class="overflow-x-auto my-6 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm"><table class="w-full text-sm"><thead><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">小时</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">平均输入t/s</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">平均输出t/s</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输入token总数</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输出token总数</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">token总数</th>
</tr>
</thead><tbody><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">00</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">216.04</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">26.08</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">115,105</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">5,701</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">120,806</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">08</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">300.02</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">34.15</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">96,329</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">16,921</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">113,250</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">09</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">190.30</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">28.20</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">19,066</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,389</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">20,455</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">10</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">58.32</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">24.36</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">538</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">2,076</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">2,614</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">12</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">166.90</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">18.84</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">45,509</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">8,311</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">53,820</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">13</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">52.57</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">28.39</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">39</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,438</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,477</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">15</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">245.35</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">17.57</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">54,168</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">3,735</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">57,903</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">16</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">259.63</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">27.41</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">69,957</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">7,702</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">77,659</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">17</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">304.44</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">25.81</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">44,088</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,764</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">45,852</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">18</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">233.83</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">27.90</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">206,821</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">17,378</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">224,199</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">20</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">374.10</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">25.27</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">30,747</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">3,918</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">34,665</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">21</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">237.74</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">31.39</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,516</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">5,912</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">7,428</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">23</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">283.86</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">25.43</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">39,894</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1,999</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">41,893</td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>全天</strong></td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>233.31</strong></td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>26.56</strong></td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>723,777</strong></td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>78,244</strong></td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>802,021</strong></td>
</tr>
</tbody></table></div>
<div class="overflow-x-auto my-6 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm"><table class="w-full text-sm"><thead><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="left">厂商</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="left">模型</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输入费用 (元)</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">输出费用 (元)</th>
<th class="px-4 py-3 text-left font-semibold bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700" align="center">总费用 (元)</th>
</tr>
</thead><tbody><tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🚀 DeepSeek</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">DeepSeek-V4-Flash</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.72</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.16</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>0.88</strong></td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🚀 DeepSeek</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">DeepSeek-V4-Pro</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">8.69</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.88</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>10.57</strong></td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">☁️ 千问 (Qwen)</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">Qwen3.6-Plus</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.45</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.94</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>2.39</strong></td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🌐 MiniMax</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">MiniMax-M2.7</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.52</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">0.66</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>2.18</strong></td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🌐 MiniMax</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">MiniMax-M2.7-highspeed</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">3.04</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.31</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>4.35</strong></td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🤖 智谱 (GLM)</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">GLM-5.1</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">4.34</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.88</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>6.22</strong></td>
</tr>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"><td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">🏠 小米 (MiMo)</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="left">MiMo-V2-Pro (256K 档)</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">2.03</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center">1.10</td>
<td class="px-4 py-3 border border-gray-200 dark:border-gray-700" align="center"><strong>3.13</strong></td>
</tr>
</tbody></table></div>
</div>
</section>
</div>
</main>
</div>
<section class="log-entry" aria-label="2026-04-26 - 副本">
<div class="log-date">2026-04-26 - 副本</div>
<table>
<thead>
<tr>
<th align="center">小时</th>
<th align="center">平均输入t/s</th>
<th align="center">平均输出t/s</th>
<th align="center">输入token总数</th>
<th align="center">输出token总数</th>
<th align="center">token总数</th>
</tr>
</thead>
<tbody><tr>
<td align="center">00</td>
<td align="center">216.04</td>
<td align="center">26.08</td>
<td align="center">115,105</td>
<td align="center">5,701</td>
<td align="center">120,806</td>
</tr>
<tr>
<td align="center">08</td>
<td align="center">300.02</td>
<td align="center">34.15</td>
<td align="center">96,329</td>
<td align="center">16,921</td>
<td align="center">113,250</td>
</tr>
<tr>
<td align="center">09</td>
<td align="center">190.30</td>
<td align="center">28.20</td>
<td align="center">19,066</td>
<td align="center">1,389</td>
<td align="center">20,455</td>
</tr>
<tr>
<td align="center">10</td>
<td align="center">58.32</td>
<td align="center">24.36</td>
<td align="center">538</td>
<td align="center">2,076</td>
<td align="center">2,614</td>
</tr>
<tr>
<td align="center">12</td>
<td align="center">166.90</td>
<td align="center">18.84</td>
<td align="center">45,509</td>
<td align="center">8,311</td>
<td align="center">53,820</td>
</tr>
<tr>
<td align="center">13</td>
<td align="center">52.57</td>
<td align="center">28.39</td>
<td align="center">39</td>
<td align="center">1,438</td>
<td align="center">1,477</td>
</tr>
<tr>
<td align="center">15</td>
<td align="center">245.35</td>
<td align="center">17.57</td>
<td align="center">54,168</td>
<td align="center">3,735</td>
<td align="center">57,903</td>
</tr>
<tr>
<td align="center">16</td>
<td align="center">259.63</td>
<td align="center">27.41</td>
<td align="center">69,957</td>
<td align="center">7,702</td>
<td align="center">77,659</td>
</tr>
<tr>
<td align="center">17</td>
<td align="center">304.44</td>
<td align="center">25.81</td>
<td align="center">44,088</td>
<td align="center">1,764</td>
<td align="center">45,852</td>
</tr>
<tr>
<td align="center">18</td>
<td align="center">233.83</td>
<td align="center">27.90</td>
<td align="center">206,821</td>
<td align="center">17,378</td>
<td align="center">224,199</td>
</tr>
<tr>
<td align="center">20</td>
<td align="center">374.10</td>
<td align="center">25.27</td>
<td align="center">30,747</td>
<td align="center">3,918</td>
<td align="center">34,665</td>
</tr>
<tr>
<td align="center">21</td>
<td align="center">237.74</td>
<td align="center">31.39</td>
<td align="center">1,516</td>
<td align="center">5,912</td>
<td align="center">7,428</td>
</tr>
<tr>
<td align="center">23</td>
<td align="center">283.86</td>
<td align="center">25.43</td>
<td align="center">39,894</td>
<td align="center">1,999</td>
<td align="center">41,893</td>
</tr>
<tr>
<td align="center"><strong>全天</strong></td>
<td align="center"><strong>233.31</strong></td>
<td align="center"><strong>26.56</strong></td>
<td align="center"><strong>723,777</strong></td>
<td align="center"><strong>78,244</strong></td>
<td align="center"><strong>802,021</strong></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left">厂商</th>
<th align="left">模型</th>
<th align="center">输入费用 (元)</th>
<th align="center">输出费用 (元)</th>
<th align="center">总费用 (元)</th>
</tr>
</thead>
<tbody><tr>
<td align="left">🚀 DeepSeek</td>
<td align="left">DeepSeek-V4-Flash</td>
<td align="center">0.72</td>
<td align="center">0.16</td>
<td align="center"><strong>0.88</strong></td>
</tr>
<tr>
<td align="left">🚀 DeepSeek</td>
<td align="left">DeepSeek-V4-Pro</td>
<td align="center">8.69</td>
<td align="center">1.88</td>
<td align="center"><strong>10.57</strong></td>
</tr>
<tr>
<td align="left">☁️ 千问 (Qwen)</td>
<td align="left">Qwen3.6-Plus</td>
<td align="center">1.45</td>
<td align="center">0.94</td>
<td align="center"><strong>2.39</strong></td>
</tr>
<tr>
<td align="left">🌐 MiniMax</td>
<td align="left">MiniMax-M2.7</td>
<td align="center">1.52</td>
<td align="center">0.66</td>
<td align="center"><strong>2.18</strong></td>
</tr>
<tr>
<td align="left">🌐 MiniMax</td>
<td align="left">MiniMax-M2.7-highspeed</td>
<td align="center">3.04</td>
<td align="center">1.31</td>
<td align="center"><strong>4.35</strong></td>
</tr>
<tr>
<td align="left">🤖 智谱 (GLM)</td>
<td align="left">GLM-5.1</td>
<td align="center">4.34</td>
<td align="center">1.88</td>
<td align="center"><strong>6.22</strong></td>
</tr>
<tr>
<td align="left">🏠 小米 (MiMo)</td>
<td align="left">MiMo-V2-Pro (256K 档)</td>
<td align="center">2.03</td>
<td align="center">1.10</td>
<td align="center"><strong>3.13</strong></td>
</tr>
</tbody></table>
</section>
</div>
</div>
</main>
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/xml.min.js"></script>
<script>
// Init highlight.js
hljs.highlightAll();
// Theme toggle
const html = document.documentElement;
const themeBtn = document.getElementById('themeToggle');
const saved = localStorage.getItem('theme');
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) html.classList.add('dark');
themeBtn.addEventListener('click', () => {
html.classList.toggle('dark');
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
document.getElementById('hljs-light').disabled = html.classList.contains('dark');
document.getElementById('hljs-dark').disabled = !html.classList.contains('dark');
});
// Sidebar toggle (mobile)
document.getElementById('menuBtn').addEventListener('click', () => {
document.querySelector('.sidebar').classList.toggle('-translate-x-full');
});
// Active TOC tracking
const sections = document.querySelectorAll('.page-section');
const tocLinks = document.querySelectorAll('.toc-link, .toc-sublink');
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const idx = entry.target.id.replace('page-', '');
tocLinks.forEach(l => l.classList.toggle('active', l.dataset.target === idx));
}
});
}, { rootMargin: '-80px 0px -70% 0px', threshold: 0 });
sections.forEach(s => observer.observe(s));
// Smooth scroll for TOC links
tocLinks.forEach(link => {
link.addEventListener('click', e => {
e.preventDefault();
const targetId = link.href.split('#').pop();
document.getElementById(targetId)?.scrollIntoView({ behavior: 'smooth' });
// Close mobile sidebar
if (window.innerWidth < 768) document.querySelector('.sidebar').classList.add('-translate-x-full');
});
});
// Copy code button
window.copyCode = function(btn) {
const code = btn.nextElementSibling;
navigator.clipboard.writeText(code.textContent).then(() => {
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = 'Copy', 2000);
});
};
document.getElementById('last-updated').textContent = new Date().toLocaleString('zh-CN');
</script>
</body>
</html>

17
package-lock.json generated
View File

@ -1,25 +1,14 @@
{
"name": "llm_log_frontend",
"name": "llm-log-frontend",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "llm_log_frontend",
"name": "llm-log-frontend",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"highlight.js": "^11.11.1",
"marked": "^12.0.2"
}
},
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz",
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12.0.0"
"marked": "^12.0.0"
}
},
"node_modules/marked": {

View File

@ -1,17 +1,12 @@
{
"name": "llm_log_frontend",
"name": "llm-log-frontend",
"version": "1.0.0",
"main": "index.js",
"description": "LLM Log Frontend - Aggregated static HTML generator",
"main": "generate.js",
"scripts": {
"build": "node build.js",
"test": "echo \"Error: no test specified\" && exit 1"
"generate": "node generate.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"highlight.js": "^11.11.1",
"marked": "^12.0.2"
"marked": "^12.0.0"
}
}
}