@apollo-music/dsh-sidecar-terms 0.1.4 → 0.1.5
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.md +2 -1
- package/dist/coordinator.d.ts +7 -0
- package/dist/coordinator.js +58 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
6. steer 修订后不再审计同一 turn 的后续 assistant 输出,避免规则回执自我触发;下一 turn 自动恢复;
|
|
11
11
|
7. 最终答案前只 steer 尚未交付的 canonical term / Rule delta。
|
|
12
12
|
8. Catalog、Resolver、鉴权或网络不可用时失败关闭,不生成离线替代规则。
|
|
13
|
+
9. 超过 Resolver 单段上限的消息只在术语审计传输层按自然或结构边界无损分段;DSH 交给模型的消息保持完整不变,分段按原事件序号在一次 Resolver 请求中合并命中与 Delta。
|
|
13
14
|
|
|
14
15
|
插件固定只读 `https://apollomusic-poc1.fde0.com/kb/terminology`。`apiUrl` 不再是运行时配置项,线上服务返回 `degraded` 也会按错误处理;插件不包含本地术语表或 P0 fallback。
|
|
15
16
|
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
在 DeepSeek Harness 项目中安装:
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
|
-
npm install @apollo-music/dsh-sidecar-terms@0.1.
|
|
22
|
+
npm install @apollo-music/dsh-sidecar-terms@0.1.5
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
## 配置
|
package/dist/coordinator.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { type UserMessage } from "@deepseek-ai/dsh-llm";
|
|
2
2
|
import { TerminologyApi } from "./api.js";
|
|
3
3
|
import type { AgentPort, LoggerPort, PluginConfig, ResolveResponse } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Split one audited message for the resolver transport without changing the
|
|
6
|
+
* message that DSH sends to the model. Chunks preserve order and concatenate
|
|
7
|
+
* exactly to the original text; natural or serialized-JSON boundaries are
|
|
8
|
+
* preferred so a multi-word terminology match is not cut unnecessarily.
|
|
9
|
+
*/
|
|
10
|
+
export declare function splitResolverText(text: string): string[];
|
|
4
11
|
interface AuditItem {
|
|
5
12
|
eventSeq: number;
|
|
6
13
|
source: string;
|
package/dist/coordinator.js
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
2
2
|
import { findCatalogMatches } from "./matcher.js";
|
|
3
3
|
const PLUGIN_NAME = "apollo-terminology-rules";
|
|
4
|
+
const RESOLVER_TEXT_TARGET_CHARS = 90_000;
|
|
5
|
+
const NATURAL_TEXT_BOUNDARIES = [
|
|
6
|
+
{ needle: "\n\n", offset: 2 },
|
|
7
|
+
{ needle: "\n", offset: 1 },
|
|
8
|
+
{ needle: "},{\"", offset: 2 },
|
|
9
|
+
{ needle: "\",\"", offset: 2 },
|
|
10
|
+
{ needle: "。", offset: 1 },
|
|
11
|
+
{ needle: "!", offset: 1 },
|
|
12
|
+
{ needle: "?", offset: 1 },
|
|
13
|
+
{ needle: ". ", offset: 2 },
|
|
14
|
+
{ needle: "! ", offset: 2 },
|
|
15
|
+
{ needle: "? ", offset: 2 },
|
|
16
|
+
{ needle: "; ", offset: 2 },
|
|
17
|
+
{ needle: ";", offset: 1 },
|
|
18
|
+
{ needle: " ", offset: 1 },
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Split one audited message for the resolver transport without changing the
|
|
22
|
+
* message that DSH sends to the model. Chunks preserve order and concatenate
|
|
23
|
+
* exactly to the original text; natural or serialized-JSON boundaries are
|
|
24
|
+
* preferred so a multi-word terminology match is not cut unnecessarily.
|
|
25
|
+
*/
|
|
26
|
+
export function splitResolverText(text) {
|
|
27
|
+
if (text.length <= RESOLVER_TEXT_TARGET_CHARS)
|
|
28
|
+
return [text];
|
|
29
|
+
const chunks = [];
|
|
30
|
+
let start = 0;
|
|
31
|
+
while (start < text.length) {
|
|
32
|
+
const targetEnd = Math.min(start + RESOLVER_TEXT_TARGET_CHARS, text.length);
|
|
33
|
+
if (targetEnd === text.length) {
|
|
34
|
+
chunks.push(text.slice(start));
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
const preferredStart = start + Math.floor(RESOLVER_TEXT_TARGET_CHARS * 0.75);
|
|
38
|
+
let end = 0;
|
|
39
|
+
for (const boundary of NATURAL_TEXT_BOUNDARIES) {
|
|
40
|
+
const index = text.lastIndexOf(boundary.needle, targetEnd - boundary.needle.length);
|
|
41
|
+
const candidate = index + boundary.offset;
|
|
42
|
+
if (index >= preferredStart && candidate > end)
|
|
43
|
+
end = candidate;
|
|
44
|
+
}
|
|
45
|
+
if (end <= start)
|
|
46
|
+
end = targetEnd;
|
|
47
|
+
// JavaScript string limits are measured in UTF-16 code units. Avoid
|
|
48
|
+
// placing a transport boundary between the halves of a surrogate pair.
|
|
49
|
+
const before = text.charCodeAt(end - 1);
|
|
50
|
+
const after = text.charCodeAt(end);
|
|
51
|
+
if (before >= 0xD800 && before <= 0xDBFF
|
|
52
|
+
&& after >= 0xDC00 && after <= 0xDFFF) {
|
|
53
|
+
end -= 1;
|
|
54
|
+
}
|
|
55
|
+
chunks.push(text.slice(start, end));
|
|
56
|
+
start = end;
|
|
57
|
+
}
|
|
58
|
+
return chunks;
|
|
59
|
+
}
|
|
4
60
|
export class TerminologyCoordinator {
|
|
5
61
|
config;
|
|
6
62
|
api;
|
|
@@ -165,6 +221,7 @@ export class TerminologyCoordinator {
|
|
|
165
221
|
async queryOnce(agent, state, mode, captured) {
|
|
166
222
|
const segments = [...state.pendingSegments.values()].sort((a, b) => a.event_seq - b.event_seq);
|
|
167
223
|
const throughSeq = segments.at(-1).event_seq;
|
|
224
|
+
const resolverSegments = segments.flatMap(({ event_seq, source, text }) => splitResolverText(text).map((chunk) => ({ event_seq, source, text: chunk })));
|
|
168
225
|
for (const segment of segments)
|
|
169
226
|
state.pendingSegments.delete(segment.event_seq);
|
|
170
227
|
state.lastQueryAt = Date.now();
|
|
@@ -172,7 +229,7 @@ export class TerminologyCoordinator {
|
|
|
172
229
|
const response = await this.api.resolve({
|
|
173
230
|
session_id: agent.id,
|
|
174
231
|
audit_through_seq: throughSeq,
|
|
175
|
-
segments:
|
|
232
|
+
segments: resolverSegments,
|
|
176
233
|
candidate_terms: [...new Set(segments.flatMap((segment) => segment.candidates ?? []))],
|
|
177
234
|
delivered_rule_ids: this.knownRuleIds(state),
|
|
178
235
|
delivered_term_ids: this.knownTermIds(state),
|
package/package.json
CHANGED