@whispering233/ai-editor-tools 0.0.57 → 0.0.58
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +176 -4
- package/dist/index.js.map +1 -1
- package/dist/query/manuscript.d.ts +7 -4
- package/dist/query/manuscript.d.ts.map +1 -1
- package/dist/query/manuscript.js +8 -5
- package/dist/query/manuscript.js.map +1 -1
- package/dist/query/practice.d.ts +94 -0
- package/dist/query/practice.d.ts.map +1 -0
- package/dist/query/practice.js +241 -0
- package/dist/query/practice.js.map +1 -0
- package/dist/registry.d.ts +5 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +9 -2
- package/dist/registry.js.map +1 -1
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/manuscript.d.ts +1 -1
- package/dist/schemas/manuscript.js +1 -1
- package/dist/schemas/practice.d.ts +87 -0
- package/dist/schemas/practice.d.ts.map +1 -0
- package/dist/schemas/practice.js +75 -0
- package/dist/schemas/practice.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ToolContext } from "../context.js";
|
|
2
|
+
import type { FreeDraftPageArgs, PracticeTextPageArgs, StyleNotesPageArgs, SubmitFreeFeedbackArgs, SubmitPracticeTopicArgs, SubmitStyleFeedbackArgs } from "../schemas/index.js";
|
|
3
|
+
/** 只读工具的分页信封(四个只读工具**同形**;工具目录逐字对齐 docs/api/tool-calling.md「练习」) */
|
|
4
|
+
export interface PracticeTextPage {
|
|
5
|
+
/** 本次所读文本全长(范文 / 稿件 / 风格说明),与 offset 无关 */
|
|
6
|
+
char_count: number;
|
|
7
|
+
offset: number;
|
|
8
|
+
/** 本次实际返回的文本片段长度(不含截断提示文案) */
|
|
9
|
+
returned_chars: number;
|
|
10
|
+
text: string;
|
|
11
|
+
truncated: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** 范文 / 仿写稿页(两个样本侧只读工具同形):`work_name` 为空即 shared 兜底文案「其他」 */
|
|
14
|
+
export interface PracticeSampleTextPage extends PracticeTextPage {
|
|
15
|
+
sample_id: string;
|
|
16
|
+
/** 关联作品名(点评对照需要,免二次查询;`work_id` 空 / 未知 → shared `PRACTICE_OTHER_WORK_LABEL`) */
|
|
17
|
+
work_name: string;
|
|
18
|
+
/** 片段说明(用户手填,如「汪曾祺《受戒》节选」;空 = 未填) */
|
|
19
|
+
excerpt_label: string;
|
|
20
|
+
}
|
|
21
|
+
/** 自由练习稿页(get_free_draft;自由练习不挂作品 / 片段说明,故只有 piece_id) */
|
|
22
|
+
export interface FreeDraftTextPage extends PracticeTextPage {
|
|
23
|
+
piece_id: string;
|
|
24
|
+
}
|
|
25
|
+
/** 风格说明页(get_style_notes):`customized` = 有用户覆盖(内容 = 覆盖 `??` 预置) */
|
|
26
|
+
export interface StyleNotesTextPage extends PracticeTextPage {
|
|
27
|
+
author_id: string;
|
|
28
|
+
author_name: string;
|
|
29
|
+
customized: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** 提交点评结果(两个提交工具同形;随 `tool_execution_end` 帧下发,前端据此即时刷新卡片) */
|
|
32
|
+
export interface SubmitFeedbackResult {
|
|
33
|
+
saved: true;
|
|
34
|
+
/** 落库的维度数 */
|
|
35
|
+
dimension_count: number;
|
|
36
|
+
/** 被丢弃的引用条数(依据侧 + 稿件侧合计;逐字找不到的引用,以及空串 / 纯空白引用) */
|
|
37
|
+
dropped_quotes: number;
|
|
38
|
+
}
|
|
39
|
+
/** 出题结果(随 `tool_execution_end` 帧下发;前端据 `piece_id` 重拉该篇详情) */
|
|
40
|
+
export interface SubmitPracticeTopicResult {
|
|
41
|
+
saved: true;
|
|
42
|
+
piece_id: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 范文快照只读分页(get_practice_sample(sample_id, offset?, max_chars?))。
|
|
46
|
+
* @throws Error 样本不存在;offset 非非负整数(schema 之外防御)
|
|
47
|
+
*/
|
|
48
|
+
export declare function runGetPracticeSample(ctx: ToolContext, args: PracticeTextPageArgs): PracticeSampleTextPage;
|
|
49
|
+
/**
|
|
50
|
+
* 用户仿写稿只读分页(get_practice_draft(sample_id, offset?, max_chars?))。
|
|
51
|
+
* 未写稿 → char_count=0、text=""(不报错);**只读**——工具面不存在写稿工具。
|
|
52
|
+
* @throws Error 样本不存在;offset 非非负整数
|
|
53
|
+
*/
|
|
54
|
+
export declare function runGetPracticeDraft(ctx: ToolContext, args: PracticeTextPageArgs): PracticeSampleTextPage;
|
|
55
|
+
/**
|
|
56
|
+
* 用户自由练习稿只读分页(get_free_draft(piece_id, offset?, max_chars?))。
|
|
57
|
+
* 未写稿 → char_count=0、text=""(不报错);**只读**——工具面不存在写稿工具。
|
|
58
|
+
* @throws Error 篇不存在;offset 非非负整数
|
|
59
|
+
*/
|
|
60
|
+
export declare function runGetFreeDraft(ctx: ToolContext, args: FreeDraftPageArgs): FreeDraftTextPage;
|
|
61
|
+
/**
|
|
62
|
+
* 作家风格说明只读分页(get_style_notes(offset?, max_chars?))——自由练习点评的**唯一依据**
|
|
63
|
+
* (仿写点评可按需参考,不强制引用)。
|
|
64
|
+
* 内容 = 用户覆盖 `??` 预置;整份说明按长文分页(不整份灌入上下文)。
|
|
65
|
+
* @throws Error 工作区未关联预置作家;offset 非非负整数
|
|
66
|
+
*/
|
|
67
|
+
export declare function runGetStyleNotes(ctx: ToolContext, args: StyleNotesPageArgs): StyleNotesTextPage;
|
|
68
|
+
/**
|
|
69
|
+
* 提交仿写点评卡(submit_style_feedback(sample_id, dimensions[], summary?))——写 `practice_samples.feedback`。
|
|
70
|
+
*
|
|
71
|
+
* 口径:引用校验源 = 范文 `text`(`sample_quotes`)与仿写稿 `draft`(`draft_quotes`);
|
|
72
|
+
* **全部引用非法时仍成功写卡**(卡片可以没有引用——诊断不依赖引用数)。只写 `feedback`:
|
|
73
|
+
* 范文与用户稿一字不动。
|
|
74
|
+
* @throws Error 维度超上限;样本不存在
|
|
75
|
+
*/
|
|
76
|
+
export declare function runSubmitStyleFeedback(ctx: ToolContext, args: SubmitStyleFeedbackArgs): SubmitFeedbackResult;
|
|
77
|
+
/**
|
|
78
|
+
* 提交自由练习点评卡(submit_free_feedback(piece_id, dimensions[], summary?))——写
|
|
79
|
+
* `practice_free_pieces.feedback`(卡形与仿写**同形**,差别只在引用校验源)。
|
|
80
|
+
*
|
|
81
|
+
* 口径:引用校验源 = 风格说明(用户覆盖 `??` 预置)与自由稿 `draft`;其余同仿写。
|
|
82
|
+
* @throws Error 维度超上限;篇不存在;工作区未关联预置作家(风格说明的来源不存在)
|
|
83
|
+
*/
|
|
84
|
+
export declare function runSubmitFreeFeedback(ctx: ToolContext, args: SubmitFreeFeedbackArgs): SubmitFeedbackResult;
|
|
85
|
+
/**
|
|
86
|
+
* 出题(submit_practice_topic(piece_id, topic, brief?))——写 `practice_free_pieces.topic` / `brief`
|
|
87
|
+
* (`brief` 省略 = 不改)。
|
|
88
|
+
*
|
|
89
|
+
* **题目非空 → 抛错拒写**(防覆盖已出的题 / 用户已开始的练习;不满意的题 = 删掉该篇重建,
|
|
90
|
+
* 不做覆盖式换题);空白题目同样拒写(落库会在界面留一条看不见的「已出题」记录)。
|
|
91
|
+
* @throws Error 该篇已有题目;题目空白;篇不存在
|
|
92
|
+
*/
|
|
93
|
+
export declare function runSubmitPracticeTopic(ctx: ToolContext, args: SubmitPracticeTopicArgs): SubmitPracticeTopicResult;
|
|
94
|
+
//# sourceMappingURL=practice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"practice.d.ts","sourceRoot":"","sources":["../../src/query/practice.ts"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAEV,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,6DAA6D;AAC7D,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,IAAI,CAAC;IACZ,aAAa;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuFD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,CAWzG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,CAWxG;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,CAG5F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,CAQ/F;AAgDD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,uBAAuB,GAC5B,oBAAoB,CAatB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,sBAAsB,GAC3B,oBAAoB,CActB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,uBAAuB,GAC5B,yBAAyB,CAa3B"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// 查询类工具:作家文风练习(四只读分页 + 三写 AI 自己的产出,2026-10 改版)
|
|
2
|
+
//
|
|
3
|
+
// 口径(docs/api/tool-calling.md「练习(2026-10)」、docs/design/60-practice.md §6/§7/§8):
|
|
4
|
+
// - get_practice_sample / get_practice_draft / get_free_draft / get_style_notes:分页读**范文快照**、
|
|
5
|
+
// **仿写稿**、**自由练习稿**、**作家风格说明**——四者都不进本轮上下文,AI 按需拉取;offset / max_chars
|
|
6
|
+
// 与 get_chapter_text **共用同一对分页常量**(`query/manuscript.ts` 的 DEFAULT_TEXT_PAGE_CHARS /
|
|
7
|
+
// MAX_TEXT_PAGE_CHARS——文本分页预算单源,实现层 clamp);截断时 text 末尾附续读提示
|
|
8
|
+
// (「截断必须显式告知」,docs/design/20-context.md §2)
|
|
9
|
+
// - 三个写工具只写 **AI 自己的产出**(`feedback` / `topic` / `brief`):工具面**没有任何写稿入口**
|
|
10
|
+
// (用户稿只有用户能写——结构性边界,不是提示词约束);提交前逐条校验引用子串,不匹配即丢弃并计入
|
|
11
|
+
// dropped_quotes(不阻断、不重试);空串 / 纯空白引用同样丢弃(否则 `includes("")` 恒真)
|
|
12
|
+
// - 引用校验**按字段分源**:`sample_quotes` ⊆ 该形态的依据(仿写 = 范文 `text`、自由练习 = 风格说明
|
|
13
|
+
// (用户覆盖 `??` shared 预置)),`draft_quotes` ⊆ 用户稿 `draft`
|
|
14
|
+
// - 写库一律经 db helper——禁止在工具层直写表(库写入唯一入口)
|
|
15
|
+
//
|
|
16
|
+
// **分页预算实测**(真路径 `truncateToolResultText(stringifyToolResult(结果))`,纯 CJK 文本、正文 = 页
|
|
17
|
+
// 上限 `MAX_TEXT_PAGE_CHARS` + 1 字、信封字段取满 = 最长预置作品名 + `excerpt_label` 上限
|
|
18
|
+
// `MAX_PRACTICE_EXCERPT_LABEL_CHARS`):
|
|
19
|
+
// - `get_practice_sample` 上限页 = **7163 tokens**(余量 10.5%)
|
|
20
|
+
// - `get_style_notes` 上限页 = **7053 tokens**(余量 11.8%)
|
|
21
|
+
// 两者都在单条上限 `TOOL_RESULT_MAX_TOKENS` 以内(续读提示与 `truncated` 标记不被外层截断切掉)。
|
|
22
|
+
// 改任一上限前必须重走同一条路径(口径同 manuscript.ts)。
|
|
23
|
+
import { getPracticePiece, getPracticeSample, getStylePack, readProjectFile, updatePracticeFeedback, updatePracticePieceFeedback, updatePracticePieceTopic, } from "@whispering233/ai-editor-db";
|
|
24
|
+
import { MAX_FEEDBACK_DIMENSIONS, PRACTICE_OTHER_WORK_LABEL, resolveAuthor, resolveWork, } from "@whispering233/ai-editor-shared";
|
|
25
|
+
import { DEFAULT_TEXT_PAGE_CHARS, MAX_TEXT_PAGE_CHARS } from "./manuscript.js";
|
|
26
|
+
/**
|
|
27
|
+
* 当前工作区的预置作家(`project.json` 的 `author_id` 经 shared `resolveAuthor` 解析)。
|
|
28
|
+
* 读侧容错:解析不到返回 null(读范文 / 稿件不该因作家未关联而失败——作品名退兜底文案)。
|
|
29
|
+
*/
|
|
30
|
+
function currentAuthor(ctx) {
|
|
31
|
+
return resolveAuthor(readProjectFile(ctx.outlineDir)?.author_id);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 需要作家事实的路径(读 / 写风格说明)用的严格解析。
|
|
35
|
+
* @throws Error 工作区未关联预置作家(`author_id` 缺失 / 未知)——风格说明的来源不存在
|
|
36
|
+
*/
|
|
37
|
+
function requireAuthor(ctx) {
|
|
38
|
+
const author = currentAuthor(ctx);
|
|
39
|
+
if (author === null) {
|
|
40
|
+
throw new Error("练习工作区未关联预置作家(project.json 的 author_id 缺失或未知)");
|
|
41
|
+
}
|
|
42
|
+
return author;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 生效的风格说明 = 用户覆盖 `??` shared 预置(与 `GET /practice/style-pack` 同一口径);
|
|
46
|
+
* `customized` = 存在用户覆盖(删除覆盖即恢复预置)。
|
|
47
|
+
*/
|
|
48
|
+
function effectiveStyleNotes(ctx, author) {
|
|
49
|
+
const override = getStylePack(ctx.db, author.id);
|
|
50
|
+
return { content: override?.content ?? author.styleNotes, customized: override !== null };
|
|
51
|
+
}
|
|
52
|
+
/** 作品名:`work_id` 空 / 未知(含作家未关联)→ shared 兜底文案「其他」 */
|
|
53
|
+
function workNameOf(author, workId) {
|
|
54
|
+
if (author === null || workId === null)
|
|
55
|
+
return PRACTICE_OTHER_WORK_LABEL;
|
|
56
|
+
return resolveWork(author.id, workId)?.name ?? PRACTICE_OTHER_WORK_LABEL;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 样本行(两个样本侧只读工具与仿写点评共用)。
|
|
60
|
+
* @throws Error 样本不存在
|
|
61
|
+
*/
|
|
62
|
+
function requireSample(ctx, sampleId) {
|
|
63
|
+
const sample = getPracticeSample(ctx.db, sampleId);
|
|
64
|
+
if (sample === null)
|
|
65
|
+
throw new Error(`练习样本不存在(sample_id=${sampleId})`);
|
|
66
|
+
return sample;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 自由练习篇(自由稿只读、自由点评、出题共用)。
|
|
70
|
+
* @throws Error 篇不存在
|
|
71
|
+
*/
|
|
72
|
+
function requirePiece(ctx, pieceId) {
|
|
73
|
+
const piece = getPracticePiece(ctx.db, pieceId);
|
|
74
|
+
if (piece === null)
|
|
75
|
+
throw new Error(`自由练习不存在(piece_id=${pieceId})`);
|
|
76
|
+
return piece;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 文本分页切片 + 截断续读提示(形状照 `query/manuscript.ts` 的 get_chapter_text:同样只认字符下标,
|
|
80
|
+
* 同样由被截断的那一页显式告知续读起点)。
|
|
81
|
+
*
|
|
82
|
+
* clamp 上限取 `MAX_TEXT_PAGE_CHARS`(与 get_chapter_text **同一常量**)——预算必须连 JSON 信封
|
|
83
|
+
* 与续读提示一起压进单条工具结果预算(agent 侧 `TOOL_RESULT_MAX_TOKENS`,实测口径见 manuscript.ts)。
|
|
84
|
+
*/
|
|
85
|
+
function readTextPage(fields, sourceText, args) {
|
|
86
|
+
const offset = args.offset ?? 0;
|
|
87
|
+
if (!Number.isInteger(offset) || offset < 0) {
|
|
88
|
+
throw new Error(`offset 必须为非负整数: ${String(args.offset)}`);
|
|
89
|
+
}
|
|
90
|
+
const maxChars = Math.min(MAX_TEXT_PAGE_CHARS, Math.max(1, Math.trunc(args.max_chars ?? DEFAULT_TEXT_PAGE_CHARS)));
|
|
91
|
+
const slice = sourceText.slice(offset, offset + maxChars);
|
|
92
|
+
const truncated = offset + slice.length < sourceText.length;
|
|
93
|
+
const text = truncated
|
|
94
|
+
? `${slice}\n\n(已截断,可用 offset=${offset + slice.length} 继续读取)`
|
|
95
|
+
: slice;
|
|
96
|
+
return { ...fields, char_count: sourceText.length, offset, returned_chars: slice.length, text, truncated };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 范文快照只读分页(get_practice_sample(sample_id, offset?, max_chars?))。
|
|
100
|
+
* @throws Error 样本不存在;offset 非非负整数(schema 之外防御)
|
|
101
|
+
*/
|
|
102
|
+
export function runGetPracticeSample(ctx, args) {
|
|
103
|
+
const sample = requireSample(ctx, args.sample_id);
|
|
104
|
+
return readTextPage({
|
|
105
|
+
sample_id: sample.id,
|
|
106
|
+
work_name: workNameOf(currentAuthor(ctx), sample.work_id),
|
|
107
|
+
excerpt_label: sample.excerpt_label,
|
|
108
|
+
}, sample.text, args);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 用户仿写稿只读分页(get_practice_draft(sample_id, offset?, max_chars?))。
|
|
112
|
+
* 未写稿 → char_count=0、text=""(不报错);**只读**——工具面不存在写稿工具。
|
|
113
|
+
* @throws Error 样本不存在;offset 非非负整数
|
|
114
|
+
*/
|
|
115
|
+
export function runGetPracticeDraft(ctx, args) {
|
|
116
|
+
const sample = requireSample(ctx, args.sample_id);
|
|
117
|
+
return readTextPage({
|
|
118
|
+
sample_id: sample.id,
|
|
119
|
+
work_name: workNameOf(currentAuthor(ctx), sample.work_id),
|
|
120
|
+
excerpt_label: sample.excerpt_label,
|
|
121
|
+
}, sample.draft, args);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 用户自由练习稿只读分页(get_free_draft(piece_id, offset?, max_chars?))。
|
|
125
|
+
* 未写稿 → char_count=0、text=""(不报错);**只读**——工具面不存在写稿工具。
|
|
126
|
+
* @throws Error 篇不存在;offset 非非负整数
|
|
127
|
+
*/
|
|
128
|
+
export function runGetFreeDraft(ctx, args) {
|
|
129
|
+
const piece = requirePiece(ctx, args.piece_id);
|
|
130
|
+
return readTextPage({ piece_id: piece.id }, piece.draft, args);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 作家风格说明只读分页(get_style_notes(offset?, max_chars?))——自由练习点评的**唯一依据**
|
|
134
|
+
* (仿写点评可按需参考,不强制引用)。
|
|
135
|
+
* 内容 = 用户覆盖 `??` 预置;整份说明按长文分页(不整份灌入上下文)。
|
|
136
|
+
* @throws Error 工作区未关联预置作家;offset 非非负整数
|
|
137
|
+
*/
|
|
138
|
+
export function runGetStyleNotes(ctx, args) {
|
|
139
|
+
const author = requireAuthor(ctx);
|
|
140
|
+
const notes = effectiveStyleNotes(ctx, author);
|
|
141
|
+
return readTextPage({ author_id: author.id, author_name: author.name, customized: notes.customized }, notes.content, args);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 逐条子串校验:保留能在 `source` 里逐字找到的引用,其余(含 trim 后为空的串)丢弃并累加计数
|
|
145
|
+
* (docs/design/60-practice.md §6 硬规则①:不匹配直接丢弃,不阻断卡片、不重试——空引用走**同一条**
|
|
146
|
+
* 丢弃路径,不另立分支语义)。
|
|
147
|
+
*/
|
|
148
|
+
function keepVerbatimQuotes(quotes, source, dropped) {
|
|
149
|
+
const kept = [];
|
|
150
|
+
for (const quote of quotes) {
|
|
151
|
+
if (quote.trim() !== "" && source.includes(quote))
|
|
152
|
+
kept.push(quote);
|
|
153
|
+
else
|
|
154
|
+
dropped.count++;
|
|
155
|
+
}
|
|
156
|
+
return kept;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 维度上限守卫(两个提交工具共用):超 `MAX_FEEDBACK_DIMENSIONS` **抛错即失败**(不编码进 content)。
|
|
160
|
+
* @throws Error 维度超上限
|
|
161
|
+
*/
|
|
162
|
+
function assertDimensionLimit(count) {
|
|
163
|
+
if (count > MAX_FEEDBACK_DIMENSIONS) {
|
|
164
|
+
throw new Error(`dimensions 最多 ${MAX_FEEDBACK_DIMENSIONS} 个维度(收到 ${count} 个)——请合并或删减维度后重试`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 维度映射(两个提交工具共用):`evidence` = 该次练习的依据文本(仿写 = 范文、自由练习 = 风格说明),
|
|
169
|
+
* `draft` = 用户稿;两处引用各自逐条校验(差别只在 evidence 来源)。
|
|
170
|
+
*/
|
|
171
|
+
function toDimensions(dimensions, evidence, draft, dropped) {
|
|
172
|
+
return dimensions.map((dimension) => ({
|
|
173
|
+
name: dimension.name,
|
|
174
|
+
sample_trait: dimension.sample_trait,
|
|
175
|
+
draft_trait: dimension.draft_trait,
|
|
176
|
+
sample_quotes: keepVerbatimQuotes(dimension.sample_quotes ?? [], evidence, dropped),
|
|
177
|
+
draft_quotes: keepVerbatimQuotes(dimension.draft_quotes ?? [], draft, dropped),
|
|
178
|
+
advice: dimension.advice,
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 提交仿写点评卡(submit_style_feedback(sample_id, dimensions[], summary?))——写 `practice_samples.feedback`。
|
|
183
|
+
*
|
|
184
|
+
* 口径:引用校验源 = 范文 `text`(`sample_quotes`)与仿写稿 `draft`(`draft_quotes`);
|
|
185
|
+
* **全部引用非法时仍成功写卡**(卡片可以没有引用——诊断不依赖引用数)。只写 `feedback`:
|
|
186
|
+
* 范文与用户稿一字不动。
|
|
187
|
+
* @throws Error 维度超上限;样本不存在
|
|
188
|
+
*/
|
|
189
|
+
export function runSubmitStyleFeedback(ctx, args) {
|
|
190
|
+
assertDimensionLimit(args.dimensions.length);
|
|
191
|
+
const sample = requireSample(ctx, args.sample_id);
|
|
192
|
+
const dropped = { count: 0 };
|
|
193
|
+
const dimensions = toDimensions(args.dimensions, sample.text, sample.draft, dropped);
|
|
194
|
+
const card = args.summary === undefined ? { dimensions } : { dimensions, summary: args.summary };
|
|
195
|
+
if (updatePracticeFeedback(ctx.db, sample.id, card) === null) {
|
|
196
|
+
throw new Error(`练习样本不存在(sample_id=${args.sample_id})`);
|
|
197
|
+
}
|
|
198
|
+
return { saved: true, dimension_count: dimensions.length, dropped_quotes: dropped.count };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 提交自由练习点评卡(submit_free_feedback(piece_id, dimensions[], summary?))——写
|
|
202
|
+
* `practice_free_pieces.feedback`(卡形与仿写**同形**,差别只在引用校验源)。
|
|
203
|
+
*
|
|
204
|
+
* 口径:引用校验源 = 风格说明(用户覆盖 `??` 预置)与自由稿 `draft`;其余同仿写。
|
|
205
|
+
* @throws Error 维度超上限;篇不存在;工作区未关联预置作家(风格说明的来源不存在)
|
|
206
|
+
*/
|
|
207
|
+
export function runSubmitFreeFeedback(ctx, args) {
|
|
208
|
+
assertDimensionLimit(args.dimensions.length);
|
|
209
|
+
const piece = requirePiece(ctx, args.piece_id);
|
|
210
|
+
const notes = effectiveStyleNotes(ctx, requireAuthor(ctx)).content;
|
|
211
|
+
const dropped = { count: 0 };
|
|
212
|
+
const dimensions = toDimensions(args.dimensions, notes, piece.draft, dropped);
|
|
213
|
+
const card = args.summary === undefined ? { dimensions } : { dimensions, summary: args.summary };
|
|
214
|
+
if (updatePracticePieceFeedback(ctx.db, piece.id, card) === null) {
|
|
215
|
+
throw new Error(`自由练习不存在(piece_id=${args.piece_id})`);
|
|
216
|
+
}
|
|
217
|
+
return { saved: true, dimension_count: dimensions.length, dropped_quotes: dropped.count };
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 出题(submit_practice_topic(piece_id, topic, brief?))——写 `practice_free_pieces.topic` / `brief`
|
|
221
|
+
* (`brief` 省略 = 不改)。
|
|
222
|
+
*
|
|
223
|
+
* **题目非空 → 抛错拒写**(防覆盖已出的题 / 用户已开始的练习;不满意的题 = 删掉该篇重建,
|
|
224
|
+
* 不做覆盖式换题);空白题目同样拒写(落库会在界面留一条看不见的「已出题」记录)。
|
|
225
|
+
* @throws Error 该篇已有题目;题目空白;篇不存在
|
|
226
|
+
*/
|
|
227
|
+
export function runSubmitPracticeTopic(ctx, args) {
|
|
228
|
+
const piece = requirePiece(ctx, args.piece_id);
|
|
229
|
+
if (piece.topic.trim() !== "") {
|
|
230
|
+
throw new Error(`该篇已有题目(piece_id=${piece.id})——出题不覆盖已出的题;请新建一篇后重试`);
|
|
231
|
+
}
|
|
232
|
+
const topic = args.topic.trim();
|
|
233
|
+
if (topic === "")
|
|
234
|
+
throw new Error("题目不能为空");
|
|
235
|
+
const patch = args.brief === undefined ? { topic } : { topic, brief: args.brief };
|
|
236
|
+
if (updatePracticePieceTopic(ctx.db, piece.id, patch) === null) {
|
|
237
|
+
throw new Error(`自由练习不存在(piece_id=${args.piece_id})`);
|
|
238
|
+
}
|
|
239
|
+
return { saved: true, piece_id: piece.id };
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=practice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"practice.js","sourceRoot":"","sources":["../../src/query/practice.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Static, type TSchema } from "@earendil-works/pi-ai";
|
|
2
|
-
import type
|
|
2
|
+
import { type ToolPermission, type ToolScope } from "@whispering233/ai-editor-shared";
|
|
3
3
|
import type { ToolContext } from "./context.js";
|
|
4
4
|
/**
|
|
5
5
|
* 工具定义(parameters 的推断类型约束 run 的 args 入参)。
|
|
@@ -17,6 +17,8 @@ export interface ToolDefinition<TSchema_ extends TSchema = TSchema> {
|
|
|
17
17
|
parameters: TSchema_;
|
|
18
18
|
/** 权限级别(TOOL_PERMISSION.AUTO / PROPOSAL) */
|
|
19
19
|
permission: ToolPermission;
|
|
20
|
+
/** 工作区作用域(novel / practice / common):装配层按当前工作区 `kind` 过滤,决定该工具是否注册 */
|
|
21
|
+
scope: ToolScope;
|
|
20
22
|
/**
|
|
21
23
|
* 执行函数:注入 ToolContext(db/outlineDir/projectId),返回可序列化结果或抛错。
|
|
22
24
|
* signal:可选取消通道(「长工具执行中检查 signal」)——分析类长任务在 executor
|
|
@@ -34,7 +36,8 @@ export declare function validateToolArgs(def: ToolDefinition, args: unknown): un
|
|
|
34
36
|
/**
|
|
35
37
|
* 注册单个工具:重复注册同名工具抛错——防后续切片扩展时撞名,
|
|
36
38
|
* 保证「注册表 = 工具目录唯一事实来源」。
|
|
37
|
-
* 防呆:name
|
|
39
|
+
* 防呆:name 必须为非空字符串(拼错名不应静默注册新工具);scope 必须是合法作用域
|
|
40
|
+
*(漏标 / 拼错会让「按 kind 过滤」行为不可预期,故在此拦下)。
|
|
38
41
|
*/
|
|
39
42
|
export declare function registerTool(def: ToolDefinition): void;
|
|
40
43
|
/** 批量注册(各工具模块组装定义数组后统一挂载;内部逐个调 registerTool 保证判重) */
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAaA,OAAO,EAA0C,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC1G,OAAO,EAAe,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACnG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,SAAS,OAAO,GAAG,OAAO;IACjE,qDAAqD;IACpD,IAAI,EAAE,MAAM,CAAC;IACd,oCAAoC;IACnC,WAAW,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACxD,UAAU,EAAE,QAAQ,CAAC;IACtB,4CAA4C;IAC3C,UAAU,EAAE,cAAc,CAAC;IAC5B,sEAAsE;IACrE,KAAK,EAAE,SAAS,CAAC;IAClB;;;;MAIE;IACD,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;CAC9E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAW5E;AAKD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAWtD;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAEnE;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEhE;AAED,4CAA4C;AAC5C,wBAAgB,SAAS,IAAI,cAAc,EAAE,CAE5C;AAED,0BAA0B;AAC1B,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
|
package/dist/registry.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// 工具注册表:统一工具定义结构 + 注册/查询 API + 参数校验入口
|
|
2
2
|
// registerTool 挂载新工具;executor 通过 getTool(name) 按名调度。
|
|
3
3
|
//
|
|
4
|
-
// 定义结构 { name, description, parameters, permission, run }:
|
|
4
|
+
// 定义结构 { name, description, parameters, permission, scope, run }:
|
|
5
5
|
// - parameters:TypeBox 参数 schema(运行时即 JSON Schema;执行前 preflight 校验,
|
|
6
6
|
// 批量 tool_call 先全部校验再执行);校验失败即抛错(executor 统一转结构化 tool_result 喂回 LLM)
|
|
7
7
|
// - permission:TOOL_PERMISSION.AUTO(自动)/ PROPOSAL(提案确认)两级(「工具分级」)
|
|
8
|
+
// - scope:ToolScope(工作区作用域)——装配层按当前工作区 `kind` 过滤(shared `isToolScopeRegistered`);
|
|
9
|
+
// **必填**:漏标 = 该工具的工作区归属未定,工具集按 kind 收窄即失效
|
|
8
10
|
// - run(ctx, args):同步执行,返回 JSON 可序列化结果;抛错即失败(不把失败编码进正常 content)
|
|
9
11
|
//
|
|
10
12
|
// 注册语义:重复注册同名工具抛错(防后续切片扩展时撞名;注册表是唯一事实来源)。
|
|
11
13
|
import { validateToolArguments } from "@earendil-works/pi-ai";
|
|
14
|
+
import { TOOL_SCOPES } from "@whispering233/ai-editor-shared";
|
|
12
15
|
/**
|
|
13
16
|
* 参数校验(执行前 preflight;校验语义与 pi agent 循环一致——pi-ai 的
|
|
14
17
|
* validateToolArguments:先做原始类型 coerce,再按 schema 检查,失败抛错)。
|
|
@@ -29,12 +32,16 @@ const registry = new Map();
|
|
|
29
32
|
/**
|
|
30
33
|
* 注册单个工具:重复注册同名工具抛错——防后续切片扩展时撞名,
|
|
31
34
|
* 保证「注册表 = 工具目录唯一事实来源」。
|
|
32
|
-
* 防呆:name
|
|
35
|
+
* 防呆:name 必须为非空字符串(拼错名不应静默注册新工具);scope 必须是合法作用域
|
|
36
|
+
*(漏标 / 拼错会让「按 kind 过滤」行为不可预期,故在此拦下)。
|
|
33
37
|
*/
|
|
34
38
|
export function registerTool(def) {
|
|
35
39
|
if (typeof def.name !== "string" || def.name.trim() === "") {
|
|
36
40
|
throw new Error("registerTool: 工具名必须为非空字符串");
|
|
37
41
|
}
|
|
42
|
+
if (!TOOL_SCOPES.includes(def.scope)) {
|
|
43
|
+
throw new Error(`registerTool: 工具 ${def.name} 的 scope 非法(${String(def.scope)})——取值见 shared TOOL_SCOPES`);
|
|
44
|
+
}
|
|
38
45
|
if (registry.has(def.name)) {
|
|
39
46
|
throw new Error(`registerTool: 工具 ${def.name} 已注册(重复注册禁止)`);
|
|
40
47
|
}
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,qDAAqD;AACrD,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,qDAAqD;AACrD,EAAE;AACF,kEAAkE;AAClE,oEAAoE;AACpE,uEAAuE;AACvE,kEAAkE;AAClE,kFAAkF;AAClF,4CAA4C;AAC5C,gEAAgE;AAChE,EAAE;AACF,0CAA0C;AAE1C,OAAO,EAAE,qBAAqB,EAA8C,MAAM,uBAAuB,CAAC;AAC1G,OAAO,EAAE,WAAW,EAAuC,MAAM,iCAAiC,CAAC;AA6BnG;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB,EAAE,IAAa;IACjE,OAAO,qBAAqB,CAC1B,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,EAC5E;QACE,IAAI,EAAE,UAAU;QAChB,EAAE,EAAE,YAAY,GAAG,CAAC,IAAI,EAAE;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,sEAAsE;QACtE,SAAS,EAAE,CAAC,IAAI,IAAI,EAAE,CAAe;KACtC,CACF,CAAC;AACJ,CAAC;AAED,gCAAgC;AAChC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAmB;IAC9C,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAE,WAAkC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,CAAC,IAAI,eAAe,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC3G,CAAC;IACD,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,CAAC,IAAI,cAAc,CAAC,CAAC;IAC9D,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,aAAa,CAAC,IAA+B;IAC3D,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,YAAY,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,SAAS;IACvB,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,SAAS;IACvB,OAAO,QAAQ,CAAC,IAAI,CAAC;AACvB,CAAC"}
|
package/dist/schemas/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./outline.js";
|
|
|
10
10
|
export * from "./delta.js";
|
|
11
11
|
export * from "./reference.js";
|
|
12
12
|
export * from "./manuscript.js";
|
|
13
|
+
export * from "./practice.js";
|
|
13
14
|
export * from "./analysis.js";
|
|
14
15
|
export * from "./proposal.js";
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,2BAA2B,kBAAmD,CAAC;AAE5F,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,2BAA2B,kBAAmD,CAAC;AAE5F,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
package/dist/schemas/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./outline.js";
|
|
|
14
14
|
export * from "./delta.js";
|
|
15
15
|
export * from "./reference.js";
|
|
16
16
|
export * from "./manuscript.js";
|
|
17
|
+
export * from "./practice.js";
|
|
17
18
|
export * from "./analysis.js";
|
|
18
19
|
export * from "./proposal.js";
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,EAAE;AACF,iEAAiE;AACjE,gFAAgF;AAEhF,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC,CAAC;AAE5F,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,EAAE;AACF,iEAAiE;AACjE,gFAAgF;AAEhF,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC,CAAC;AAE5F,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { Type, type Static } from "@earendil-works/pi-ai";
|
|
|
2
2
|
/**
|
|
3
3
|
* get_chapter_text 入参:node_id(**必须是章节点**)+ offset(起始字符下标,缺省 0)
|
|
4
4
|
* + max_chars(本次最多返回字符数,缺省与上限见 `query/manuscript.ts` 的
|
|
5
|
-
* `
|
|
5
|
+
* `DEFAULT_TEXT_PAGE_CHARS` / `MAX_TEXT_PAGE_CHARS`——与练习工具共用同一对,实现层 clamp)。
|
|
6
6
|
*/
|
|
7
7
|
export declare const getChapterTextArgsSchema: Type.TObject<{
|
|
8
8
|
node_id: Type.TString;
|
|
@@ -6,7 +6,7 @@ import { Type } from "@earendil-works/pi-ai";
|
|
|
6
6
|
/**
|
|
7
7
|
* get_chapter_text 入参:node_id(**必须是章节点**)+ offset(起始字符下标,缺省 0)
|
|
8
8
|
* + max_chars(本次最多返回字符数,缺省与上限见 `query/manuscript.ts` 的
|
|
9
|
-
* `
|
|
9
|
+
* `DEFAULT_TEXT_PAGE_CHARS` / `MAX_TEXT_PAGE_CHARS`——与练习工具共用同一对,实现层 clamp)。
|
|
10
10
|
*/
|
|
11
11
|
export const getChapterTextArgsSchema = Type.Object({
|
|
12
12
|
node_id: Type.String(),
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Type, type Static } from "@earendil-works/pi-ai";
|
|
2
|
+
/**
|
|
3
|
+
* 仿写侧文本分页入参(get_practice_sample 与 get_practice_draft **同形**:都读样本里的一段文本):
|
|
4
|
+
* sample_id 必填(ps-*)——范文与稿件都挂在样本行上。
|
|
5
|
+
*/
|
|
6
|
+
export declare const practiceTextPageArgsSchema: Type.TObject<{
|
|
7
|
+
offset: Type.TOptional<Type.TInteger>;
|
|
8
|
+
max_chars: Type.TOptional<Type.TInteger>;
|
|
9
|
+
sample_id: Type.TString;
|
|
10
|
+
}>;
|
|
11
|
+
export type PracticeTextPageArgs = Static<typeof practiceTextPageArgsSchema>;
|
|
12
|
+
/**
|
|
13
|
+
* 自由练习稿分页入参(get_free_draft):piece_id 必填(pf-*)。
|
|
14
|
+
* 刻意不做「一个工具 + 多态 id」——两张表的语义与引用校验源不同(见 docs/api/tool-calling.md)。
|
|
15
|
+
*/
|
|
16
|
+
export declare const freeDraftPageArgsSchema: Type.TObject<{
|
|
17
|
+
offset: Type.TOptional<Type.TInteger>;
|
|
18
|
+
max_chars: Type.TOptional<Type.TInteger>;
|
|
19
|
+
piece_id: Type.TString;
|
|
20
|
+
}>;
|
|
21
|
+
export type FreeDraftPageArgs = Static<typeof freeDraftPageArgsSchema>;
|
|
22
|
+
/** 风格说明分页入参(get_style_notes):无 id——风格说明按当前工作区的作家取(一作家一份) */
|
|
23
|
+
export declare const styleNotesPageArgsSchema: Type.TObject<{
|
|
24
|
+
offset: Type.TOptional<Type.TInteger>;
|
|
25
|
+
max_chars: Type.TOptional<Type.TInteger>;
|
|
26
|
+
}>;
|
|
27
|
+
export type StyleNotesPageArgs = Static<typeof styleNotesPageArgsSchema>;
|
|
28
|
+
/**
|
|
29
|
+
* 维度(点评卡的一项):维度名 + 依据/稿件特征 + 证据引用 + 可执行建议——字段与
|
|
30
|
+
* shared `practiceFeedbackSchema` 的 `dimensions[]` 同形(snake_case,勿转换)。
|
|
31
|
+
* 引用数组**可省**(省 = 无证据);落库卡恒有数组(实现层补 `[]`,读取侧形状完整)。
|
|
32
|
+
*/
|
|
33
|
+
declare const feedbackDimensionSchema: Type.TObject<{
|
|
34
|
+
name: Type.TString;
|
|
35
|
+
sample_trait: Type.TString;
|
|
36
|
+
draft_trait: Type.TString;
|
|
37
|
+
sample_quotes: Type.TOptional<Type.TArray<Type.TString>>;
|
|
38
|
+
draft_quotes: Type.TOptional<Type.TArray<Type.TString>>;
|
|
39
|
+
advice: Type.TString;
|
|
40
|
+
}>;
|
|
41
|
+
/** 维度入参类型(两个提交工具共用同一份维度 schema——卡内层同形,差别只在引用校验源) */
|
|
42
|
+
export type FeedbackDimensionArgs = Static<typeof feedbackDimensionSchema>;
|
|
43
|
+
/**
|
|
44
|
+
* submit_style_feedback 入参:sample_id + dimensions(**上限 = shared `MAX_FEEDBACK_DIMENSIONS`**,
|
|
45
|
+
* 实现层校验——工具描述由该常量插值生成)+ summary(可选一句话总评)。
|
|
46
|
+
* **没有「示范」字段**:卡片只做诊断,没有位置放成段文字(docs/design/60-practice.md §6)。
|
|
47
|
+
*/
|
|
48
|
+
export declare const submitStyleFeedbackArgsSchema: Type.TObject<{
|
|
49
|
+
sample_id: Type.TString;
|
|
50
|
+
dimensions: Type.TArray<Type.TObject<{
|
|
51
|
+
name: Type.TString;
|
|
52
|
+
sample_trait: Type.TString;
|
|
53
|
+
draft_trait: Type.TString;
|
|
54
|
+
sample_quotes: Type.TOptional<Type.TArray<Type.TString>>;
|
|
55
|
+
draft_quotes: Type.TOptional<Type.TArray<Type.TString>>;
|
|
56
|
+
advice: Type.TString;
|
|
57
|
+
}>>;
|
|
58
|
+
summary: Type.TOptional<Type.TString>;
|
|
59
|
+
}>;
|
|
60
|
+
export type SubmitStyleFeedbackArgs = Static<typeof submitStyleFeedbackArgsSchema>;
|
|
61
|
+
/** submit_free_feedback 入参:与仿写侧**同形**,只把 sample_id 换成 piece_id(卡内层与校验口径共用) */
|
|
62
|
+
export declare const submitFreeFeedbackArgsSchema: Type.TObject<{
|
|
63
|
+
piece_id: Type.TString;
|
|
64
|
+
dimensions: Type.TArray<Type.TObject<{
|
|
65
|
+
name: Type.TString;
|
|
66
|
+
sample_trait: Type.TString;
|
|
67
|
+
draft_trait: Type.TString;
|
|
68
|
+
sample_quotes: Type.TOptional<Type.TArray<Type.TString>>;
|
|
69
|
+
draft_quotes: Type.TOptional<Type.TArray<Type.TString>>;
|
|
70
|
+
advice: Type.TString;
|
|
71
|
+
}>>;
|
|
72
|
+
summary: Type.TOptional<Type.TString>;
|
|
73
|
+
}>;
|
|
74
|
+
export type SubmitFreeFeedbackArgs = Static<typeof submitFreeFeedbackArgsSchema>;
|
|
75
|
+
/**
|
|
76
|
+
* submit_practice_topic 入参:piece_id + topic + brief?(写 `practice_free_pieces.topic` / `brief`)。
|
|
77
|
+
* 两个长度上限由 shared 常量作 schema 约束(超出即参数校验失败)——描述里的数字同样由常量插值。
|
|
78
|
+
* **题目非空则拒写**(实现层守卫,防覆盖已出的题 / 用户已开始的练习)。
|
|
79
|
+
*/
|
|
80
|
+
export declare const submitPracticeTopicArgsSchema: Type.TObject<{
|
|
81
|
+
piece_id: Type.TString;
|
|
82
|
+
topic: Type.TString;
|
|
83
|
+
brief: Type.TOptional<Type.TString>;
|
|
84
|
+
}>;
|
|
85
|
+
export type SubmitPracticeTopicArgs = Static<typeof submitPracticeTopicArgsSchema>;
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=practice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"practice.d.ts","sourceRoot":"","sources":["../../src/schemas/practice.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAY1D;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;EAMtC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;EAMnC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAEvE,4DAA4D;AAC5D,eAAO,MAAM,wBAAwB;;;EAAsE,CAAC;AAE5G,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEzE;;;;GAIG;AACH,QAAA,MAAM,uBAAuB;;;;;;;EAU5B,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;EAOzC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEnF,8EAA8E;AAC9E,eAAO,MAAM,4BAA4B;;;;;;;;;;;EAOxC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAEjF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B;;;;EAOzC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
|