@theokit/sdk 2.15.1 → 2.15.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/CHANGELOG.md +6 -0
- package/dist/a2a/index.cjs +106 -10
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +106 -10
- package/dist/a2a/index.js.map +1 -1
- package/dist/cron.cjs +105 -9
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.js +105 -9
- package/dist/cron.js.map +1 -1
- package/dist/eval.cjs +105 -9
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +105 -9
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +105 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +105 -9
- package/dist/index.js.map +1 -1
- package/dist/internal/llm/openai.d.ts +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.15.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6336f81: Suppress the leaked-dialect tool-call from the visible stream (R7). When `extractToolCallsFromContent` is enabled and a model leaks a `<function=NAME>` tool call as assistant text, the OpenAI-compat streaming now HOLDS that text back at the stream boundary (a small suspicion-buffer FSM that reuses the request-scoped allowlist from R5) instead of emitting it as `text_delta` events — so the raw dialect no longer flashes by in the live stream or lands in the final assistant text. `finish()` still recovers the call (unchanged). Fail-open: a never-closing marker or un-suppressable input is flushed as visible text (never held forever). Flag-off streaming is byte-for-byte unchanged. Grounded in openclaw's stream-normalizer FSM.
|
|
8
|
+
|
|
3
9
|
## 2.15.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/a2a/index.cjs
CHANGED
|
@@ -10365,12 +10365,83 @@ function parseHermesParams(inner) {
|
|
|
10365
10365
|
}
|
|
10366
10366
|
return sanitizeToolInput(input, { trim: true }).value;
|
|
10367
10367
|
}
|
|
10368
|
-
|
|
10368
|
+
function streamToolCallBufferState(held, allowedToolNames, cap = DEFAULT_STREAM_BUFFER_CAP) {
|
|
10369
|
+
if (allowedToolNames.size === 0) return "impossible";
|
|
10370
|
+
const t = held.trimStart();
|
|
10371
|
+
if (t.length < STREAM_MARKER.length) {
|
|
10372
|
+
return STREAM_MARKER.startsWith(t) ? "possible" : "impossible";
|
|
10373
|
+
}
|
|
10374
|
+
if (!t.startsWith(STREAM_MARKER)) return "impossible";
|
|
10375
|
+
const parsed = parseStreamMarkerName(t);
|
|
10376
|
+
if (parsed === "building") return "possible";
|
|
10377
|
+
if (parsed === "invalid") return "impossible";
|
|
10378
|
+
const nameOk = parsed.complete ? allowedToolNames.has(parsed.name) : someToolNameStartsWith(allowedToolNames, parsed.name);
|
|
10379
|
+
if (!nameOk) return "impossible";
|
|
10380
|
+
return held.length > cap ? "impossible" : "possible";
|
|
10381
|
+
}
|
|
10382
|
+
function parseStreamMarkerName(t) {
|
|
10383
|
+
let cursor = STREAM_MARKER.length;
|
|
10384
|
+
while (cursor < t.length && isStreamWs(t[cursor])) cursor += 1;
|
|
10385
|
+
const nameStart = cursor;
|
|
10386
|
+
while (cursor < t.length && t[cursor] !== ">" && !isStreamWs(t[cursor])) cursor += 1;
|
|
10387
|
+
const name = t.slice(nameStart, cursor);
|
|
10388
|
+
if (name.length === 0) return cursor >= t.length ? "building" : "invalid";
|
|
10389
|
+
return { name, complete: cursor < t.length && t[cursor] === ">" };
|
|
10390
|
+
}
|
|
10391
|
+
function someToolNameStartsWith(allowedToolNames, prefix) {
|
|
10392
|
+
for (const name of allowedToolNames) {
|
|
10393
|
+
if (name.startsWith(prefix)) return true;
|
|
10394
|
+
}
|
|
10395
|
+
return false;
|
|
10396
|
+
}
|
|
10397
|
+
function firstPossibleMarkerStart(held, allowedToolNames) {
|
|
10398
|
+
for (let i = held.indexOf("<"); i !== -1; i = held.indexOf("<", i + 1)) {
|
|
10399
|
+
if (streamToolCallBufferState(held.slice(i), allowedToolNames) === "possible") return i;
|
|
10400
|
+
}
|
|
10401
|
+
return -1;
|
|
10402
|
+
}
|
|
10403
|
+
var HERMES_BLOCK, HERMES_PARAM, STREAM_MARKER, DEFAULT_STREAM_BUFFER_CAP, isStreamWs, StreamSuppressionBuffer;
|
|
10369
10404
|
var init_hermes_tool_extract = __esm({
|
|
10370
10405
|
"src/internal/llm/hermes-tool-extract.ts"() {
|
|
10371
10406
|
init_sanitize_tool_input();
|
|
10372
10407
|
HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
|
|
10373
10408
|
HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
|
|
10409
|
+
STREAM_MARKER = "<function=";
|
|
10410
|
+
DEFAULT_STREAM_BUFFER_CAP = 8192;
|
|
10411
|
+
isStreamWs = (c) => c === " " || c === " " || c === "\n" || c === "\r";
|
|
10412
|
+
StreamSuppressionBuffer = class {
|
|
10413
|
+
constructor(allowedToolNames) {
|
|
10414
|
+
this.allowedToolNames = allowedToolNames;
|
|
10415
|
+
}
|
|
10416
|
+
allowedToolNames;
|
|
10417
|
+
#held = "";
|
|
10418
|
+
/** Feed a content delta; returns the text to emit as a `text_delta` now, or `undefined` to hold. */
|
|
10419
|
+
push(content) {
|
|
10420
|
+
this.#held += content;
|
|
10421
|
+
if (streamToolCallBufferState(this.#held, this.allowedToolNames) === "possible")
|
|
10422
|
+
return void 0;
|
|
10423
|
+
const holdStart = firstPossibleMarkerStart(this.#held, this.allowedToolNames);
|
|
10424
|
+
if (holdStart > 0) {
|
|
10425
|
+
const flush2 = this.#held.slice(0, holdStart);
|
|
10426
|
+
this.#held = this.#held.slice(holdStart);
|
|
10427
|
+
return flush2;
|
|
10428
|
+
}
|
|
10429
|
+
const flush = this.#held;
|
|
10430
|
+
this.#held = "";
|
|
10431
|
+
return flush;
|
|
10432
|
+
}
|
|
10433
|
+
/** Drain the held buffer at stream end. `hasNativeCalls` mirrors `finish()`'s size-guard: when
|
|
10434
|
+
* native `tool_calls` exist, `finish()` won't strip the leaked block, so stream the held text WHOLE
|
|
10435
|
+
* (keeping `accumulatedText == finish.text`); otherwise strip the recoverable blocks. Idempotent. */
|
|
10436
|
+
drain(hasNativeCalls) {
|
|
10437
|
+
if (this.#held.length === 0) return void 0;
|
|
10438
|
+
const held = this.#held;
|
|
10439
|
+
this.#held = "";
|
|
10440
|
+
if (hasNativeCalls) return held;
|
|
10441
|
+
const residual = extractHermesToolCalls(held, () => "held", this.allowedToolNames).residualText;
|
|
10442
|
+
return residual.length > 0 ? residual : void 0;
|
|
10443
|
+
}
|
|
10444
|
+
};
|
|
10374
10445
|
}
|
|
10375
10446
|
});
|
|
10376
10447
|
|
|
@@ -10584,6 +10655,8 @@ var init_openai2 = __esm({
|
|
|
10584
10655
|
const events = accumulator.consume(chunk);
|
|
10585
10656
|
for (const event of events) yield event;
|
|
10586
10657
|
}
|
|
10658
|
+
const drainEvent = accumulator.finalizeHeldText();
|
|
10659
|
+
if (drainEvent !== void 0) yield drainEvent;
|
|
10587
10660
|
return accumulator.finish();
|
|
10588
10661
|
}
|
|
10589
10662
|
};
|
|
@@ -10599,6 +10672,7 @@ var init_openai2 = __esm({
|
|
|
10599
10672
|
this.extractFromContent = extractFromContent;
|
|
10600
10673
|
this.providerName = providerName;
|
|
10601
10674
|
this.allowedToolNames = allowedToolNames;
|
|
10675
|
+
this.suppress = extractFromContent && allowedToolNames !== void 0 && allowedToolNames.size > 0 ? new StreamSuppressionBuffer(allowedToolNames) : void 0;
|
|
10602
10676
|
}
|
|
10603
10677
|
extractFromContent;
|
|
10604
10678
|
providerName;
|
|
@@ -10611,18 +10685,30 @@ var init_openai2 = __esm({
|
|
|
10611
10685
|
cacheWriteTokens;
|
|
10612
10686
|
reasoningTokens;
|
|
10613
10687
|
toolCalls = /* @__PURE__ */ new Map();
|
|
10688
|
+
/** R7: present only when recovery is enabled AND the request declares tools — holds suspected
|
|
10689
|
+
* leaked-dialect content back from the `text_delta` stream. `undefined` ⇒ stream immediately. */
|
|
10690
|
+
suppress;
|
|
10614
10691
|
consume(chunk) {
|
|
10615
10692
|
const events = [];
|
|
10616
10693
|
this.applyUsage(chunk.usage);
|
|
10617
10694
|
for (const choice of chunk.choices ?? []) {
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10695
|
+
events.push(...this.applyChoice(choice));
|
|
10696
|
+
}
|
|
10697
|
+
return events;
|
|
10698
|
+
}
|
|
10699
|
+
applyChoice(choice) {
|
|
10700
|
+
const events = [];
|
|
10701
|
+
const reasoningEvent = this.applyReasoningDelta(
|
|
10702
|
+
choice.delta?.reasoning ?? choice.delta?.reasoning_content
|
|
10703
|
+
);
|
|
10704
|
+
if (reasoningEvent !== void 0) events.push(reasoningEvent);
|
|
10705
|
+
const textEvent = this.applyContentDelta(choice.delta?.content);
|
|
10706
|
+
if (textEvent !== void 0) events.push(textEvent);
|
|
10707
|
+
this.mergeToolCallDeltas(choice.delta?.tool_calls);
|
|
10708
|
+
this.applyFinishReason(choice.finish_reason);
|
|
10709
|
+
if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
|
|
10710
|
+
const flushEvent = this.finalizeHeldText();
|
|
10711
|
+
if (flushEvent !== void 0) events.push(flushEvent);
|
|
10626
10712
|
}
|
|
10627
10713
|
return events;
|
|
10628
10714
|
}
|
|
@@ -10647,7 +10733,17 @@ var init_openai2 = __esm({
|
|
|
10647
10733
|
applyContentDelta(content) {
|
|
10648
10734
|
if (typeof content !== "string" || content.length === 0) return void 0;
|
|
10649
10735
|
this.text += content;
|
|
10650
|
-
return { type: "text_delta", text: content };
|
|
10736
|
+
if (this.suppress === void 0) return { type: "text_delta", text: content };
|
|
10737
|
+
const emit = this.suppress.push(content);
|
|
10738
|
+
return emit !== void 0 ? { type: "text_delta", text: emit } : void 0;
|
|
10739
|
+
}
|
|
10740
|
+
/** R7 held-buffer finalizer, called at the `finish_reason` chunk (in `applyChoice`) AND after the
|
|
10741
|
+
* SSE loop in `stream()` — so a stream that omits a `finish_reason` terminal never silently drops
|
|
10742
|
+
* held text. `toolCalls.size > 0` (native calls present) makes `finish()` skip recovery, so the
|
|
10743
|
+
* buffer streams the held text whole. Idempotent once drained. */
|
|
10744
|
+
finalizeHeldText() {
|
|
10745
|
+
const emit = this.suppress?.drain(this.toolCalls.size > 0);
|
|
10746
|
+
return emit !== void 0 ? { type: "text_delta", text: emit } : void 0;
|
|
10651
10747
|
}
|
|
10652
10748
|
mergeToolCallDeltas(deltas) {
|
|
10653
10749
|
for (const call of deltas ?? []) {
|