bilisum 1.13.2 → 1.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/bilisum.js +157 -76
- package/package.json +7 -2
- package/runtime/VERSION +1 -0
- package/runtime/apps/service/pyproject.toml +29 -0
- package/runtime/apps/service/src/video_sum_service/__init__.py +1 -0
- package/runtime/apps/service/src/video_sum_service/__main__.py +5 -0
- package/runtime/apps/service/src/video_sum_service/app.py +480 -0
- package/runtime/apps/service/src/video_sum_service/context.py +24 -0
- package/runtime/apps/service/src/video_sum_service/integrations.py +264 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/__init__.py +5 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/index_service.py +408 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/local_llm.py +319 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/rag_service.py +492 -0
- package/runtime/apps/service/src/video_sum_service/knowledge/tag_service.py +242 -0
- package/runtime/apps/service/src/video_sum_service/main.py +31 -0
- package/runtime/apps/service/src/video_sum_service/repository.py +942 -0
- package/runtime/apps/service/src/video_sum_service/routers/__init__.py +1 -0
- package/runtime/apps/service/src/video_sum_service/routers/knowledge.py +272 -0
- package/runtime/apps/service/src/video_sum_service/routers/system.py +280 -0
- package/runtime/apps/service/src/video_sum_service/routers/tasks.py +287 -0
- package/runtime/apps/service/src/video_sum_service/routers/videos.py +766 -0
- package/runtime/apps/service/src/video_sum_service/runtime_support.py +1007 -0
- package/runtime/apps/service/src/video_sum_service/schemas.py +418 -0
- package/runtime/apps/service/src/video_sum_service/settings_manager.py +130 -0
- package/runtime/apps/service/src/video_sum_service/task_artifacts.py +102 -0
- package/runtime/apps/service/src/video_sum_service/task_exports.py +108 -0
- package/runtime/apps/service/src/video_sum_service/transcribe_worker.py +9 -0
- package/runtime/apps/service/src/video_sum_service/video_assets.py +623 -0
- package/runtime/apps/service/src/video_sum_service/worker.py +381 -0
- package/runtime/apps/web/static/apple-touch-icon.png +0 -0
- package/runtime/apps/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/runtime/apps/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/runtime/apps/web/static/assets/icons/icon-180.png +0 -0
- package/runtime/apps/web/static/assets/icons/icon-512.png +0 -0
- package/runtime/apps/web/static/assets/icons/icon.svg +17 -0
- package/runtime/apps/web/static/assets/index-Cj5PVjQg.js +388 -0
- package/runtime/apps/web/static/assets/index-DU2t4_7s.css +1 -0
- package/runtime/apps/web/static/favicon-32x32.png +0 -0
- package/runtime/apps/web/static/favicon.ico +0 -0
- package/runtime/apps/web/static/favicon.svg +17 -0
- package/runtime/apps/web/static/index.html +19 -0
- package/runtime/apps/web/static/js/api.js +123 -0
- package/runtime/apps/web/static/js/main.js +932 -0
- package/runtime/apps/web/static/js/state.js +28 -0
- package/runtime/apps/web/static/js/utils.js +81 -0
- package/runtime/apps/web/static/js/views/home.js +604 -0
- package/runtime/apps/web/static/js/views/settings.js +477 -0
- package/runtime/apps/web/static/styles.css +2206 -0
- package/runtime/packages/core/pyproject.toml +18 -0
- package/runtime/packages/core/src/video_sum_core/__init__.py +1 -0
- package/runtime/packages/core/src/video_sum_core/errors.py +22 -0
- package/runtime/packages/core/src/video_sum_core/markdown_exports.py +154 -0
- package/runtime/packages/core/src/video_sum_core/models/__init__.py +1 -0
- package/runtime/packages/core/src/video_sum_core/models/tasks.py +70 -0
- package/runtime/packages/core/src/video_sum_core/pipeline/__init__.py +1 -0
- package/runtime/packages/core/src/video_sum_core/pipeline/base.py +46 -0
- package/runtime/packages/core/src/video_sum_core/pipeline/real.py +3467 -0
- package/runtime/packages/core/src/video_sum_core/transcribe_subprocess.py +173 -0
- package/runtime/packages/core/src/video_sum_core/utils.py +127 -0
- package/runtime/packages/infra/pyproject.toml +16 -0
- package/runtime/packages/infra/src/video_sum_infra/__init__.py +1 -0
- package/runtime/packages/infra/src/video_sum_infra/app.py +39 -0
- package/runtime/packages/infra/src/video_sum_infra/config.py +393 -0
- package/runtime/packages/infra/src/video_sum_infra/db.py +34 -0
- package/runtime/packages/infra/src/video_sum_infra/logging.py +54 -0
- package/runtime/packages/infra/src/video_sum_infra/paths.py +6 -0
- package/runtime/packages/infra/src/video_sum_infra/runtime.py +485 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { escapeHtml } from "../utils.js";
|
|
2
|
+
|
|
3
|
+
function getStatusTone(message) {
|
|
4
|
+
if (/失败|错误|不可用|未就绪|无效|已取消|关闭/i.test(message)) {
|
|
5
|
+
return "error";
|
|
6
|
+
}
|
|
7
|
+
if (/完成|成功|已保存|已刷新|已开始|已删除|已更新|已复制|已请求/i.test(message)) {
|
|
8
|
+
return "success";
|
|
9
|
+
}
|
|
10
|
+
return "info";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function renderStatusNotice(message, statusKey) {
|
|
14
|
+
if (!message) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
const tone = getStatusTone(message);
|
|
18
|
+
return `
|
|
19
|
+
<div class="action-status tone-${tone}">
|
|
20
|
+
<span class="action-status-copy">${escapeHtml(message)}</span>
|
|
21
|
+
<button class="action-status-close" type="button" data-dismiss-status="${statusKey}" aria-label="关闭提示">
|
|
22
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
23
|
+
<path d="M3 3L9 9M9 3L3 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
|
24
|
+
</svg>
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function renderSettingsView(state) {
|
|
31
|
+
const settings = state.settings || {};
|
|
32
|
+
const info = state.systemInfo || {};
|
|
33
|
+
const env = state.environment || {};
|
|
34
|
+
|
|
35
|
+
return `
|
|
36
|
+
<section class="settings-grid">
|
|
37
|
+
<!-- 运行环境与 CUDA -->
|
|
38
|
+
<article class="grid-card settings-wide env-card">
|
|
39
|
+
<div class="panel-header">
|
|
40
|
+
<h2>
|
|
41
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
42
|
+
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
|
|
43
|
+
<line x1="8" y1="21" x2="16" y2="21"></line>
|
|
44
|
+
<line x1="12" y1="17" x2="12" y2="21"></line>
|
|
45
|
+
</svg>
|
|
46
|
+
运行环境与 CUDA
|
|
47
|
+
</h2>
|
|
48
|
+
<p>环境检测、推荐设备和 CUDA 配置</p>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- 环境状态网格 -->
|
|
52
|
+
<section class="env-panel">
|
|
53
|
+
<div class="env-panel-head">
|
|
54
|
+
<span class="env-panel-kicker">Environment Snapshot</span>
|
|
55
|
+
<p>当前硬件、依赖版本和运行时建议一览</p>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="env-summary-grid">
|
|
58
|
+
${renderEnvCard("推荐设备", env.recommendedDevice || "-", "cpu")}
|
|
59
|
+
${renderEnvCard("推荐模型", env.recommendedModel || "-", "model")}
|
|
60
|
+
${renderEnvCard("GPU 状态", env.cudaAvailable ? "已启用" : "未启用", env.cudaAvailable ? "success" : "warning")}
|
|
61
|
+
${renderEnvCard("GPU 名称", env.gpuName || "未检测到", env.gpuName ? "success" : "neutral")}
|
|
62
|
+
${renderEnvCard("Torch", env.torchInstalled ? env.torchVersion || "已安装" : "未安装", env.torchInstalled ? "success" : "warning")}
|
|
63
|
+
${renderEnvCard("yt-dlp", env.ytDlpVersion || "未安装", env.ytDlpVersion ? "success" : "warning")}
|
|
64
|
+
${renderEnvCard("本地 ASR", env.localAsrInstalled ? (env.localAsrVersion || "已安装") : "未安装", env.localAsrInstalled ? "success" : "neutral")}
|
|
65
|
+
${renderEnvCard("FFmpeg", env.ffmpegLocation ? `已安装 (${escapeHtml(env.ffmpegLocation)})` : "未安装", env.ffmpegLocation ? "success" : "warning")}
|
|
66
|
+
${renderEnvCard("Python", env.pythonVersion || "-", "neutral")}
|
|
67
|
+
${renderEnvCard("运行时通道", env.runtimeChannel || settings.runtime_channel || "base", "neutral")}
|
|
68
|
+
${renderEnvCard("运行时状态", env.runtimeReady === false ? "未就绪" : "已就绪", env.runtimeReady === false ? "warning" : "success")}
|
|
69
|
+
${renderEnvCard("运行时解释器", env.runtimePython || "未检测到", env.runtimePython ? "success" : "neutral")}
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
<!-- CUDA 操作区 -->
|
|
74
|
+
<section class="cuda-control-panel">
|
|
75
|
+
<div class="cuda-control-copy">
|
|
76
|
+
<span class="env-panel-kicker">CUDA Control</span>
|
|
77
|
+
<h3>CUDA 目标版本</h3>
|
|
78
|
+
<p>选择目标运行时后,可重新检测环境或安装对应 CUDA 支持。</p>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="cuda-actions">
|
|
81
|
+
<label class="input-row cuda-picker">
|
|
82
|
+
<span class="input-label">CUDA 目标版本</span>
|
|
83
|
+
<select id="cuda_variant" class="select-field">
|
|
84
|
+
<option value="cu128" ${settings.cuda_variant === "cu128" ? "selected" : ""}>CUDA 12.8</option>
|
|
85
|
+
<option value="cu126" ${settings.cuda_variant === "cu126" ? "selected" : ""}>CUDA 12.6</option>
|
|
86
|
+
<option value="cu124" ${settings.cuda_variant === "cu124" ? "selected" : ""}>CUDA 12.4</option>
|
|
87
|
+
</select>
|
|
88
|
+
</label>
|
|
89
|
+
<div class="settings-actions cuda-button-row">
|
|
90
|
+
<button id="refresh-env" class="secondary-button" type="button">
|
|
91
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
92
|
+
<polyline points="23 4 23 10 17 10"></polyline>
|
|
93
|
+
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
|
|
94
|
+
</svg>
|
|
95
|
+
重新检测
|
|
96
|
+
</button>
|
|
97
|
+
<button id="install-cuda" class="primary-button" type="button">
|
|
98
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
99
|
+
<line x1="12" y1="5" x2="12" y2="19"></line>
|
|
100
|
+
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
101
|
+
</svg>
|
|
102
|
+
安装 CUDA 支持
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
${renderStatusNotice(state.cudaActionStatus, "cudaActionStatus")}
|
|
107
|
+
${state.cudaInstallOutput ? `
|
|
108
|
+
<label class="input-row">
|
|
109
|
+
<span class="input-label">CUDA 安装输出</span>
|
|
110
|
+
<textarea class="textarea-field log-viewer" rows="8" readonly>${escapeHtml(state.cudaInstallOutput)}</textarea>
|
|
111
|
+
</label>
|
|
112
|
+
` : ''}
|
|
113
|
+
${env.runtimeError ? `
|
|
114
|
+
<label class="input-row">
|
|
115
|
+
<span class="input-label">运行时错误详情</span>
|
|
116
|
+
<textarea class="textarea-field log-viewer" rows="8" readonly>${escapeHtml(env.runtimeError)}</textarea>
|
|
117
|
+
</label>
|
|
118
|
+
` : ''}
|
|
119
|
+
<label class="input-row">
|
|
120
|
+
<span class="input-label">本地 ASR 运行时</span>
|
|
121
|
+
<div class="settings-actions">
|
|
122
|
+
<button id="install-local-asr" class="secondary-button" type="button">安装本地 ASR</button>
|
|
123
|
+
</div>
|
|
124
|
+
<span class="input-caption">${env.localAsrInstalled ? `当前已安装 ${escapeHtml(env.localAsrVersion || "")},安装后会自动切换到本地模式。` : "正式安装包默认不包含本地 ASR;安装到当前运行时后会自动切换到本地模式。"}</span>
|
|
125
|
+
</label>
|
|
126
|
+
${renderStatusNotice(state.localAsrActionStatus, "localAsrActionStatus")}
|
|
127
|
+
${state.localAsrInstallOutput ? `
|
|
128
|
+
<label class="input-row">
|
|
129
|
+
<span class="input-label">本地 ASR 安装输出</span>
|
|
130
|
+
<textarea class="textarea-field log-viewer" rows="8" readonly>${escapeHtml(state.localAsrInstallOutput)}</textarea>
|
|
131
|
+
</label>
|
|
132
|
+
` : ""}
|
|
133
|
+
</section>
|
|
134
|
+
</article>
|
|
135
|
+
|
|
136
|
+
<!-- 运行配置表单 -->
|
|
137
|
+
<article class="grid-card settings-form-card">
|
|
138
|
+
<div class="panel-header">
|
|
139
|
+
<h2>
|
|
140
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
141
|
+
<circle cx="12" cy="12" r="3"></circle>
|
|
142
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
|
143
|
+
</svg>
|
|
144
|
+
运行配置
|
|
145
|
+
</h2>
|
|
146
|
+
<p>编辑并保存后端配置</p>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<form id="settings-form" class="setting-form settings-sections">
|
|
150
|
+
<!-- 基础运行 -->
|
|
151
|
+
<section class="settings-subsection">
|
|
152
|
+
<h3>基础运行</h3>
|
|
153
|
+
${renderInput("host", "监听地址 Host", settings.host || "", "text", "0.0.0.0")}
|
|
154
|
+
${renderInput("port", "监听端口", settings.port || "", "number", "3838")}
|
|
155
|
+
${renderInput("data_dir", "数据目录", settings.data_dir || "", "text", "/path/to/data")}
|
|
156
|
+
${renderInput("cache_dir", "缓存目录", settings.cache_dir || "", "text", "/path/to/cache")}
|
|
157
|
+
${renderInput("tasks_dir", "任务目录", settings.tasks_dir || "", "text", "/path/to/tasks")}
|
|
158
|
+
${renderInput("database_url", "数据库", settings.database_url || "", "text", "sqlite:///data.db")}
|
|
159
|
+
${renderSelect("runtime_channel", "运行时通道", settings.runtime_channel || "base", buildRuntimeChannelOptions(settings))}
|
|
160
|
+
</section>
|
|
161
|
+
|
|
162
|
+
<!-- 转写模型 -->
|
|
163
|
+
<section class="settings-subsection">
|
|
164
|
+
<h3>转写模型</h3>
|
|
165
|
+
${renderSelect("transcription_provider", "转写方式", settings.transcription_provider || "siliconflow", [
|
|
166
|
+
{ value: "siliconflow", label: "硅基流动 API" },
|
|
167
|
+
...(env.localAsrInstalled ? [{ value: "local", label: "本地 ASR" }] : [])
|
|
168
|
+
])}
|
|
169
|
+
${renderSelect("device_preference", "推理设备", settings.device_preference || "cpu", [
|
|
170
|
+
{ value: "auto", label: "自动选择" },
|
|
171
|
+
{ value: "cuda", label: "CUDA (GPU)" },
|
|
172
|
+
{ value: "cpu", label: "CPU" }
|
|
173
|
+
])}
|
|
174
|
+
${renderSelect("compute_type", "计算精度", settings.compute_type || "int8", [
|
|
175
|
+
{ value: "auto", label: "自动" },
|
|
176
|
+
{ value: "float16", label: "Float16" },
|
|
177
|
+
{ value: "int8", label: "Int8" },
|
|
178
|
+
{ value: "float32", label: "Float32" }
|
|
179
|
+
])}
|
|
180
|
+
${renderSelect("model_mode", "模型模式", settings.model_mode || "fixed", [
|
|
181
|
+
{ value: "auto", label: "自动" },
|
|
182
|
+
{ value: "fixed", label: "固定" }
|
|
183
|
+
])}
|
|
184
|
+
${renderSelect("fixed_model", "固定模型", settings.fixed_model || "tiny", [
|
|
185
|
+
{ value: "tiny", label: "Tiny (最快)" },
|
|
186
|
+
{ value: "base", label: "Base (平衡)" },
|
|
187
|
+
{ value: "large-v3-turbo", label: "Large v3 Turbo (最准)" }
|
|
188
|
+
])}
|
|
189
|
+
${renderInput("siliconflow_asr_base_url", "SiliconFlow Base URL", settings.siliconflow_asr_base_url || "https://api.siliconflow.cn/v1", "text", "https://api.siliconflow.cn/v1")}
|
|
190
|
+
${renderInput("siliconflow_asr_model", "SiliconFlow ASR 模型", settings.siliconflow_asr_model || "TeleAI/TeleSpeechASR", "text", "TeleAI/TeleSpeechASR")}
|
|
191
|
+
${renderSiliconFlowApiKeyInput(settings.siliconflow_asr_api_key || "")}
|
|
192
|
+
${renderInput("language", "语言", settings.language || "", "text", "zh")}
|
|
193
|
+
</section>
|
|
194
|
+
|
|
195
|
+
<!-- 输出与缓存 -->
|
|
196
|
+
<section class="settings-subsection">
|
|
197
|
+
<h3>输出与缓存</h3>
|
|
198
|
+
${renderInput("output_dir", "输出目录", settings.output_dir || "", "text", "/path/to/output")}
|
|
199
|
+
<label class="toggle-row">
|
|
200
|
+
<span>保留临时音频</span>
|
|
201
|
+
<input id="preserve_temp_audio" type="checkbox" ${settings.preserve_temp_audio ? "checked" : ""} />
|
|
202
|
+
</label>
|
|
203
|
+
<label class="toggle-row">
|
|
204
|
+
<span>启用本地缓存</span>
|
|
205
|
+
<input id="enable_cache" type="checkbox" ${settings.enable_cache ? "checked" : ""} />
|
|
206
|
+
</label>
|
|
207
|
+
</section>
|
|
208
|
+
|
|
209
|
+
<!-- 摘要配置 -->
|
|
210
|
+
<section class="settings-subsection">
|
|
211
|
+
<h3>摘要配置</h3>
|
|
212
|
+
${renderSelect("summary_mode", "摘要模式", settings.summary_mode || "llm", [
|
|
213
|
+
{ value: "auto", label: "自动" },
|
|
214
|
+
{ value: "rule", label: "规则摘要" },
|
|
215
|
+
{ value: "llm", label: "LLM 摘要" }
|
|
216
|
+
])}
|
|
217
|
+
<label class="toggle-row">
|
|
218
|
+
<span>启用 LLM 摘要</span>
|
|
219
|
+
<input id="llm_enabled" type="checkbox" ${settings.llm_enabled ? "checked" : ""} />
|
|
220
|
+
</label>
|
|
221
|
+
${renderInput("llm_provider", "LLM Provider", settings.llm_provider || "", "text", "openai-compatible")}
|
|
222
|
+
${renderInput("llm_base_url", "LLM Base URL", settings.llm_base_url || "", "text", "https://api.openai.com/v1")}
|
|
223
|
+
${renderInput("llm_model", "LLM 模型", settings.llm_model || "", "text", "gpt-3.5-turbo")}
|
|
224
|
+
${renderInput("llm_api_key", "LLM API Key", settings.llm_api_key || "", "password", "sk-...", "current-password")}
|
|
225
|
+
${renderInput("summary_chunk_target_chars", "分块目标字符数", settings.summary_chunk_target_chars || 2200, "number", "2200")}
|
|
226
|
+
${renderInput("summary_chunk_overlap_segments", "分块重叠段数", settings.summary_chunk_overlap_segments || 2, "number", "2")}
|
|
227
|
+
${renderInput("summary_chunk_concurrency", "分块并发数", settings.summary_chunk_concurrency || 2, "number", "2")}
|
|
228
|
+
${renderInput("summary_chunk_retry_count", "单块重试次数", settings.summary_chunk_retry_count || 2, "number", "2")}
|
|
229
|
+
${renderTextarea("summary_system_prompt", "系统提示词", settings.summary_system_prompt || "", 4)}
|
|
230
|
+
${renderTextarea("summary_user_prompt_template", "用户提示词模板", settings.summary_user_prompt_template || "", 6)}
|
|
231
|
+
</section>
|
|
232
|
+
|
|
233
|
+
<!-- 保存按钮 -->
|
|
234
|
+
<section class="settings-subsection settings-actions-section">
|
|
235
|
+
<div class="settings-actions">
|
|
236
|
+
<button class="primary-button" type="submit">
|
|
237
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
238
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
|
|
239
|
+
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
|
240
|
+
<polyline points="7 3 7 8 15 8"></polyline>
|
|
241
|
+
</svg>
|
|
242
|
+
保存设置
|
|
243
|
+
</button>
|
|
244
|
+
${renderStatusNotice(state.settingsSaveStatus, "settingsSaveStatus")}
|
|
245
|
+
</div>
|
|
246
|
+
</section>
|
|
247
|
+
</form>
|
|
248
|
+
</article>
|
|
249
|
+
|
|
250
|
+
<!-- 摘要配置概览 -->
|
|
251
|
+
<article class="grid-card">
|
|
252
|
+
<div class="panel-header">
|
|
253
|
+
<h2>
|
|
254
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
255
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
|
256
|
+
<polyline points="14 2 14 8 20 8"></polyline>
|
|
257
|
+
<line x1="16" y1="13" x2="8" y2="13"></line>
|
|
258
|
+
<line x1="16" y1="17" x2="8" y2="17"></line>
|
|
259
|
+
<polyline points="10 9 9 9 8 9"></polyline>
|
|
260
|
+
</svg>
|
|
261
|
+
摘要配置概览
|
|
262
|
+
</h2>
|
|
263
|
+
<p>当前摘要相关运行参数</p>
|
|
264
|
+
</div>
|
|
265
|
+
<div class="setting-list">
|
|
266
|
+
${renderRow("LLM 启用", settings.llm_enabled ? "✓ 是" : "✗ 否", settings.llm_enabled ? "success" : "neutral")}
|
|
267
|
+
${renderRow("转写方式", settings.transcription_provider === "siliconflow" ? "硅基流动 API" : "本地 ASR", settings.transcription_provider === "siliconflow" ? "success" : "neutral")}
|
|
268
|
+
${renderRow("SiliconFlow 模型", settings.siliconflow_asr_model || "-", settings.siliconflow_asr_model ? "success" : "neutral")}
|
|
269
|
+
${renderRow("SiliconFlow API Key", settings.siliconflow_asr_api_key_configured ? "✓ 已配置" : "✗ 未配置", settings.siliconflow_asr_api_key_configured ? "success" : "warning")}
|
|
270
|
+
${renderRow("本地 ASR", env.localAsrInstalled ? `✓ 已安装${env.localAsrVersion ? ` (${env.localAsrVersion})` : ""}` : "✗ 未安装", env.localAsrInstalled ? "success" : "neutral")}
|
|
271
|
+
${renderRow("LLM Base URL", settings.llm_base_url || "-", settings.llm_base_url ? "success" : "neutral")}
|
|
272
|
+
${renderRow("LLM 模型", settings.llm_model || "-", settings.llm_model ? "success" : "neutral")}
|
|
273
|
+
${renderRow("运行时通道", settings.runtime_channel || "base", "neutral")}
|
|
274
|
+
${renderRow("摘要模式", settings.summary_mode || "-", "neutral")}
|
|
275
|
+
${renderRow("分块大小", String(settings.summary_chunk_target_chars || "-"), "neutral")}
|
|
276
|
+
${renderRow("分块并发", String(settings.summary_chunk_concurrency || "-"), "neutral")}
|
|
277
|
+
${renderRow("API Key", settings.llm_api_key_configured ? "✓ 已配置" : "✗ 未配置", settings.llm_api_key_configured ? "success" : "warning")}
|
|
278
|
+
</div>
|
|
279
|
+
</article>
|
|
280
|
+
|
|
281
|
+
<!-- 后端信息 -->
|
|
282
|
+
<article class="grid-card">
|
|
283
|
+
<div class="panel-header">
|
|
284
|
+
<h2>
|
|
285
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
286
|
+
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
|
|
287
|
+
<line x1="8" y1="21" x2="16" y2="21"></line>
|
|
288
|
+
<line x1="12" y1="17" x2="12" y2="21"></line>
|
|
289
|
+
</svg>
|
|
290
|
+
后端信息
|
|
291
|
+
</h2>
|
|
292
|
+
<p>系统运行详情</p>
|
|
293
|
+
</div>
|
|
294
|
+
<div class="setting-list">
|
|
295
|
+
${renderRow("服务名", info.application?.name || "-")}
|
|
296
|
+
${renderRow("版本", info.application?.version || "-")}
|
|
297
|
+
${renderRow("任务状态", (info.taskModel?.statuses || []).join(", ") || "-")}
|
|
298
|
+
${renderRow("日志文件", info.service?.log_file || state.logPath || "-", "neutral")}
|
|
299
|
+
</div>
|
|
300
|
+
</article>
|
|
301
|
+
|
|
302
|
+
<!-- 日志与控制 -->
|
|
303
|
+
<article class="grid-card settings-wide">
|
|
304
|
+
<div class="panel-header">
|
|
305
|
+
<h2>
|
|
306
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
307
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
|
308
|
+
<polyline points="14 2 14 8 20 8"></polyline>
|
|
309
|
+
<line x1="16" y1="13" x2="8" y2="13"></line>
|
|
310
|
+
<line x1="16" y1="17" x2="8" y2="17"></line>
|
|
311
|
+
</svg>
|
|
312
|
+
日志与控制
|
|
313
|
+
</h2>
|
|
314
|
+
<p>查看后端日志,并直接关闭当前后端服务</p>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="settings-actions">
|
|
317
|
+
<button id="refresh-logs" class="secondary-button" type="button">刷新日志</button>
|
|
318
|
+
<button id="shutdown-service" class="secondary-button danger-button" type="button">关闭后端服务</button>
|
|
319
|
+
</div>
|
|
320
|
+
${renderStatusNotice(state.serviceActionStatus, "serviceActionStatus")}
|
|
321
|
+
<label class="input-row">
|
|
322
|
+
<span class="input-label">当前日志文件</span>
|
|
323
|
+
<input class="input-field" value="${escapeHtml(state.logPath || info.service?.log_file || '')}" readonly />
|
|
324
|
+
</label>
|
|
325
|
+
<label class="input-row">
|
|
326
|
+
<span class="input-label">最近日志</span>
|
|
327
|
+
<textarea class="textarea-field log-viewer" rows="16" readonly>${escapeHtml(state.logOutput || "")}</textarea>
|
|
328
|
+
</label>
|
|
329
|
+
</article>
|
|
330
|
+
|
|
331
|
+
<!-- 关于 -->
|
|
332
|
+
<article class="grid-card about-card">
|
|
333
|
+
<div class="panel-header">
|
|
334
|
+
<h2>
|
|
335
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
336
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
337
|
+
<line x1="12" y1="16" x2="12" y2="12"></line>
|
|
338
|
+
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
|
339
|
+
</svg>
|
|
340
|
+
关于
|
|
341
|
+
</h2>
|
|
342
|
+
<p>项目信息</p>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="setting-list">
|
|
345
|
+
${renderRow("当前形态", "开发态可用 + 本地 Web UI", "info")}
|
|
346
|
+
${renderRow("下一目标", "桌面 UI + 可执行后端", "info")}
|
|
347
|
+
${renderRow("最终目标", "MSI / DMG / AppImage", "info")}
|
|
348
|
+
</div>
|
|
349
|
+
</article>
|
|
350
|
+
</section>
|
|
351
|
+
`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function renderEnvCard(label, value, type) {
|
|
355
|
+
const icons = {
|
|
356
|
+
cpu: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line></svg>`,
|
|
357
|
+
model: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path></svg>`,
|
|
358
|
+
success: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="20 6 9 17 4 12"></polyline></svg>`,
|
|
359
|
+
warning: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>`,
|
|
360
|
+
neutral: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>`,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const icon = icons[type] || icons.neutral;
|
|
364
|
+
|
|
365
|
+
return `
|
|
366
|
+
<div class="env-card-item ${type}">
|
|
367
|
+
<div class="env-card-icon">${icon}</div>
|
|
368
|
+
<div class="env-card-content">
|
|
369
|
+
<span class="env-card-label">${escapeHtml(label)}</span>
|
|
370
|
+
<span class="env-card-value">${escapeHtml(value)}</span>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function renderRow(label, value, type = "neutral") {
|
|
377
|
+
const typeClass = type === "success" ? "row-success" : type === "warning" ? "row-warning" : type === "info" ? "row-info" : "";
|
|
378
|
+
|
|
379
|
+
return `
|
|
380
|
+
<div class="setting-row ${typeClass}">
|
|
381
|
+
<span class="setting-label">${escapeHtml(label)}</span>
|
|
382
|
+
<span class="setting-value">${escapeHtml(value)}</span>
|
|
383
|
+
</div>
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function renderInput(id, label, value, type = "text", placeholder = "", autocomplete = "") {
|
|
388
|
+
return `
|
|
389
|
+
<label class="input-row">
|
|
390
|
+
<span class="input-label">${escapeHtml(label)}</span>
|
|
391
|
+
<input
|
|
392
|
+
id="${escapeHtml(id)}"
|
|
393
|
+
type="${escapeHtml(type)}"
|
|
394
|
+
value="${escapeHtml(value)}"
|
|
395
|
+
placeholder="${escapeHtml(placeholder)}"
|
|
396
|
+
${autocomplete ? `autocomplete="${escapeHtml(autocomplete)}"` : ""}
|
|
397
|
+
class="input-field"
|
|
398
|
+
/>
|
|
399
|
+
</label>
|
|
400
|
+
`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function renderSiliconFlowApiKeyInput(value) {
|
|
404
|
+
return `
|
|
405
|
+
<div class="input-row api-key-help-row">
|
|
406
|
+
<label class="input-row" for="siliconflow_asr_api_key">
|
|
407
|
+
<span class="input-label">SiliconFlow API Key</span>
|
|
408
|
+
<input
|
|
409
|
+
id="siliconflow_asr_api_key"
|
|
410
|
+
type="password"
|
|
411
|
+
value="${escapeHtml(value)}"
|
|
412
|
+
placeholder="sk-..."
|
|
413
|
+
autocomplete="current-password"
|
|
414
|
+
class="input-field"
|
|
415
|
+
/>
|
|
416
|
+
</label>
|
|
417
|
+
<div class="api-key-help-inline">
|
|
418
|
+
<span class="api-key-help-copy">调用云端语音识别必须提供 API Key。</span>
|
|
419
|
+
<div class="api-key-help-popover">
|
|
420
|
+
<span class="api-key-help-link" role="button" tabindex="0">如何获得 API?</span>
|
|
421
|
+
<div class="api-key-help-popover-card" id="siliconflow-api-help">
|
|
422
|
+
<strong>获取步骤</strong>
|
|
423
|
+
<ol>
|
|
424
|
+
<li>
|
|
425
|
+
注册 SiliconFlow 账号:
|
|
426
|
+
<a href="https://cloud.siliconflow.cn/i/d8SF8w5Z" target="_blank" rel="noreferrer">点此注册</a>
|
|
427
|
+
</li>
|
|
428
|
+
<li>
|
|
429
|
+
新建 API Key:
|
|
430
|
+
<a href="https://cloud.siliconflow.cn/me/account/ak" target="_blank" rel="noreferrer">前往创建</a>
|
|
431
|
+
</li>
|
|
432
|
+
</ol>
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function renderSelect(id, label, selectedValue, options) {
|
|
441
|
+
return `
|
|
442
|
+
<label class="input-row">
|
|
443
|
+
<span class="input-label">${escapeHtml(label)}</span>
|
|
444
|
+
<select id="${escapeHtml(id)}" class="select-field">
|
|
445
|
+
${options
|
|
446
|
+
.map((opt) => `<option value="${escapeHtml(opt.value)}" ${opt.value === selectedValue ? "selected" : ""}>${escapeHtml(opt.label)}</option>`)
|
|
447
|
+
.join("")}
|
|
448
|
+
</select>
|
|
449
|
+
</label>
|
|
450
|
+
`;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function renderTextarea(id, label, value, rows) {
|
|
454
|
+
return `
|
|
455
|
+
<label class="input-row">
|
|
456
|
+
<span class="input-label">${escapeHtml(label)}</span>
|
|
457
|
+
<textarea
|
|
458
|
+
id="${escapeHtml(id)}"
|
|
459
|
+
rows="${rows}"
|
|
460
|
+
class="textarea-field"
|
|
461
|
+
>${escapeHtml(value)}</textarea>
|
|
462
|
+
</label>
|
|
463
|
+
`;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function buildRuntimeChannelOptions(settings) {
|
|
467
|
+
const options = [
|
|
468
|
+
{ value: "base", label: "base (CPU 基础运行时)" },
|
|
469
|
+
];
|
|
470
|
+
for (const value of ["gpu-cu124", "gpu-cu126", "gpu-cu128"]) {
|
|
471
|
+
options.push({
|
|
472
|
+
value,
|
|
473
|
+
label: `${value}${settings.runtime_channel === value ? " (当前)" : ""}`,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return options;
|
|
477
|
+
}
|