codebee 0.1.80 → 0.1.82
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/CHANGELOG.md +11 -0
- package/README.md +4 -2
- package/app/core/flows.py +11 -0
- package/app/core/pipeline.py +36 -11
- package/app/core/skills.py +9 -2
- package/app/core/store.py +4 -0
- package/app/ui/app.js +10 -1
- package/app/ui/i18n.js +6 -0
- package/app/ui/index.html +1 -0
- package/app/ui/style.css +7086 -7086
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
|
|
|
6
6
|
|
|
7
7
|
## 未发布
|
|
8
8
|
|
|
9
|
+
## v0.1.82(2026-10-04)
|
|
10
|
+
|
|
11
|
+
- **连载「多线剧情推演」开放到界面**:任务表单与流程编辑器都能设 1-3 路分支——起草前先让智能体推演几条剧情走向、择优再动笔(比同章多稿赛马省一个数量级 token)。此前该能力只能手拼 API,而且流程里配好的推演数会被任务创建/保存流程静默冲掉。
|
|
12
|
+
- **赛马复赛不再白烧**:容量受限通道上同章赛马首轮全挂后,复赛此前照发同一份全量提示词再烧一遍 N×2400 秒——现在复赛与重试同口径按身份收缩(经验库→4K、圣经保整段),首轮仍发全量先给一次机会。
|
|
13
|
+
- **降级发生可对账**:起草重试/赛马复赛触发降级时,运行页步骤说明会写明降了哪几层(如「降级:经验库→4K;模块库→边界截断」),提示词为什么变小不再靠猜。
|
|
14
|
+
|
|
15
|
+
## v0.1.81(2026-10-04)
|
|
16
|
+
|
|
17
|
+
- **连载超时重试的「分层降级」修准了**:此前起草超时重试把经验库/圣经/知识库折成一整块再收缩,只能整体 4K 硬截——现在三块分开留底,重试时按各自身份降级(经验库 4K、圣经按二级标题保整段、模块库按模块边界保整模块),大纲/前情/本章要点永不动。重试提示词保住的是结构化上下文,不是一截碎尾巴。
|
|
18
|
+
- **经验教训新增「没用」一键负反馈**:教训卡上可直接点「没用」——停用该条并记一次显式负证据(按失守同权计入排序,粘滞下沉)。此前用户只能绕道停用+手动台账,负反馈口径从台账推断的「没防住」变成用户亲口说。
|
|
19
|
+
|
|
9
20
|
## v0.1.80(2026-10-04)
|
|
10
21
|
|
|
11
22
|
- **翻译任务交稿前自查「翻译腔」**:翻译起草要求在术语表之外新增一条交稿自查——欧化句式/逐词直译当面清(长句拆短、被动改主动,清掉「被……所」「……性」「……的存在」这类直译腔标记),译文读感应是「用目标语写出来的」、不是「翻出来的」。此前翻译腔只能靠评审 rubric「流畅度」事后抓,现在起草侧先过一遍,少一轮返工。
|
package/README.md
CHANGED
|
@@ -33,9 +33,11 @@ Trae Agent、OpenClaw……),提供
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
35
|
<!-- relnotes:start -->
|
|
36
|
-
### 最新版更新内容(v0.1.
|
|
36
|
+
### 最新版更新内容(v0.1.82)
|
|
37
37
|
|
|
38
|
-
-
|
|
38
|
+
- 连载「多线剧情推演」开放到界面:任务表单/流程编辑器可设 1-3 路分支,写前推演择优再动笔;流程里配好的推演数/赛马数不再被静默冲掉
|
|
39
|
+
- 赛马复赛不再白烧:容量受限通道首轮全挂后,复赛按身份收缩提示词(大纲/前情永不动),首轮仍发全量
|
|
40
|
+
- 降级发生可对账:重试/复赛触发降级时,步骤说明写明降了哪几层
|
|
39
41
|
<!-- relnotes:end -->
|
|
40
42
|
|
|
41
43
|
|
package/app/core/flows.py
CHANGED
|
@@ -167,6 +167,17 @@ def _norm_serial(serial):
|
|
|
167
167
|
per = volumes.norm_per(serial.get("volume_chapters"))
|
|
168
168
|
if per:
|
|
169
169
|
out["volume_chapters"] = per
|
|
170
|
+
# 同章赛马/多线推演(1=不启用不落键):流程编辑器(app.js fl-variants)
|
|
171
|
+
# 会把 variants 随 serial 提交,漏带会在 upsert 时被静默丢弃——任务创建
|
|
172
|
+
# 从 flow.serial 继承(store.create_task)后永远读到 1,赛马形同虚设。
|
|
173
|
+
# 钳位口径与 store.create_task 一致(1-3)。
|
|
174
|
+
for key in ("variants", "branches"):
|
|
175
|
+
try:
|
|
176
|
+
n = max(1, min(3, int(serial.get(key) or 1)))
|
|
177
|
+
except Exception:
|
|
178
|
+
n = 1
|
|
179
|
+
if n > 1:
|
|
180
|
+
out[key] = n
|
|
170
181
|
return out
|
|
171
182
|
|
|
172
183
|
|
package/app/core/pipeline.py
CHANGED
|
@@ -2481,7 +2481,7 @@ def _shrink_context_block(sk_block, bible, budget=12000):
|
|
|
2481
2481
|
- 圣经截断保整段(按二级标题边界,无边界才硬截)
|
|
2482
2482
|
- 模块库截断保整模块(按「## 」标题边界)
|
|
2483
2483
|
- 经验库直接硬截(条目本身短,损失最小)
|
|
2484
|
-
返回 (新 sk_block, 降级说明)
|
|
2484
|
+
返回 (新 sk_block, 新 bible, 降级说明)。无降级原样返回(说明为空串)。"""
|
|
2485
2485
|
total = len(sk_block or "") + len(bible or "")
|
|
2486
2486
|
if total <= budget:
|
|
2487
2487
|
return sk_block, bible, ""
|
|
@@ -2516,6 +2516,15 @@ def _shrink_context_block(sk_block, bible, budget=12000):
|
|
|
2516
2516
|
return sk_block, bible, ";".join(notes)
|
|
2517
2517
|
|
|
2518
2518
|
|
|
2519
|
+
def _serial_shrunk_block(lessons, bible, kb_block, budget=12000):
|
|
2520
|
+
"""连载起草重试的分层降级组装:经验库与圣经按各自身份过 _shrink_context_block
|
|
2521
|
+
(经验库→4K、模块库按模块边界、圣经按二级标题边界),知识库块与大纲/前情
|
|
2522
|
+
同待遇永不动。返回 (重组后的替换块, 降级说明)——调用方整块 replace,字节级
|
|
2523
|
+
对位;降级说明(哪几层生效了)进 step note,运行页可对账(预算内为空串)。"""
|
|
2524
|
+
sk2, bible2, note = _shrink_context_block(lessons, bible, budget=budget)
|
|
2525
|
+
return "\n\n".join(p for p in (sk2, bible2, kb_block) if p), note
|
|
2526
|
+
|
|
2527
|
+
|
|
2519
2528
|
def _critic_lens(critics, agent):
|
|
2520
2529
|
"""该评审的专属视角;单评审/手动指定时不播种(无从轮换,也别稀释注意力)。"""
|
|
2521
2530
|
try:
|
|
@@ -3522,12 +3531,11 @@ def _run_serial_review(run, task, agents, ev, stats, mode, critics, impl, route,
|
|
|
3522
3531
|
stale_note = ("- 注意:目录里已有一版**未通过评审**的旧稿 `%s`——不要校验沿用、"
|
|
3523
3532
|
"不要增量修补,按本章任务直接覆盖重写。" % ch_file)
|
|
3524
3533
|
# stable_order:同一任务 8 个章节的技能块必须字节级一致(§07 T1.2' 前缀缓存)
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3534
|
+
# 三块分开留底:起草重试的分层降级(_serial_shrunk_block)要按
|
|
3535
|
+
# 「经验库/圣经」各自的身份收缩,折成一整块就只剩 4K 硬截一层了
|
|
3536
|
+
sk_lessons, _ = skills.block_for(task, stable_order=True, run_id=run_id)
|
|
3528
3537
|
kb_block = knowledge.block_for(task)
|
|
3529
|
-
if
|
|
3530
|
-
sk_block = (sk_block + "\n\n" + kb_block) if sk_block else kb_block
|
|
3538
|
+
sk_block = "\n\n".join(p for p in (sk_lessons, bible, kb_block) if p)
|
|
3531
3539
|
scope = ("本章 = 大纲第 %d 章" % i) if start == 1 else (
|
|
3532
3540
|
"本批为第 %d–%d 章,下列按全书章号列出各章要点" % (start, end))
|
|
3533
3541
|
|
|
@@ -3589,6 +3597,7 @@ def _run_serial_review(run, task, agents, ev, stats, mode, critics, impl, route,
|
|
|
3589
3597
|
last_attempt_reason = chapter_reason
|
|
3590
3598
|
prev_prompt = None # 上一次实际下发的提示词
|
|
3591
3599
|
prev_timed_out = False # 上一次是否「超时/停滞」收场
|
|
3600
|
+
shrink_note = "" # 本次重试的分层降级说明(进 step note 供对账)
|
|
3592
3601
|
for draft_attempt in range(3):
|
|
3593
3602
|
if draft_attempt:
|
|
3594
3603
|
# 30s / 60s 退避;ev.wait 睡等可被取消即刻唤醒
|
|
@@ -3602,8 +3611,9 @@ def _run_serial_review(run, task, agents, ev, stats, mode, critics, impl, route,
|
|
|
3602
3611
|
# ——分层降级:经验库→4K、模块库按模块边界、圣经按二级标题
|
|
3603
3612
|
# 边界,保大纲/前情/本章要点(2026-09-17 七猫实测:全量
|
|
3604
3613
|
# 30KB 对讯飞必挂)
|
|
3605
|
-
|
|
3606
|
-
|
|
3614
|
+
shrunk_block, shrink_note = _serial_shrunk_block(
|
|
3615
|
+
sk_lessons, bible, kb_block)
|
|
3616
|
+
use_prompt = prompt.replace(sk_block, shrunk_block)
|
|
3607
3617
|
# 超时重试必须有新变量才值得做(2026-09-22):提示词与上次逐字节
|
|
3608
3618
|
# 相同、上次又是超时/停滞收场时,重试只是把同一个超时再烧一遍
|
|
3609
3619
|
# (2400s × N)。此时直接跳出交替换将——换 CLI/模型才是新机会。
|
|
@@ -3615,7 +3625,9 @@ def _run_serial_review(run, task, agents, ev, stats, mode, critics, impl, route,
|
|
|
3615
3625
|
step_wd, readonly=False, ev=ev, timeout=2400,
|
|
3616
3626
|
resume=resume_ctx["session"] if resume_ctx else None,
|
|
3617
3627
|
images=_task_images(task, workdir),
|
|
3618
|
-
note=("起草重试 %d/2
|
|
3628
|
+
note=("起草重试 %d/2(网关限流退避%s)"
|
|
3629
|
+
% (draft_attempt,
|
|
3630
|
+
(";降级:" + shrink_note) if shrink_note else "")) if draft_attempt else "")
|
|
3619
3631
|
prev_prompt = use_prompt
|
|
3620
3632
|
prev_timed_out = bool((res.get("raw") or {}).get("timed_out"))
|
|
3621
3633
|
if (res.get("raw") or {}).get("repeat_stop"):
|
|
@@ -3722,6 +3734,10 @@ def _run_serial_review(run, task, agents, ev, stats, mode, critics, impl, route,
|
|
|
3722
3734
|
# 败稿删除;变体 0 = 本任作者(revise 会话沿用),其余取跨族优先的
|
|
3723
3735
|
# 其他真实智能体,不足时同作者开新会话凑数。
|
|
3724
3736
|
scored_variants = []
|
|
3737
|
+
# 复赛降级块(算一次):与非赛马起草重试同源(_serial_shrunk_block)
|
|
3738
|
+
# ——预算内与 sk_block 字节级一致(replace 等于原样),超预算才真降级
|
|
3739
|
+
race_shrunk_block, race_shrink_note = _serial_shrunk_block(
|
|
3740
|
+
sk_lessons, bible, kb_block)
|
|
3725
3741
|
for race_round in range(2):
|
|
3726
3742
|
# 全变体失败(网关突发限流)→ 60s 退避重赛一轮,别一章判死
|
|
3727
3743
|
if race_round:
|
|
@@ -3747,13 +3763,22 @@ def _run_serial_review(run, task, agents, ev, stats, mode, critics, impl, route,
|
|
|
3747
3763
|
|
|
3748
3764
|
def _draft_one(kk, agent):
|
|
3749
3765
|
vfile = "chapter-%02d-v%d.md" % (i, kk)
|
|
3766
|
+
vp = _draft_prompt(vfile)
|
|
3767
|
+
if race_round and len(vp) > 12000 and sk_block and sk_block in vp:
|
|
3768
|
+
# 复赛分层降级(与非赛马起草重试同口径):容量受限通道上
|
|
3769
|
+
# 全量提示词必挂,复赛照发同一份全量只是把 N×2400s 再烧
|
|
3770
|
+
# 一遍——按身份收缩经验库/圣经(大纲/前情/知识库块不动)。
|
|
3771
|
+
vp = vp.replace(sk_block, race_shrunk_block)
|
|
3772
|
+
note = "赛马变体 %d/%d(%s)" % (kk + 1, len(pool), agent.get("id"))
|
|
3773
|
+
if race_round and race_shrink_note:
|
|
3774
|
+
note += ";复赛降级:%s" % race_shrink_note
|
|
3750
3775
|
r = _run_step(run_id, "draft-c%d-v%d" % (i, kk),
|
|
3751
3776
|
modelhub.bind_agent(agent, difficulty),
|
|
3752
|
-
|
|
3777
|
+
vp, step_wd, readonly=False, ev=ev,
|
|
3753
3778
|
timeout=2400,
|
|
3754
3779
|
# 赛马只在全新起草时启用(无续会话),每路都是新会话
|
|
3755
3780
|
images=_task_images(task, workdir),
|
|
3756
|
-
note=
|
|
3781
|
+
note=note)
|
|
3757
3782
|
results[kk] = (vfile, agent, r)
|
|
3758
3783
|
|
|
3759
3784
|
threads = []
|
package/app/core/skills.py
CHANGED
|
@@ -336,6 +336,8 @@ def _karma(item):
|
|
|
336
336
|
lost = max(0, int(item.get("lost") or 0))
|
|
337
337
|
if lost > hits:
|
|
338
338
|
lost = hits # 台账被手工改坏时不失守倒挂
|
|
339
|
+
useless = min(max(0, int(item.get("useless") or 0)), hits)
|
|
340
|
+
lost = max(lost, useless) # 用户显式「标记无用」按失守同权计入(粘滞负证据)
|
|
339
341
|
return (hits - lost + _KARMA_PRIOR) / (hits + 2 * _KARMA_PRIOR)
|
|
340
342
|
|
|
341
343
|
|
|
@@ -351,6 +353,7 @@ def _surplus_decay(item, now=None):
|
|
|
351
353
|
被再次触及即重置。只压排序,不删除、不改数据。"""
|
|
352
354
|
hits = max(0, int(item.get("hits") or 0))
|
|
353
355
|
lost = min(max(0, int(item.get("lost") or 0)), hits)
|
|
356
|
+
lost = max(lost, min(max(0, int(item.get("useless") or 0)), hits))
|
|
354
357
|
if int(item.get("won") or 0) > 0:
|
|
355
358
|
return hits - lost, lost
|
|
356
359
|
stamp = item.get("updated_at") or item.get("created_at")
|
|
@@ -489,8 +492,9 @@ def upsert_lesson(scope, title, content, source="", category=None, dim=None):
|
|
|
489
492
|
|
|
490
493
|
|
|
491
494
|
def lesson_op(lesson_id, op):
|
|
492
|
-
"""
|
|
493
|
-
|
|
495
|
+
"""启用/停用/删除教训;useless=用户显式「标记无用」(停用+记粘滞负证据)。
|
|
496
|
+
返回错误或 None。"""
|
|
497
|
+
if op not in ("enable", "disable", "delete", "useless"):
|
|
494
498
|
return "未知操作 " + str(op)
|
|
495
499
|
with _LOCK:
|
|
496
500
|
data = _load()
|
|
@@ -500,6 +504,9 @@ def lesson_op(lesson_id, op):
|
|
|
500
504
|
return "教训不存在"
|
|
501
505
|
if op == "delete":
|
|
502
506
|
data["lessons"] = [x for x in items if x.get("id") != lesson_id]
|
|
507
|
+
elif op == "useless":
|
|
508
|
+
hit["enabled"] = False # 显式负反馈即停用:不再注入
|
|
509
|
+
hit["useless"] = int(hit.get("useless") or 0) + 1
|
|
503
510
|
else:
|
|
504
511
|
hit["enabled"] = (op == "enable")
|
|
505
512
|
_save(data)
|
package/app/core/store.py
CHANGED
|
@@ -1801,6 +1801,10 @@ def continue_task(task_id, chapters=None):
|
|
|
1801
1801
|
}
|
|
1802
1802
|
if serial.get("variants"): # 赛马配置随链条沿用
|
|
1803
1803
|
payload["serial"]["variants"] = serial["variants"]
|
|
1804
|
+
# 多线推演(branches)同口径随链条沿用:漏带则续写批次恒回 1,
|
|
1805
|
+
# 多线剧情推演在连载链上只剩首批生效
|
|
1806
|
+
if serial.get("branches"):
|
|
1807
|
+
payload["serial"]["branches"] = serial["branches"]
|
|
1804
1808
|
# 分卷配置随链条沿用:卷边界必须跨批次稳定,否则同一卷会被续写批次重切
|
|
1805
1809
|
if serial.get("volumes"):
|
|
1806
1810
|
payload["serial"]["volumes"] = serial["volumes"]
|
package/app/ui/app.js
CHANGED
|
@@ -766,7 +766,7 @@ function onTypeChange() {
|
|
|
766
766
|
const bibleCreate = $("bible-create-field");
|
|
767
767
|
const isSerialReview = isReview && !!(flow && flow.serial);
|
|
768
768
|
if (bibleCreate) bibleCreate.classList.toggle("hidden", !isSerialReview);
|
|
769
|
-
const clearSerialFields = () => ["f-chapters", "f-words-per-ch", "f-variants", "f-bible",
|
|
769
|
+
const clearSerialFields = () => ["f-chapters", "f-words-per-ch", "f-variants", "f-branches", "f-bible",
|
|
770
770
|
"f-vol-chapters", "f-vol-spec"].forEach((id) => {
|
|
771
771
|
const el = $(id);
|
|
772
772
|
if (el) el.value = "";
|
|
@@ -3187,6 +3187,9 @@ async function createTask() {
|
|
|
3187
3187
|
chapters: ch,
|
|
3188
3188
|
words_per_chapter: parseInt($("f-words-per-ch").value, 10) || 2500,
|
|
3189
3189
|
variants: Math.max(1, Math.min(3, parseInt($("f-variants").value, 10) || 1)),
|
|
3190
|
+
// 多线剧情推演:任务级 serial 整体替换流程默认,漏带会把流程里
|
|
3191
|
+
// 配好的 branches 冲掉(store.create_task 字段级不合并)
|
|
3192
|
+
branches: Math.max(1, Math.min(3, parseInt($("f-branches").value, 10) || 1)),
|
|
3190
3193
|
};
|
|
3191
3194
|
// 分卷:显式卷结构文本优先(后端解析成卷表),否则用每卷章数。
|
|
3192
3195
|
// 两个都留空 = 不分卷(后端收到 0/缺字段即不分卷)。
|
|
@@ -3951,6 +3954,7 @@ async function newFromTaskAsync(id) {
|
|
|
3951
3954
|
$("f-chapters").value = tk.serial ? tk.serial.chapters : "";
|
|
3952
3955
|
$("f-words-per-ch").value = tk.serial ? tk.serial.words_per_chapter : "";
|
|
3953
3956
|
$("f-variants").value = tk.serial && tk.serial.variants ? tk.serial.variants : "";
|
|
3957
|
+
$("f-branches").value = tk.serial && tk.serial.branches ? tk.serial.branches : "";
|
|
3954
3958
|
// 分卷:还原每卷章数;指定卷结构还原成可读文本(卷名 + 章数/章号范围)
|
|
3955
3959
|
$("f-vol-chapters").value = tk.serial && tk.serial.volume_chapters ? tk.serial.volume_chapters : "";
|
|
3956
3960
|
$("f-vol-spec").value = tk.serial && Array.isArray(tk.serial.volumes)
|
|
@@ -10887,6 +10891,7 @@ function flowForm(fid) {
|
|
|
10887
10891
|
'<div class="field"><label>' + t("连载章节数(留空 = 单稿件)") + '</label><input id="fl-chapters" type="number" min="2" max="20" value="' + (f && f.serial ? f.serial.chapters : "") + '" placeholder="' + t("例:8") + '"></div>' +
|
|
10888
10892
|
'<div class="field"><label>' + t("每章约字数") + '</label><input id="fl-words-per-ch" type="number" min="500" max="8000" step="100" value="' + (f && f.serial ? f.serial.words_per_chapter : "") + '" placeholder="' + t("例:2500") + '"></div>' +
|
|
10889
10893
|
'<div class="field"><label>' + t("同章赛马稿件数") + '</label><input id="fl-variants" type="number" min="1" max="3" step="1" value="' + (f && f.serial ? (f.serial.variants || 1) : 1) + '" placeholder="' + t("1 = 关闭") + '"></div>' +
|
|
10894
|
+
'<div class="field"><label>' + t("多线剧情推演数") + '</label><input id="fl-branches" type="number" min="1" max="3" step="1" value="' + (f && f.serial ? (f.serial.branches || 1) : 1) + '" placeholder="' + t("1 = 关闭") + '"></div>' +
|
|
10890
10895
|
'<div class="field"><label>' + t("分卷:每卷章数(留空 = 不分卷)") + '</label><input id="fl-vol-chapters" type="number" min="2" max="200" step="1" value="' + (f && f.serial && f.serial.volume_chapters ? f.serial.volume_chapters : "") + '" placeholder="' + t("例:20") + '"></div>' +
|
|
10891
10896
|
"</div>" +
|
|
10892
10897
|
'<div class="field"><label>' + t("起草提示词(可选,占位符 __FILE__ __GOAL__ __CONTEXT__ __SKILLS__)") + '</label><textarea id="fl-draft" rows="3" placeholder="' + t("留空 = 内置通用模板") + '">' + esc(f && f.draft_prompt ? f.draft_prompt : "") + "</textarea></div>" +
|
|
@@ -10932,6 +10937,7 @@ async function saveFlow() {
|
|
|
10932
10937
|
chapters: ch,
|
|
10933
10938
|
words_per_chapter: parseInt($("fl-words-per-ch").value, 10) || 2500,
|
|
10934
10939
|
variants: Math.max(1, Math.min(3, parseInt($("fl-variants").value, 10) || 1)),
|
|
10940
|
+
branches: Math.max(1, Math.min(3, parseInt($("fl-branches").value, 10) || 1)),
|
|
10935
10941
|
};
|
|
10936
10942
|
const vp = parseInt(($("fl-vol-chapters") || {}).value, 10);
|
|
10937
10943
|
if (vp >= 2) payload.serial.volume_chapters = vp; // 流程默认分卷(任务级可覆盖)
|
|
@@ -11032,11 +11038,13 @@ function renderSkills() {
|
|
|
11032
11038
|
(x.hits ? '<span class="tag">' + t("已注入 ") + x.hits + t(" 次") + "</span>" : "") +
|
|
11033
11039
|
(x.won > 0 ? '<span class="tag ok" title="' + t("注入后任务过审的次数(真实帮上忙)") + '">' + t("有效 ") + x.won + "</span>" : "") +
|
|
11034
11040
|
(x.lost > 0 ? '<span class="tag" style="color:var(--bad)" title="' + t("注入后任务仍失败的次数(没防住)") + '">' + t("失守 ") + x.lost + "</span>" : "") +
|
|
11041
|
+
(x.useless ? '<span class="tag" style="color:var(--bad)" title="' + t("用户标记无用的次数(显式负反馈,排序下沉)") + '">' + t("没用 ") + x.useless + "</span>" : "") +
|
|
11035
11042
|
(x.enabled === false ? '<span class="tag">' + t("已停用") + "</span>" : "") + "</div>" +
|
|
11036
11043
|
'<div class="note">' + esc(t(x.content)) + "</div>" +
|
|
11037
11044
|
'<div class="ops">' +
|
|
11038
11045
|
'<button class="ghost small" onclick="skillLessonOp(\'' + esc(x.id) + '\', \'' +
|
|
11039
11046
|
(x.enabled === false ? "enable" : "disable") + '\')">' + (x.enabled === false ? t("启用") : t("停用")) + "</button>" +
|
|
11047
|
+
'<button class="ghost small" onclick="skillLessonOp(\'' + esc(x.id) + '\', \'useless\')" title="' + t("标记这条教训没帮上忙?将停用并记一次无用反馈") + '">' + t("没用") + "</button>" +
|
|
11040
11048
|
'<button class="danger small" onclick="skillLessonOp(\'' + esc(x.id) + '\', \'delete\')">' + t("删除") + "</button>" +
|
|
11041
11049
|
"</div></div>").join("") ||
|
|
11042
11050
|
(filt ? '<div class="empty">' + t("该分类下暂无教训") + "</div>"
|
|
@@ -11055,6 +11063,7 @@ async function skillPackOp(id, op) {
|
|
|
11055
11063
|
}
|
|
11056
11064
|
|
|
11057
11065
|
async function skillLessonOp(id, op) {
|
|
11066
|
+
if (op === "useless" && !await uiConfirm(t("标记这条教训没帮上忙?将停用并记一次无用反馈"), { ok: t("没用"), danger: true })) return;
|
|
11058
11067
|
if (op === "delete" && !await uiConfirm(t("删除这条教训?"), { ok: t("删除"), danger: true })) return;
|
|
11059
11068
|
try { await api("/api/skills/lesson-op", { method: "POST", body: JSON.stringify({ id, op }) }); }
|
|
11060
11069
|
catch (e) { toast(e.message, true); return; }
|
package/app/ui/i18n.js
CHANGED
|
@@ -728,6 +728,10 @@
|
|
|
728
728
|
"删除该任务及其全部运行记录(含日志与报告)?不可恢复。": "Delete this task and all its runs (logs and reports)? This cannot be undone.",
|
|
729
729
|
"删除该运行记录(含全部日志与报告)?不可恢复。": "Delete this run (with logs and reports)? This cannot be undone.",
|
|
730
730
|
"删除这条教训?": "Delete this lesson?",
|
|
731
|
+
"没用": "Useless",
|
|
732
|
+
"没用 ": "Useless ",
|
|
733
|
+
"标记这条教训没帮上忙?将停用并记一次无用反馈": "Mark this lesson as unhelpful? It gets disabled and recorded as negative feedback.",
|
|
734
|
+
"用户标记无用的次数(显式负反馈,排序下沉)": "Times the user marked this useless (explicit negative feedback; sinks in ranking)",
|
|
731
735
|
"删除全部已结束的运行记录(运行中的会保留)": "Delete all finished runs (active ones are kept)",
|
|
732
736
|
"清除全部运行记录(含日志与报告)?运行中的记录会保留,需先取消。不可恢复。": "Clear all runs (logs and reports)? Active runs are kept — cancel them first. This cannot be undone.",
|
|
733
737
|
"急停": "Kill all",
|
|
@@ -1827,6 +1831,8 @@
|
|
|
1827
1831
|
"工作目录内的文件名": "File name inside the workdir",
|
|
1828
1832
|
"1 = 关闭": "1 = off",
|
|
1829
1833
|
"1 = 关闭(2-3 每章多稿择优,更贵)": "1 = off (2-3 drafts per chapter, best wins — pricier)",
|
|
1834
|
+
"多线剧情推演数": "Plot branch plans per chapter",
|
|
1835
|
+
"1 = 关闭(2-3 写前剧情分支推演择优)": "1 = off (2-3 branch plans, best picked before drafting)",
|
|
1830
1836
|
"小写字母开头,可留空": "start with a lowercase letter; optional",
|
|
1831
1837
|
"留空 = 2": "empty = 2",
|
|
1832
1838
|
"留空 = 7.0": "empty = 7.0",
|
package/app/ui/index.html
CHANGED
|
@@ -510,6 +510,7 @@
|
|
|
510
510
|
<div class="field"><label data-i18n="连载模式:章节数(空 = 单稿件一次成文)">连载模式:章节数(空 = 单稿件一次成文)</label><input id="f-chapters" type="number" min="2" max="20" placeholder="例:8(逐章起草+评审+修订)" data-i18n-ph="例:8(逐章起草+评审+修订)"></div>
|
|
511
511
|
<div class="field"><label data-i18n="每章约字数">每章约字数</label><input id="f-words-per-ch" type="number" min="500" max="8000" step="100" placeholder="例:2500" data-i18n-ph="例:2500"></div>
|
|
512
512
|
<div class="field"><label data-i18n="同章赛马稿件数">同章赛马稿件数</label><input id="f-variants" type="number" min="1" max="3" step="1" placeholder="1 = 关闭(2-3 每章多稿择优,更贵)" data-i18n-ph="1 = 关闭(2-3 每章多稿择优,更贵)"></div>
|
|
513
|
+
<div class="field"><label data-i18n="多线剧情推演数">多线剧情推演数</label><input id="f-branches" type="number" min="1" max="3" step="1" placeholder="1 = 关闭(2-3 写前剧情分支推演择优)" data-i18n-ph="1 = 关闭(2-3 写前剧情分支推演择优)"></div>
|
|
513
514
|
<div class="field"><label data-i18n="分卷:每卷章数(空 = 不分卷)">分卷:每卷章数(空 = 不分卷)</label><input id="f-vol-chapters" type="number" min="2" max="200" step="1" placeholder="例:20(卷边界按全书章号自动推导)" data-i18n-ph="例:20(卷边界按全书章号自动推导)"></div>
|
|
514
515
|
</div>
|
|
515
516
|
<div class="field" id="f-vol-spec-field"><label data-i18n="分卷:指定卷结构(可选,优先于每卷章数)">分卷:指定卷结构(可选,优先于每卷章数)</label><input id="f-vol-spec" placeholder="例:第一卷 少年初入江湖 20章;第二卷 风云再起 16章" data-i18n-ph="例:第一卷 少年初入江湖 20章;第二卷 风云再起 16章" data-i18n-title="每卷一行或分号分隔:卷名 + 章数(或第1-20章)。也可以在任务目标里直接这样写,会自动识别" title="每卷一行或分号分隔:卷名 + 章数(或第1-20章)。也可以在任务目标里直接这样写,会自动识别"></div>
|