@yfwu2020/dsh-selection-explain 0.11.1 → 0.12.0

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.
@@ -0,0 +1,31 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { Config } from './config.js';
3
+ import type { ChatTurn, ContextState, MaterialRef } from '../chat-context.js';
4
+ type Route = {
5
+ provider: string;
6
+ model: string;
7
+ };
8
+ export declare function resolveChatCapacity(ctx: Context, config: Config, route: Route): Promise<{
9
+ budget: number;
10
+ price: (t: ChatTurn) => number;
11
+ attachmentPrice: (a: MaterialRef) => number;
12
+ modelWindow: number | null;
13
+ }>;
14
+ export declare function fitBackground(background: ContextState['background'], tokens: number): ContextState['background'];
15
+ export declare function planChatContext(ctx: Context, route: Route, capacity: Awaited<ReturnType<typeof resolveChatCapacity>>, input: {
16
+ state: ContextState;
17
+ history: ChatTurn[];
18
+ fixedTokens: number;
19
+ signal: AbortSignal;
20
+ effort: string;
21
+ }): Promise<{
22
+ state: ContextState;
23
+ history: ChatTurn[];
24
+ estimatedTokens: number;
25
+ compacted: boolean;
26
+ }>;
27
+ /** Check the actual assembled input, including images and nested tool results. */
28
+ export declare function estimateRequestTokens(messages: readonly {
29
+ content?: unknown;
30
+ }[], capacity: Awaited<ReturnType<typeof resolveChatCapacity>>, tools: unknown): number;
31
+ export {};
@@ -0,0 +1,16 @@
1
+ import type { ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm';
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { ChatMaterial, MaterialRef } from '../chat-context.js';
4
+ export declare function readMaterialCatalog(raw: unknown): ChatMaterial[];
5
+ export declare const MATERIAL_TOOL: ToolSchema;
6
+ export declare function materialIndex(catalog: ChatMaterial[], through: number, all?: boolean): string;
7
+ export declare function restoreMaterial(ctx: Context, catalog: ChatMaterial[], parsed: unknown, options: {
8
+ through: number;
9
+ key: string;
10
+ maxChars: number;
11
+ readMessage: (key: string, index: number) => Promise<string | null>;
12
+ }): Promise<{
13
+ content: ContentBlock[];
14
+ isError: boolean;
15
+ restored?: MaterialRef;
16
+ }>;
@@ -12,8 +12,9 @@ export declare const Config: z<Schemastery.ObjectS<{
12
12
  translationReasoningEffort: z<string, string>;
13
13
  /**
14
14
  * 小窗对话历史(A′):把整段对话落到插件自己的目录,重划同一个词直接回放。
15
- * 0 = 关闭历史。只留最近 N 个「划词条目」(每个条目内含它自己的全部追问轮次)。
15
+ * 0 = 停止保存新历史,不清空已有记录。所有条目(含已升格)统一保留最近 N 个。
16
16
  */
17
+ chatContextTokens: z<number, number>;
17
18
  historyMaxEntries: z<number, number>;
18
19
  /** 采样温度。**默认 -1 = 不传**(交给供应商默认,与主会话一致)。0~2 之间才真的传。 */
19
20
  temperature: z<number, number>;
@@ -82,8 +83,9 @@ export declare const Config: z<Schemastery.ObjectS<{
82
83
  translationReasoningEffort: z<string, string>;
83
84
  /**
84
85
  * 小窗对话历史(A′):把整段对话落到插件自己的目录,重划同一个词直接回放。
85
- * 0 = 关闭历史。只留最近 N 个「划词条目」(每个条目内含它自己的全部追问轮次)。
86
+ * 0 = 停止保存新历史,不清空已有记录。所有条目(含已升格)统一保留最近 N 个。
86
87
  */
88
+ chatContextTokens: z<number, number>;
87
89
  historyMaxEntries: z<number, number>;
88
90
  /** 采样温度。**默认 -1 = 不传**(交给供应商默认,与主会话一致)。0~2 之间才真的传。 */
89
91
  temperature: z<number, number>;
@@ -1,36 +1,9 @@
1
- /**
2
- * 小窗历史里的附件:**持久化的引用**怎么进模型请求,以及图片预算怎么降级。
3
- *
4
- * 为什么要单独一个模块:这段逻辑全是**纯函数**(形状收敛 / 预算规划 / 内容块重建),
5
- * 它决定了"第 N 轮还能不能看见第 1 轮那张图",是最容易写错、又最难人工发现的地方 ——
6
- * 单独放出来才测得动(见 tests/unit/history-attachments.test.mjs)。
7
- *
8
- * ── 三条设计约束 ──
9
- *
10
- * ① **只回传引用,不回传字节**。历史里存的是 `/api/attach` 给的持久引用
11
- * (`{attachmentId:'sha256:…', name, bytes, mediaType, width, height}`),
12
- * 和主会话往会话事件里写的是同一套形状。字节只在附件服务里存一份。
13
- *
14
- * ② **图片有预算,超了退化成一行文字**。小窗直接调 `ctx.llm.stream()`,不走 agent loop,
15
- * 所以吃不到 `dsh-compaction-image-offload`(它挂在 `agent/request-error` 这个
16
- * agent-loop 才派发的 waterfall 上)。历史里的图要是不管,每多问一轮就重发一遍,
17
- * token 会一路涨上去 —— 所以这里自己卡预算:**从最近往前数**,新图优先,
18
- * 超出的老图换成一行为模型可读的说明(照 dsh-llm 那句 offloaded 措辞的风格写,
19
- * 模型在别处见过同款说法)。
20
- *
21
- * ③ **文件不占图片预算**。文件本来就不进请求体:dsh-llm 会把它投影成
22
- * "名字 + 字节数 + 只读路径"一行文字,模型需要时用文件工具按路径去读。
23
- * 这行的成本很低,而且"能读回来"正是持久化它的全部意义 —— 所以文件只要限个条数就行。
1
+ /** Durable attachment validation and stable block reconstruction.
2
+ * The context planner owns the token budget and compaction boundary. Every
3
+ * image/file in its selected segment stays unchanged until that boundary moves.
4
+ * Binary data lives in the host attachment library; history stores only refs.
24
5
  */
25
6
  import type { ContentBlock } from '@deepseek-ai/dsh-llm';
26
- /** 历史里最多回带几张**图片**(当前这一轮的附件也算在这个额度里)。 */
27
- export declare const HISTORY_IMAGE_MAX = 2;
28
- /** 历史回带图片的总字节上限(规范化后的字节,取自引用里的 bytes)。 */
29
- export declare const HISTORY_IMAGE_BYTES: number;
30
- /** 历史里最多回带几个**文件**引用(每个只投影成一行路径文字)。 */
31
- export declare const HISTORY_FILE_MAX = 8;
32
- /** 单轮最多认几件附件(客户端被改坏、或有人手写请求时的护栏)。 */
33
- export declare const HISTORY_ATTACHMENTS_PER_TURN_MAX = 10;
34
7
  /** 附件引用里我们**只**认这几个字段 —— 其余一律丢掉。 */
35
8
  export interface AttachmentRefLike {
36
9
  attachmentId: string;
@@ -85,15 +58,9 @@ export declare function omittedImageText(ref: AttachmentRefLike): string;
85
58
  /** 文件引用被条数上限挤掉时的那行文字。 */
86
59
  export declare function omittedFileText(ref: AttachmentRefLike): string;
87
60
  /**
88
- * 把客户端传来的历史轮次重建成**每轮一份内容块**,并按预算决定图片是回带还是降级。
89
- *
90
- * @param turns - 客户端给的历史(`[{role, text, attachments?}]`),顺序 = 从旧到新。
91
- * @param current - **当前这一轮**的附件;它已经占了额度(用户刚附上的图必须发出去,
92
- * 但总张数仍要封顶,否则"历史 2 张 + 本轮 1 张"会逐轮往上叠)。
93
- * @returns 与 `turns` 等长(下标对齐)的内容块,外加几个计数。
94
- *
95
- * 返回 `[]` 表示"这一轮没有可进模型的内容"(既没文字也没附件)—— 调用方据此跳过它,
96
- * 与从前的 `if (!text) continue` 等价。
61
+ * Rebuild every selected historical attachment in request order.
62
+ * Capacity planning happens before this function; current attachments are
63
+ * counted for diagnostics and never cause older occurrences to be rewritten.
97
64
  */
98
65
  export declare function historyContentBlocks(turns: readonly unknown[], current?: readonly HistoryAttachment[]): HistoryContentPlan;
99
66
  /**
@@ -10,4 +10,5 @@ export declare function createHistoryStore(ctx: Context, config: Config, models:
10
10
  handleHistory: (req: IncomingMessage, res: ServerResponse) => void;
11
11
  handlePanelSize: (req: IncomingMessage, res: ServerResponse) => void;
12
12
  handlePillPos: (req: IncomingMessage, res: ServerResponse) => void;
13
+ readMessage: (key: string, index: number) => Promise<string | null>;
13
14
  };
@@ -34,6 +34,10 @@ export interface AnalyzeBody {
34
34
  question?: unknown;
35
35
  /** 追问模式下的历史轮次:[{ role: 'user' | 'assistant', text }]。 */
36
36
  history?: unknown;
37
+ historyStart?: unknown;
38
+ contextState?: unknown;
39
+ materials?: unknown;
40
+ initialExplanation?: unknown;
37
41
  /**
38
42
  * 这一轮追问带的附件:[{ kind: 'image' | 'file', ref }],ref 是 /api/attach 存好之后的持久引用。
39
43
  * 图片 → ImageBlock(模型直接看);文件 → FileBlock(dsh-llm 投影成"名字 + 字节数 + 只读路径"一行)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yfwu2020/dsh-selection-explain",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "DSH 划词解读:选中文字浮出按钮,点击弹窗流式显示专业中英翻译 + 该文字在当前上下文中的含义详解",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",