
















@@ -57,6 +57,15 @@ export type PlainTextToolCallParseOptions = {
57575858const DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES = 256_000;
5959const END_TOOL_REQUEST = "[END_TOOL_REQUEST]";
60+const HARMONY_CHANNEL_MARKER = "<|channel|>";
61+const HARMONY_MESSAGE_MARKER = "<|message|>";
62+const HARMONY_CALL_MARKER = "<|call|>";
63+64+type PlainTextToolCallOpening = {
65+end: number;
66+name: string;
67+requiresClosing: boolean;
68+};
60696170function isToolNameChar(char: string | undefined): boolean {
6271return Boolean(char && /[A-Za-z0-9_-]/.test(char));
@@ -88,7 +97,7 @@ function consumeLineBreak(text: string, start: number): number | null {
8897return null;
8998}
909991-function parseOpening(text: string, start: number): { end: number; name: string } | null {
100+function parseBracketOpening(text: string, start: number): PlainTextToolCallOpening | null {
92101if (text[start] !== "[") {
93102return null;
94103}
@@ -107,7 +116,49 @@ function parseOpening(text: string, start: number): { end: number; name: string
107116if (afterLineBreak === null) {
108117return null;
109118}
110-return { end: afterLineBreak, name };
119+return { end: afterLineBreak, name, requiresClosing: true };
120+}
121+122+function parseHarmonyOpening(text: string, start: number): PlainTextToolCallOpening | null {
123+let cursor = start;
124+if (text.startsWith(HARMONY_CHANNEL_MARKER, cursor)) {
125+cursor += HARMONY_CHANNEL_MARKER.length;
126+}
127+const channelStart = cursor;
128+while (/[A-Za-z_]/.test(text[cursor] ?? "")) {
129+cursor += 1;
130+}
131+const channel = text.slice(channelStart, cursor);
132+if (channel !== "commentary" && channel !== "analysis" && channel !== "final") {
133+return null;
134+}
135+cursor = skipHorizontalWhitespace(text, cursor);
136+if (!text.startsWith("to=", cursor)) {
137+return null;
138+}
139+cursor += 3;
140+const nameStart = cursor;
141+while (isToolNameChar(text[cursor])) {
142+cursor += 1;
143+}
144+if (cursor === nameStart) {
145+return null;
146+}
147+const name = text.slice(nameStart, cursor);
148+cursor = skipHorizontalWhitespace(text, cursor);
149+if (!text.startsWith("code", cursor)) {
150+return null;
151+}
152+cursor += 4;
153+cursor = skipWhitespace(text, cursor);
154+if (text.startsWith(HARMONY_MESSAGE_MARKER, cursor)) {
155+cursor = skipWhitespace(text, cursor + HARMONY_MESSAGE_MARKER.length);
156+}
157+return { end: cursor, name, requiresClosing: false };
158+}
159+160+function parseOpening(text: string, start: number): PlainTextToolCallOpening | null {
161+return parseBracketOpening(text, start) ?? parseHarmonyOpening(text, start);
111162}
112163113164function consumeJsonObject(
@@ -174,6 +225,14 @@ function parseClosing(text: string, start: number, name: string): number | null
174225return null;
175226}
176227228+function parseOptionalHarmonyClosing(text: string, start: number): number {
229+const cursor = skipWhitespace(text, start);
230+if (text.startsWith(HARMONY_CALL_MARKER, cursor)) {
231+return cursor + HARMONY_CALL_MARKER.length;
232+}
233+return start;
234+}
235+177236function parsePlainTextToolCallBlockAt(
178237text: string,
179238start: number,
@@ -197,15 +256,17 @@ function parsePlainTextToolCallBlockAt(
197256if (!payload) {
198257return null;
199258}
200-const end = parseClosing(text, payload.end, opening.name);
201-if (end === null) {
259+const closingEnd = opening.requiresClosing
260+ ? parseClosing(text, payload.end, opening.name)
261+ : parseOptionalHarmonyClosing(text, payload.end);
262+if (closingEnd === null) {
202263return null;
203264}
204265return {
205266arguments: payload.value,
206- end,
267+end: closingEnd,
207268name: opening.name,
208-raw: text.slice(start, end),
269+raw: text.slice(start, closingEnd),
209270 start,
210271};
211272}
@@ -228,7 +289,11 @@ export function parseStandalonePlainTextToolCallBlocks(
228289}
229290230291export function stripPlainTextToolCallBlocks(text: string): string {
231-if (!text || !/\[[A-Za-z0-9_-]+\]/.test(text)) {
292+if (
293+!text ||
294+(!/\[[A-Za-z0-9_-]+\]/.test(text) &&
295+!/(?:^|\n)\s*(?:<\|channel\|>)?(?:commentary|analysis|final)\s+to=/.test(text))
296+) {
232297return text;
233298}
234299let result = "";
@@ -248,7 +313,11 @@ export function stripPlainTextToolCallBlocks(text: string): string {
248313}
249314result += text.slice(cursor, index);
250315cursor = block.end;
251-index = block.end;
316+const afterBlockLineBreak = consumeLineBreak(text, cursor);
317+if (afterBlockLineBreak !== null) {
318+cursor = afterBlockLineBreak;
319+}
320+index = cursor;
252321}
253322result += text.slice(cursor);
254323return result;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。