Skip to content

Commit f841b56

Browse files
committed
feat: 更新模型目录至 catalog-v7,密钥页新增「调用」卡片
- models: 同步 catalog-v7 —— 新增 Qwen3.8-Flash / GLM-5.3-Flash / Qwen3.7-Flash, Kimi-K2.7-Code -> Kimi-K3 (kmodel_latest),DeepSeek-V4-Flash 更名 DeepSeek-Flash,移除 Qwen3.6-Flash;同步 DefaultVisionModels 与 README - web: 密钥页新增「调用」卡片,展示 Base URL / 端点 / curl 示例, 地址由 location.origin 推导,支持一键复制;表头改「API Key」; 新增 TestKeysPageCallCard 保证不写死 localhost
1 parent f1eee5e commit f841b56

6 files changed

Lines changed: 109 additions & 15 deletions

File tree

‎README.md‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@
2323
| 显示名称 | 内部 Key |
2424
| ----------------- | -------------- |
2525
| Qwen3.8-Max | qmodel_38max |
26+
| Qwen3.8-Flash | qfmodel |
2627
| Qwen3.7-Max | qmodel_latest |
2728
| Qwen3.7-Plus | qmodel |
28-
| Qwen3.6-Flash | q36fmodel |
29+
| Qwen3.7-Flash | q37fmodel |
2930
| DeepSeek-V4-Pro | dmodel |
30-
| DeepSeek-V4-Flash | dfmodel |
31+
| DeepSeek-Flash | dfmodel |
3132
| GLM-5.3 | gmodel |
33+
| GLM-5.3-Flash | gfmodel |
3234
| GLM-5.2 | gm51model |
33-
| Kimi-K2.7-Code | kmodel |
35+
| Kimi-K3 | kmodel_latest |
36+
| Kimi-K2.8-Preview | kmodel |
3437
| MiniMax-M2.7 | mmodel |
3538

36-
> 以上为内置默认列表(catalog-v6,2026-08-15),实际可用模型以网关动态返回为准。
39+
> 以上为内置默认列表(catalog-v7,2026-10-07),实际可用模型以网关动态返回为准。
3740
>
3841
> **关于图片输入**:部分模型并非原生多模态,而是由网关对图片附带辅助识别(非原生多模态模型加上辅助后实际可以识图),因此上表不再标注是否支持视觉;识图效果以实际调用结果为准。
3942
@@ -144,7 +147,7 @@ curl http://localhost:10081/v1/chat/completions \
144147
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxx" \
145148
-H "Content-Type: application/json" \
146149
-d '{
147-
"model": "Qwen3.7-Max",
150+
"model": "Qwen3.8-Flash",
148151
"messages": [{"role": "user", "content": "你好"}],
149152
"stream": true
150153
}'

‎models/models.go‎

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,24 @@ func enableFlag(v interface{}) bool {
2525
}
2626

2727
// DefaultModelMap is the built-in fallback map (display_name -> qoder internal key).
28-
// Matches catalog-v6 (2026-08-15) chat scene. Only used when dynamic fetch fails.
29-
// Notable vs v5: Qwen3.8-Max graduated from preview (qmodel_preview ->
30-
// qmodel_38max, renamed without "-Preview"); GLM-5.3 (gmodel) added.
28+
// Matches catalog-v7 (2026-09-22) chat scene. Only used when dynamic fetch fails.
29+
// Notable vs v6: Qwen3.8-Flash (qfmodel) added; DeepSeek-V4-Flash renamed to
30+
// DeepSeek-Flash (same key dfmodel); Kimi-K2.7-Code -> Kimi-K3 (kmodel_latest,
31+
// K2.8-Preview keeps kmodel); GLM-5.3-Flash (gfmodel) and Qwen3.7-Flash
32+
// (q37fmodel) added; Qwen3.6-Flash (q36fmodel) retired.
3133
var DefaultModelMap = map[string]string{
3234
"Qwen3.8-Max": "qmodel_38max",
35+
"Qwen3.8-Flash": "qfmodel",
3336
"Qwen3.7-Max": "qmodel_latest",
3437
"Qwen3.7-Plus": "qmodel",
35-
"Qwen3.6-Flash": "q36fmodel",
38+
"Qwen3.7-Flash": "q37fmodel",
3639
"DeepSeek-V4-Pro": "dmodel",
37-
"DeepSeek-V4-Flash": "dfmodel",
40+
"DeepSeek-Flash": "dfmodel",
3841
"GLM-5.3": "gmodel",
42+
"GLM-5.3-Flash": "gfmodel",
3943
"GLM-5.2": "gm51model",
40-
"Kimi-K2.7-Code": "kmodel",
44+
"Kimi-K3": "kmodel_latest",
45+
"Kimi-K2.8-Preview": "kmodel",
4146
"MiniMax-M2.7": "mmodel",
4247
}
4348

@@ -46,14 +51,17 @@ var DefaultModelMap = map[string]string{
4651
// unreliable — do not treat this as an authoritative capability matrix.
4752
var DefaultVisionModels = map[string]bool{
4853
"Qwen3.8-Max": true,
54+
"Qwen3.8-Flash": true,
4955
"Qwen3.7-Max": true,
5056
"Qwen3.7-Plus": true,
51-
"Qwen3.6-Flash": true,
57+
"Qwen3.7-Flash": true,
5258
"DeepSeek-V4-Pro": true,
53-
"DeepSeek-V4-Flash": true,
59+
"DeepSeek-Flash": true,
5460
"GLM-5.3": true,
61+
"GLM-5.3-Flash": true,
5562
"GLM-5.2": true,
56-
"Kimi-K2.7-Code": true,
63+
"Kimi-K3": true,
64+
"Kimi-K2.8-Preview": true,
5765
}
5866

5967
// PreferredDefaultKey is the default model key when model param is None/empty.

‎web/assets/app.css‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,11 @@ textarea.input{height:auto;padding:8px 11px;line-height:1.5}
299299
.stat-v{font-size:var(--fs-lg);font-weight:600;letter-spacing:-.01em;font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
300300
/* 动作行:卡片底部独立一行,与统计/表格区用分隔线隔开,避免按钮混进信息堆 */
301301
.action-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:14px;padding-top:14px;border-top:1px solid color-mix(in oklab,var(--border) 55%,transparent)}
302+
/* 调用示例(密钥页「调用」卡片):多行命令保持原样换行,窄屏横向滚动而不折行;
303+
复制按钮浮于右上角 */
304+
.code-wrap{position:relative;margin-top:12px}
305+
.code-block{margin:0;background:var(--secondary);border-radius:8px;padding:11px 38px 11px 13px;font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;font-size:var(--fs-xs);line-height:1.7;color:var(--fg);white-space:pre;overflow-x:auto}
306+
.code-copy{position:absolute;top:6px;right:6px;background:var(--secondary)}
302307

303308
/* ── 任务 ── */
304309
.hm-cells{display:flex;flex-wrap:wrap;gap:3px;padding:2px 0;max-height:150px;overflow-y:auto}

‎web/assets/pages/keys.js‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
PAGE('keys', {
33
realtime: true,
44

5+
/* ── 调用卡片 ──
6+
* Base URL 由浏览器当前来源(location.origin)推导:用户可能用任意 IP / 端口部署,
7+
* 写死 localhost 的示例无法直接复制。面板与 API 同源;经反向代理改变对外域名时
8+
* 以代理暴露的地址为准(卡片内亦有提示)。
9+
*/
10+
get callBase(){ return location.origin + '/v1'; },
11+
get callChat(){ return this.callBase + '/chat/completions'; },
12+
get callModels(){ return this.callBase + '/models'; },
13+
/* 示例中的密钥用占位符:真实密钥在上方列表里,避免截图 / 录屏连带泄露 */
14+
get callCurl(){
15+
return `curl ${this.callChat} \\
16+
-H "Authorization: Bearer sk-xxxxxxxxxxxxxxxx" \\
17+
-H "Content-Type: application/json" \\
18+
-d '{"model":"Qwen3.8-Flash","messages":[{"role":"user","content":"你好"}],"stream":true}'`;
19+
},
20+
521
/* ── Keys ── */
622
async loadKeys(){
723
try{ const r = await this.api('/admin/api/keys'); this.keys = r.keys||[]; }

‎web/pages/keys.html‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ <h1>密钥</h1>
99
<div class="card">
1010
<div class="table-wrap" x-show="keys.length">
1111
<table>
12-
<thead><tr><th>密钥</th><th>备注</th><th>创建时间</th><th></th></tr></thead>
12+
<thead><tr><th>API Key</th><th>备注</th><th>创建时间</th><th></th></tr></thead>
1313
<tbody>
1414
<template x-for="k in keys" :key="k.id">
1515
<tr>
@@ -32,6 +32,33 @@ <h1>密钥</h1>
3232
暂无密钥,点击右上角创建第一个密钥
3333
</div>
3434
</div>
35+
36+
<!-- 调用:地址由浏览器当前来源推导(location.origin),用户以不同 IP / 端口部署时
37+
无需手动替换示例里的 localhost -->
38+
<div class="card">
39+
<div class="card-head">
40+
<h3>调用</h3>
41+
</div>
42+
<div class="kv">
43+
<span class="k">Base URL</span>
44+
<span class="v mono" x-text="callBase"></span>
45+
<button class="btn btn-ghost btn-sm" style="margin-left:auto" @click="copy(callBase)">复制</button>
46+
</div>
47+
<div class="kv">
48+
<span class="k">对话补全</span>
49+
<span class="v mono" x-text="callChat"></span>
50+
<button class="btn btn-ghost btn-sm" style="margin-left:auto" @click="copy(callChat)">复制</button>
51+
</div>
52+
<div class="kv">
53+
<span class="k">模型列表</span>
54+
<span class="v mono" x-text="callModels"></span>
55+
<button class="btn btn-ghost btn-sm" style="margin-left:auto" @click="copy(callModels)">复制</button>
56+
</div>
57+
<div class="code-wrap">
58+
<pre class="code-block" x-text="callCurl"></pre>
59+
<button class="btn btn-ghost btn-sm btn-icon code-copy" title="复制调用示例" aria-label="复制调用示例" @click="copy(callCurl)"><span class="ic" x-html="ic('copy')"></span></button>
60+
</div>
61+
</div>
3562
</section>
3663

3764
<!-- ═══ 创建密钥弹窗 ═══ -->

‎web/web_test.go‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,41 @@ func TestMovedModelsAndTier(t *testing.T) {
573573
}
574574
}
575575

576+
// TestKeysPageCallCard 密钥页的「调用」卡片:地址必须由浏览器当前来源推导
577+
// (location.origin),不能写死 localhost —— 用户会用不同的 IP / 端口部署,
578+
// 写死的示例无法直接复制;卡片同时给出 Base URL、两个端点的复制入口与调用示例。
579+
func TestKeysPageCallCard(t *testing.T) {
580+
read := func(fsys fs.FS, name string) string {
581+
t.Helper()
582+
b, err := fs.ReadFile(fsys, name)
583+
if err != nil {
584+
t.Fatalf("读取 %s: %v", name, err)
585+
}
586+
return string(b)
587+
}
588+
589+
html := read(pageFS, "pages/keys.html")
590+
for _, want := range []string{
591+
`<h3>调用</h3>`,
592+
`x-text="callBase"`, `x-text="callChat"`, `x-text="callModels"`, `x-text="callCurl"`,
593+
`@click="copy(callBase)"`, `@click="copy(callChat)"`, `@click="copy(callModels)"`, `@click="copy(callCurl)"`,
594+
} {
595+
if !strings.Contains(html, want) {
596+
t.Errorf("密钥页「调用」卡片缺少 %s", want)
597+
}
598+
}
599+
600+
js := read(assetFS, "assets/pages/keys.js")
601+
if !strings.Contains(js, "location.origin") {
602+
t.Error("调用地址必须由 location.origin 推导(用户以不同 IP / 端口部署)")
603+
}
604+
for _, dead := range []string{"http://localhost", "http://127.0.0.1"} {
605+
if strings.Contains(html, dead) || strings.Contains(js, dead) {
606+
t.Errorf("调用地址不得写死 %s(应由当前访问来源推导)", dead)
607+
}
608+
}
609+
}
610+
576611
// TestNoTemplateLeak 渲染产物不得残留模板指令。
577612
func TestNoTemplateLeak(t *testing.T) {
578613
h, _ := newTestRouter(t)

0 commit comments

Comments
 (0)