@deepseek-ai/dsh-session-log-deepseek 0.1.7-alpha.2 → 0.1.7-rc.2
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.i18n.yaml +45 -5
- package/README.md +8 -4
- package/README.zh.md +8 -4
- package/lib/index.js +53 -9
- package/lib/types/index.d.ts +6 -0
- package/lib/types/index.js +51 -9
- package/package.json +17 -16
package/README.i18n.yaml
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
|
-
# Bilingual-pair consistency record (docs/i18n/README.md):
|
|
2
|
-
#
|
|
3
|
-
#
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/session/session-log-deepseek/README.md
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/:
|
|
6
|
+
en: 29b2294131f1a592
|
|
7
|
+
zh: 6d7f4fa9e1f46943
|
|
8
|
+
/deepseek-ai-dsh-session-log-deepseek:
|
|
9
|
+
en: 6d991785cbfd70ae
|
|
10
|
+
zh: 0918e2462821cde4
|
|
11
|
+
/deepseek-ai-dsh-session-log-deepseek/summary:
|
|
12
|
+
en: 19c182dbd7d8e064
|
|
13
|
+
zh: 51c043d6ea52db0d
|
|
14
|
+
/deepseek-ai-dsh-session-log-deepseek/table-of-contents:
|
|
15
|
+
en: e2aa29e41bc101c4
|
|
16
|
+
zh: 2ef629f2a444bc13
|
|
17
|
+
/deepseek-ai-dsh-session-log-deepseek/configuration:
|
|
18
|
+
en: 1e8712ec4f0fefcc
|
|
19
|
+
zh: 5ffe754b2cefdd21
|
|
20
|
+
/deepseek-ai-dsh-session-log-deepseek/request-field:
|
|
21
|
+
en: 197814e2c48d05b5
|
|
22
|
+
zh: f4616fb23a3c7a32
|
|
23
|
+
/deepseek-ai-dsh-session-log-deepseek/acceptance-and-retry:
|
|
24
|
+
en: 3688592c474b3e95
|
|
25
|
+
zh: 5e83afeb9aa9e9e3
|
|
26
|
+
/deepseek-ai-dsh-session-log-deepseek/model-experience:
|
|
27
|
+
en: 215e7ba838619b7b
|
|
28
|
+
zh: a311da3843709f90
|
|
29
|
+
/deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata:
|
|
30
|
+
en: 468b9eaee350f6e2
|
|
31
|
+
zh: c192ad6228e7044d
|
|
32
|
+
/deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata/what-the-model-sees:
|
|
33
|
+
en: 89fef23a54a0f9d6
|
|
34
|
+
zh: fd17806f1df6083e
|
|
35
|
+
/deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata/token-effect:
|
|
36
|
+
en: 6a1bb7bbc7db7f06
|
|
37
|
+
zh: 94ddaa773b7538f7
|
|
38
|
+
/deepseek-ai-dsh-session-log-deepseek/model-experience/session-log-metadata/kv-cache-effect:
|
|
39
|
+
en: dcf00aa1af688f09
|
|
40
|
+
zh: df5851d66c3a6b4f
|
|
41
|
+
/deepseek-ai-dsh-session-log-deepseek/known-limitations-and-deferred-work:
|
|
42
|
+
en: 249ef8dbc237a0c2
|
|
43
|
+
zh: c169d64ce9ab984c
|
|
44
|
+
/deepseek-ai-dsh-session-log-deepseek/known-limitations-and-deferred-work/dev-note:
|
|
45
|
+
en: e67f633dc6fe7773
|
|
46
|
+
zh: e88aeb617ffb74ea
|
package/README.md
CHANGED
|
@@ -28,18 +28,21 @@ Incremental canonical session-log upload for official DeepSeek LLM API requests.
|
|
|
28
28
|
| Key | Default | Meaning |
|
|
29
29
|
|---|---:|---|
|
|
30
30
|
| `enabled` | `true` | Register the `dsh_session_log` contribution. Set it to `false` to stop Session-log upload. |
|
|
31
|
+
| `maxBytes` | 8 MiB | Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries. |
|
|
31
32
|
|
|
32
33
|
Shipped profiles mount the plugin, so the default configuration registers the request field and appends the acceptance watermark; an overlay opts out with `enabled: false`.
|
|
33
34
|
|
|
34
35
|
<a id="request-field"></a>
|
|
35
36
|
## Request field
|
|
36
37
|
|
|
37
|
-
For a request carrying a live `sessionId`, the plugin folds the greatest accepted watermark for that exact Session format generation, snapshots `Session.events`, and sends the contiguous
|
|
38
|
+
For a request carrying a live `sessionId`, the plugin folds the greatest accepted watermark for that exact Session format generation, snapshots `Session.events`, and sends the longest contiguous run after the watermark that fits `maxBytes`. A process-local fold scans each event once and consumes later appends incrementally; restart and HMR rebuild it from the durable log. The version-1 field contains `sessionFormatVersion`, a raw session header (`seedLength` is present only for a seeded Session), numeric `afterSeq` and `throughSeq`, and every complete canonical event translated to raw-number envelope fields. Forked sessions ignore inherited parent watermarks because both the recorded Session id and format generation must match the request source. Surface events require `surfaceOp`, with numeric `startSeq` and `endSeq` for replacements; only system, user, and tool events may carry `sourceEventSeqs`. Assistant provider metadata stays in the embedded stream, and log-only events carry neither metadata field.
|
|
39
|
+
|
|
40
|
+
`maxBytes` bounds the complete serialized field in UTF-8 bytes, including the header and numeric envelope fields. A backlog above the limit drains across consecutive accepted requests, each continuing after the previous `throughSeq`. When the first pending event alone exceeds the limit, the request omits `dsh_session_log`, the plugin logs a warning, and the watermark stays before that event until `maxBytes` admits it. An event too large for the runtime to serialize at all is handled the same way, and no `maxBytes` value admits it.
|
|
38
41
|
|
|
39
42
|
<a id="acceptance-and-retry"></a>
|
|
40
43
|
## Acceptance and retry
|
|
41
44
|
|
|
42
|
-
The DeepSeek adapter calls the prepared contribution's `accept()` after HTTP 2xx, before it consumes the SSE body. Acceptance appends `session-log-deepseek/delivery-accepted` with the uploaded `throughSeq` and `sessionFormatVersion`; a record that omits the format field denotes v0. The next request uploads that event as part of its new suffix. Transport
|
|
45
|
+
The DeepSeek adapter calls the prepared contribution's `accept()` after HTTP 2xx, before it consumes the SSE body. Acceptance appends `session-log-deepseek/delivery-accepted` with the uploaded `throughSeq` and `sessionFormatVersion`; a record that omits the format field denotes v0. The next request uploads that event as part of its new suffix. Transport failures, non-2xx failures, and requests that the DeepSeek adapter sends without extension fields after they fail to serialize append no acceptance record, so later requests resend the uncertain range. Concurrent deliveries may be accepted out of order; folding the maximum matching `throughSeq` prevents cursor regression.
|
|
43
46
|
|
|
44
47
|
A crash after server acceptance but before the watermark reaches persistence can replay an accepted range after restart. This is the at-least-once failure direction: uncertainty creates duplicates, never a skipped sequence. The ordinary session checkpoint policy persists the watermark at the next semantic checkpoint; this plugin performs no independent I/O.
|
|
45
48
|
|
|
@@ -56,7 +59,7 @@ Nothing. `dsh_session_log` is a sibling of the DeepSeek request's model-input fi
|
|
|
56
59
|
|
|
57
60
|
#### Token effect
|
|
58
61
|
|
|
59
|
-
Zero model-input tokens; the field only increases HTTP request bytes
|
|
62
|
+
Zero model-input tokens; the field only increases HTTP request bytes, bounded by `maxBytes`.
|
|
60
63
|
|
|
61
64
|
#### KV Cache effect
|
|
62
65
|
|
|
@@ -68,7 +71,8 @@ None; the model-visible request prefix remains unchanged.
|
|
|
68
71
|
|
|
69
72
|
- **Crash-window duplicates** — a 2xx followed by process loss before the acceptance watermark persists causes conservative replay on resume.
|
|
70
73
|
- **No live Session means no field** — direct or stale-session calls have no canonical log to snapshot; explicit absence semantics remain deferred.
|
|
71
|
-
- **
|
|
74
|
+
- **An oversized event stalls upload** — an event whose field alone exceeds `maxBytes` is not sent, and later events wait behind it until the limit increases; an event too large to serialize at all stays blocked.
|
|
75
|
+
- **Provider rejection fails the request** — a provider that rejects the request because of this field fails the model request and leaves the cursor unchanged instead of truncating the log.
|
|
72
76
|
|
|
73
77
|
<a id="dev-note"></a>
|
|
74
78
|
### Dev Note
|
package/README.zh.md
CHANGED
|
@@ -28,18 +28,21 @@ kind: "package-reference"
|
|
|
28
28
|
| 配置键 | 默认值 | 含义 |
|
|
29
29
|
|---|---:|---|
|
|
30
30
|
| `enabled` | `true` | 注册 `dsh_session_log` 贡献。将其设为 `false` 可停止会话日志上传。 |
|
|
31
|
+
| `maxBytes` | 8 MiB | 单次请求携带的 `dsh_session_log` 字段序列化后的最大 UTF-8 字节数。 |
|
|
31
32
|
|
|
32
33
|
随附 profile 会挂载该插件,因此默认配置会注册请求字段并追加接受水位;overlay 可用 `enabled: false` 选择退出。
|
|
33
34
|
|
|
34
35
|
<a id="request-field"></a>
|
|
35
36
|
## 请求字段
|
|
36
37
|
|
|
37
|
-
对于携带存活 `sessionId` 的请求,插件会折叠该确切会话格式代的最大已接受水位,对 `Session.events`
|
|
38
|
+
对于携带存活 `sessionId` 的请求,插件会折叠该确切会话格式代的最大已接受水位,对 `Session.events` 取快照,并发送水位之后能放进 `maxBytes` 的最长连续事件段。进程内 fold 会让每条事件只被扫描一次并增量消费后续追加;重启与 HMR(热模块替换)会从持久日志重建它。版本 1 字段包含 `sessionFormatVersion`、原始会话 header(仅 seeded Session 携带 `seedLength`)、数值型 `afterSeq` 与 `throughSeq`,以及每个已转换为原始数值 envelope 字段的完整规范事件。只有记录的会话 id 与格式代均匹配请求来源时水位才生效,因此 fork 会话会忽略从父会话继承的水位。表层事件必须携带 `surfaceOp`,替换范围使用数值型 `startSeq` 与 `endSeq`;仅 system、user 与 tool 事件可以携带 `sourceEventSeqs`。assistant 的提供方元数据保留在内嵌流中,只出现在日志中的事件不携带这两个元数据字段。
|
|
39
|
+
|
|
40
|
+
`maxBytes` 按 UTF-8 字节限制完整的序列化字段,包括 header 与数值型 envelope 字段。超过上限的积压会分多次已接受的请求补传,每次都从上一次的 `throughSeq` 之后继续。第一条待发事件本身就超过上限时,请求会省略 `dsh_session_log`,插件记录一条告警,水位停在该事件之前,直到 `maxBytes` 能容纳它。运行时根本无法序列化的超大事件也按同样方式处理,且任何 `maxBytes` 都无法容纳它。
|
|
38
41
|
|
|
39
42
|
<a id="acceptance-and-retry"></a>
|
|
40
43
|
## 接受与重试
|
|
41
44
|
|
|
42
|
-
DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文前调用已准备贡献的 `accept()`。接受操作会追加 `session-log-deepseek/delivery-accepted`,其中包含已上传的 `throughSeq` 与 `sessionFormatVersion`;省略格式字段的记录表示 v0
|
|
45
|
+
DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文前调用已准备贡献的 `accept()`。接受操作会追加 `session-log-deepseek/delivery-accepted`,其中包含已上传的 `throughSeq` 与 `sessionFormatVersion`;省略格式字段的记录表示 v0。下一次请求再把该事件作为新后缀的一部分上传。传输失败、非 2xx 失败,以及 DeepSeek 适配器在扩展字段序列化失败后不带扩展字段发出的请求,都不会追加接受记录,因此后续请求会重发不确定范围。并发交付可能乱序得到接受;折叠匹配记录中最大的 `throughSeq` 可以防止游标回退。
|
|
43
46
|
|
|
44
47
|
服务端接受后、持久化水位前发生崩溃,可能让恢复后的进程重放已经接受的范围。这是至少一次交付的失败方向:不确定性会制造重复,绝不会跳过序列。普通会话检查点策略会在下一个语义检查点持久化水位;本插件不执行独立 I/O。
|
|
45
48
|
|
|
@@ -56,7 +59,7 @@ DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文
|
|
|
56
59
|
|
|
57
60
|
#### Token 影响
|
|
58
61
|
|
|
59
|
-
模型输入 token 为零;该字段只会增加 HTTP
|
|
62
|
+
模型输入 token 为零;该字段只会增加 HTTP 请求字节数,且受 `maxBytes` 限制。
|
|
60
63
|
|
|
61
64
|
#### KV Cache 影响
|
|
62
65
|
|
|
@@ -68,7 +71,8 @@ DeepSeek 适配器会在 HTTP 2xx 后、消费 SSE(Server-Sent Events)正文
|
|
|
68
71
|
|
|
69
72
|
- **崩溃窗口重复**——2xx 后、接受水位持久化前进程终止,会在恢复时触发保守重放。
|
|
70
73
|
- **缺少存活会话就没有字段**——直接调用或陈旧会话调用没有可供快照的规范日志;显式缺失语义仍暂缓处理。
|
|
71
|
-
-
|
|
74
|
+
- **超限事件会让上传停住**——字段单独就超过 `maxBytes` 的事件不会发送,后续事件也排在它后面,直到上限调高;根本无法序列化的事件会一直阻塞上传。
|
|
75
|
+
- **提供方拒绝会让请求失败**——提供方因该字段拒绝请求时,模型请求失败,游标保持不变,而非截断日志。
|
|
72
76
|
|
|
73
77
|
<a id="dev-note"></a>
|
|
74
78
|
### 开发备注
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
1
2
|
import z from "@deepseek-ai/schemastery";
|
|
2
3
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
3
4
|
import { KNOWN_SESSION_EVENT_TYPES, SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
|
|
@@ -13,7 +14,10 @@ const name = "session-log-deepseek";
|
|
|
13
14
|
/** Services required to resolve sessions and contribute the provider request field. */
|
|
14
15
|
const inject = ["deepseekLlmApiExtensions", "sessions"];
|
|
15
16
|
/** Validated Session-log request contribution configuration. */
|
|
16
|
-
const Config = z.object({
|
|
17
|
+
const Config = z.object({
|
|
18
|
+
enabled: z.boolean().default(true),
|
|
19
|
+
maxBytes: z.number().step(1).min(1).default(8 * 1024 * 1024)
|
|
20
|
+
});
|
|
17
21
|
const acceptanceFolds = /* @__PURE__ */ new WeakMap();
|
|
18
22
|
/** Translate logical Session metadata to raw external request fields. */
|
|
19
23
|
function wireHeader(session) {
|
|
@@ -78,6 +82,19 @@ function wireSurfaceOp(op) {
|
|
|
78
82
|
};
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
85
|
+
* UTF-8 length of one value's JSON text as the request body encodes it.
|
|
86
|
+
* @returns `Infinity` when the value fails to serialize, which counts as exceeding every limit.
|
|
87
|
+
*/
|
|
88
|
+
function jsonBytes(value) {
|
|
89
|
+
let text;
|
|
90
|
+
try {
|
|
91
|
+
text = JSON.stringify(value);
|
|
92
|
+
} catch (_unserializable) {
|
|
93
|
+
return Number.POSITIVE_INFINITY;
|
|
94
|
+
}
|
|
95
|
+
return Buffer.byteLength(text);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
81
98
|
* Highest confirmed sequence for this exact Session format generation.
|
|
82
99
|
* @param session - canonical log whose matching acceptance events are folded.
|
|
83
100
|
* @returns greatest accepted sequence, or `-1` before any accepted request.
|
|
@@ -117,22 +134,49 @@ function acceptedThrough(session) {
|
|
|
117
134
|
*/
|
|
118
135
|
function apply(ctx, config) {
|
|
119
136
|
if (config.enabled !== true) return;
|
|
137
|
+
const { maxBytes } = config;
|
|
120
138
|
ctx.deepseekLlmApiExtensions.register("dsh_session_log", { prepare: (request) => {
|
|
121
139
|
if (request.sessionId === void 0) return void 0;
|
|
122
140
|
const session = ctx.sessions.get(brandString(request.sessionId));
|
|
123
141
|
if (session === void 0) return void 0;
|
|
124
142
|
const afterSeq = acceptedThrough(session);
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
const pending = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
|
|
144
|
+
const envelope = {
|
|
145
|
+
version: 1,
|
|
146
|
+
sessionFormatVersion: session.header.version,
|
|
147
|
+
session: wireHeader(session),
|
|
148
|
+
afterSeq: Number(afterSeq)
|
|
149
|
+
};
|
|
150
|
+
let bytes = jsonBytes({
|
|
151
|
+
...envelope,
|
|
152
|
+
throughSeq: 0,
|
|
153
|
+
events: []
|
|
154
|
+
}) - 1;
|
|
155
|
+
const events = [];
|
|
156
|
+
let candidateBytes = 0;
|
|
157
|
+
for (const event of pending) {
|
|
158
|
+
const wire = wireEvent(event);
|
|
159
|
+
const next = bytes + (events.length === 0 ? 0 : 1) + jsonBytes(wire);
|
|
160
|
+
candidateBytes = next + String(event.seq).length;
|
|
161
|
+
if (candidateBytes > maxBytes) break;
|
|
162
|
+
bytes = next;
|
|
163
|
+
events.push(wire);
|
|
164
|
+
}
|
|
165
|
+
const last = events.length === 0 ? void 0 : pending[events.length - 1];
|
|
166
|
+
if (last === void 0) {
|
|
167
|
+
const first = pending[0];
|
|
168
|
+
if (first !== void 0) {
|
|
169
|
+
const seq = String(first.seq);
|
|
170
|
+
ctx.logger.warn(Number.isFinite(candidateBytes) ? `session-log-deepseek: event ${seq} of session "${session.id}" needs a ${String(candidateBytes)}-byte dsh_session_log field, above maxBytes ${String(maxBytes)}; this session's upload stays at event ${seq} until maxBytes admits it` : `session-log-deepseek: event ${seq} of session "${session.id}" is too large to serialize into a dsh_session_log field; this session's upload stays at event ${seq}`);
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const throughSeq = last.seq;
|
|
128
175
|
return {
|
|
129
176
|
value: {
|
|
130
|
-
|
|
131
|
-
sessionFormatVersion: session.header.version,
|
|
132
|
-
session: wireHeader(session),
|
|
133
|
-
afterSeq: Number(afterSeq),
|
|
177
|
+
...envelope,
|
|
134
178
|
throughSeq: Number(throughSeq),
|
|
135
|
-
events
|
|
179
|
+
events
|
|
136
180
|
},
|
|
137
181
|
accept: () => {
|
|
138
182
|
session.append("session-log-deepseek/delivery-accepted", {
|
package/lib/types/index.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ export declare const inject: string[];
|
|
|
16
16
|
export interface Config {
|
|
17
17
|
/** Contribute `dsh_session_log` to official DeepSeek requests. Defaults to `true`. */
|
|
18
18
|
enabled?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries.
|
|
21
|
+
* A request uploads the longest pending event prefix that fits; later requests continue
|
|
22
|
+
* after its acceptance. Defaults to 8 MiB.
|
|
23
|
+
*/
|
|
24
|
+
maxBytes?: number;
|
|
19
25
|
}
|
|
20
26
|
/** Validated Session-log request contribution configuration. */
|
|
21
27
|
export declare const Config: z<Config>;
|
package/lib/types/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* can conservatively resend uncertain tails without maintaining another store.
|
|
5
5
|
* @module @deepseek-ai/dsh-session-log-deepseek
|
|
6
6
|
*/
|
|
7
|
+
import { Buffer } from 'node:buffer';
|
|
7
8
|
import z from '@deepseek-ai/schemastery';
|
|
8
9
|
import { brandString } from '@deepseek-ai/dsh-brand';
|
|
9
10
|
import { KNOWN_SESSION_EVENT_TYPES, SessionLogOffset, SessionSeq } from '@deepseek-ai/dsh-session';
|
|
@@ -14,6 +15,7 @@ export const inject = ['deepseekLlmApiExtensions', 'sessions'];
|
|
|
14
15
|
/** Validated Session-log request contribution configuration. */
|
|
15
16
|
export const Config = z.object({
|
|
16
17
|
enabled: z.boolean().default(true),
|
|
18
|
+
maxBytes: z.number().step(1).min(1).default(8 * 1024 * 1024),
|
|
17
19
|
});
|
|
18
20
|
const acceptanceFolds = new WeakMap();
|
|
19
21
|
/** Translate logical Session metadata to raw external request fields. */
|
|
@@ -71,6 +73,21 @@ function wireSurfaceOp(op) {
|
|
|
71
73
|
? op
|
|
72
74
|
: { op: 'replace', startSeq: Number(op.startSeq), endSeq: Number(op.endSeq) };
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* UTF-8 length of one value's JSON text as the request body encodes it.
|
|
78
|
+
* @returns `Infinity` when the value fails to serialize, which counts as exceeding every limit.
|
|
79
|
+
*/
|
|
80
|
+
function jsonBytes(value) {
|
|
81
|
+
let text;
|
|
82
|
+
try {
|
|
83
|
+
text = JSON.stringify(value);
|
|
84
|
+
}
|
|
85
|
+
catch (_unserializable) {
|
|
86
|
+
// No request can carry a value that fails to serialize.
|
|
87
|
+
return Number.POSITIVE_INFINITY;
|
|
88
|
+
}
|
|
89
|
+
return Buffer.byteLength(text);
|
|
90
|
+
}
|
|
74
91
|
/**
|
|
75
92
|
* Highest confirmed sequence for this exact Session format generation.
|
|
76
93
|
* @param session - canonical log whose matching acceptance events are folded.
|
|
@@ -124,6 +141,8 @@ export function acceptedThrough(session) {
|
|
|
124
141
|
export function apply(ctx, config) {
|
|
125
142
|
if (config.enabled !== true)
|
|
126
143
|
return;
|
|
144
|
+
// Schemastery validates and fills the defaults before `apply` runs.
|
|
145
|
+
const { maxBytes } = config;
|
|
127
146
|
ctx.deepseekLlmApiExtensions.register('dsh_session_log', {
|
|
128
147
|
prepare: (request) => {
|
|
129
148
|
// TODO: Define an explicit wire result for direct or stale-session calls if they become a supported product path.
|
|
@@ -134,20 +153,43 @@ export function apply(ctx, config) {
|
|
|
134
153
|
return undefined;
|
|
135
154
|
const afterSeq = acceptedThrough(session);
|
|
136
155
|
// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
if (throughSeq === undefined)
|
|
140
|
-
return undefined;
|
|
141
|
-
// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
|
|
142
|
-
const suffix = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
|
|
143
|
-
const value = {
|
|
156
|
+
const pending = session.snapshotEvents(SessionLogOffset(afterSeq + 1));
|
|
157
|
+
const envelope = {
|
|
144
158
|
version: 1,
|
|
145
159
|
sessionFormatVersion: session.header.version,
|
|
146
160
|
session: wireHeader(session),
|
|
147
161
|
afterSeq: Number(afterSeq),
|
|
148
|
-
throughSeq: Number(throughSeq),
|
|
149
|
-
events: suffix.map(wireEvent),
|
|
150
162
|
};
|
|
163
|
+
// Field bytes without events or throughSeq digits; each candidate prefix adds its own.
|
|
164
|
+
let bytes = jsonBytes({ ...envelope, throughSeq: 0, events: [] }) - 1;
|
|
165
|
+
const events = [];
|
|
166
|
+
// Field bytes with the last examined event; when none fits, the first event's own field size.
|
|
167
|
+
let candidateBytes = 0;
|
|
168
|
+
for (const event of pending) {
|
|
169
|
+
const wire = wireEvent(event);
|
|
170
|
+
const next = bytes + (events.length === 0 ? 0 : 1) + jsonBytes(wire);
|
|
171
|
+
candidateBytes = next + String(event.seq).length;
|
|
172
|
+
if (candidateBytes > maxBytes)
|
|
173
|
+
break;
|
|
174
|
+
bytes = next;
|
|
175
|
+
events.push(wire);
|
|
176
|
+
}
|
|
177
|
+
const last = events.length === 0 ? undefined : pending[events.length - 1];
|
|
178
|
+
if (last === undefined) {
|
|
179
|
+
const first = pending[0];
|
|
180
|
+
if (first !== undefined) {
|
|
181
|
+
const seq = String(first.seq);
|
|
182
|
+
ctx.logger.warn(Number.isFinite(candidateBytes)
|
|
183
|
+
? `session-log-deepseek: event ${seq} of session "${session.id}" needs a ${String(candidateBytes)}-byte`
|
|
184
|
+
+ ` dsh_session_log field, above maxBytes ${String(maxBytes)}; this session's upload stays at event ${seq}`
|
|
185
|
+
+ ' until maxBytes admits it'
|
|
186
|
+
: `session-log-deepseek: event ${seq} of session "${session.id}" is too large to serialize into a dsh_session_log field;`
|
|
187
|
+
+ ` this session's upload stays at event ${seq}`);
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
const throughSeq = last.seq;
|
|
192
|
+
const value = { ...envelope, throughSeq: Number(throughSeq), events };
|
|
151
193
|
return {
|
|
152
194
|
value,
|
|
153
195
|
accept: () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-session-log-deepseek",
|
|
3
3
|
"description": "Incremental lossless session-log request extension for the official DeepSeek LLM API",
|
|
4
|
-
"version": "0.1.7-
|
|
4
|
+
"version": "0.1.7-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,28 +37,29 @@
|
|
|
37
37
|
],
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@deepseek-ai/dsh-brand": "0.1.7-
|
|
41
|
-
"@deepseek-ai/dsh-util-values": "0.1.7-
|
|
40
|
+
"@deepseek-ai/dsh-brand": "0.1.7-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-rc.2",
|
|
42
42
|
"@deepseek-ai/schemastery": "~3.18.4"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-alpha.2",
|
|
46
45
|
"@deepseek-ai/cordis": "~4.0.4",
|
|
47
|
-
"@deepseek-ai/dsh-
|
|
48
|
-
"@deepseek-ai/dsh-
|
|
46
|
+
"@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-invariants": "0.1.7-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
+
"@deepseek-ai/cordis": "~4.0.4",
|
|
51
52
|
"@deepseek-ai/cordis-plugin-include": "~1.0.9",
|
|
52
53
|
"@deepseek-ai/cordis-plugin-loader": "~1.0.5",
|
|
53
|
-
"@deepseek-ai/dsh-command-feedback": "0.1.7-
|
|
54
|
-
"@deepseek-ai/
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-llm": "0.1.7-
|
|
58
|
-
"@deepseek-ai/dsh-llm-deepseek": "0.1.7-
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-session": "0.1.7-
|
|
62
|
-
"@deepseek-ai/dsh-
|
|
54
|
+
"@deepseek-ai/dsh-command-feedback": "0.1.7-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-deepseek-llm-api-extensions": "0.1.7-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-invariants": "0.1.7-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-llm-deepseek": "0.1.7-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-llm-deepseek-api-key": "0.1.7-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-message-feedback": "0.1.7-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "0.1.7-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-llm-mock-server": "0.1.7-rc.2"
|
|
63
64
|
}
|
|
64
65
|
}
|