@axlsdk/axl 0.17.4 → 0.17.6
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 +3 -3
- package/dist/index.cjs +593 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +256 -7
- package/dist/index.d.ts +256 -7
- package/dist/index.js +594 -43
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -163,6 +163,7 @@ __export(index_exports, {
|
|
|
163
163
|
registerConditions: () => registerConditions,
|
|
164
164
|
resolveRuntime: () => resolveRuntime,
|
|
165
165
|
resolveThinkingOptions: () => resolveThinkingOptions,
|
|
166
|
+
stringStreamFromEvents: () => stringStreamFromEvents,
|
|
166
167
|
tool: () => tool,
|
|
167
168
|
workflow: () => workflow,
|
|
168
169
|
zodToJsonSchema: () => zodToJsonSchema
|
|
@@ -1006,6 +1007,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1006
1007
|
// src/providers/anthropic.ts
|
|
1007
1008
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
1008
1009
|
var ANTHROPIC_PRICING = {
|
|
1010
|
+
"claude-opus-4-7": [5e-6, 25e-6],
|
|
1009
1011
|
"claude-opus-4-6": [5e-6, 25e-6],
|
|
1010
1012
|
"claude-sonnet-4-6": [3e-6, 15e-6],
|
|
1011
1013
|
"claude-opus-4-5": [5e-6, 25e-6],
|
|
@@ -1050,13 +1052,21 @@ var THINKING_BUDGETS = {
|
|
|
1050
1052
|
max: 3e4
|
|
1051
1053
|
};
|
|
1052
1054
|
function supportsAdaptiveThinking(model) {
|
|
1053
|
-
return model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6");
|
|
1055
|
+
return model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6");
|
|
1054
1056
|
}
|
|
1055
1057
|
function supportsMaxEffort(model) {
|
|
1056
|
-
return model.startsWith("claude-opus-4-6");
|
|
1058
|
+
return model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6");
|
|
1059
|
+
}
|
|
1060
|
+
function supportsXhigh2(model) {
|
|
1061
|
+
return model.startsWith("claude-opus-4-7");
|
|
1057
1062
|
}
|
|
1058
1063
|
function supportsEffort(model) {
|
|
1059
|
-
return model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6") || model.startsWith("claude-opus-4-5");
|
|
1064
|
+
return model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6") || model.startsWith("claude-opus-4-5");
|
|
1065
|
+
}
|
|
1066
|
+
function clampAnthropicEffort(model, effort) {
|
|
1067
|
+
if (effort === "max" && !supportsMaxEffort(model)) return "high";
|
|
1068
|
+
if (effort === "xhigh" && !supportsXhigh2(model)) return "high";
|
|
1069
|
+
return effort;
|
|
1060
1070
|
}
|
|
1061
1071
|
var AnthropicProvider = class {
|
|
1062
1072
|
name = "anthropic";
|
|
@@ -1157,10 +1167,7 @@ var AnthropicProvider = class {
|
|
|
1157
1167
|
body.tool_choice = this.mapToolChoice(options.toolChoice);
|
|
1158
1168
|
}
|
|
1159
1169
|
const { thinkingBudget, thinkingDisabled, activeEffort, hasBudgetOverride } = resolveThinkingOptions(options);
|
|
1160
|
-
|
|
1161
|
-
if (resolvedEffort === "max" && !supportsMaxEffort(options.model)) {
|
|
1162
|
-
resolvedEffort = "high";
|
|
1163
|
-
}
|
|
1170
|
+
const resolvedEffort = activeEffort ? clampAnthropicEffort(options.model, activeEffort) : void 0;
|
|
1164
1171
|
if (hasBudgetOverride) {
|
|
1165
1172
|
body.thinking = { type: "enabled", budget_tokens: thinkingBudget };
|
|
1166
1173
|
const currentMax = body.max_tokens;
|
|
@@ -1527,6 +1534,7 @@ var GEMINI_PRICING = {
|
|
|
1527
1534
|
"gemini-3-pro-preview": [2e-6, 12e-6],
|
|
1528
1535
|
"gemini-3-flash-preview": [5e-7, 3e-6],
|
|
1529
1536
|
"gemini-3.1-pro-preview": [2e-6, 12e-6],
|
|
1537
|
+
"gemini-3.1-flash-lite": [25e-8, 15e-7],
|
|
1530
1538
|
"gemini-3.1-flash-lite-preview": [25e-8, 15e-7]
|
|
1531
1539
|
};
|
|
1532
1540
|
var GEMINI_PRICING_KEYS_BY_LENGTH = Object.keys(GEMINI_PRICING).sort(
|
|
@@ -1552,12 +1560,16 @@ var THINKING_BUDGETS2 = {
|
|
|
1552
1560
|
low: 1024,
|
|
1553
1561
|
medium: 5e3,
|
|
1554
1562
|
high: 1e4,
|
|
1563
|
+
xhigh: 16384,
|
|
1564
|
+
// between high (10000) and max (24576)
|
|
1555
1565
|
max: 24576
|
|
1556
1566
|
};
|
|
1557
1567
|
var THINKING_LEVELS = {
|
|
1558
1568
|
low: "low",
|
|
1559
1569
|
medium: "medium",
|
|
1560
1570
|
high: "high",
|
|
1571
|
+
xhigh: "high",
|
|
1572
|
+
// 3.x has no xhigh tier; clamp to 'high'
|
|
1561
1573
|
max: "high"
|
|
1562
1574
|
// 3.x caps at 'high'
|
|
1563
1575
|
};
|
|
@@ -2504,6 +2516,314 @@ var PartialParser = class {
|
|
|
2504
2516
|
}
|
|
2505
2517
|
};
|
|
2506
2518
|
|
|
2519
|
+
// src/streaming-walker.ts
|
|
2520
|
+
var MAX_DEPTH2 = 256;
|
|
2521
|
+
function encodeSegment(s) {
|
|
2522
|
+
if (typeof s === "number") return String(s);
|
|
2523
|
+
return s.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2524
|
+
}
|
|
2525
|
+
function computePath(stack) {
|
|
2526
|
+
let path = "";
|
|
2527
|
+
for (const frame of stack) {
|
|
2528
|
+
if (frame.type === "object") {
|
|
2529
|
+
path += "/" + encodeSegment(frame.currentKey ?? "");
|
|
2530
|
+
} else {
|
|
2531
|
+
path += "/" + encodeSegment(frame.currentIndex);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
return path;
|
|
2535
|
+
}
|
|
2536
|
+
var StreamingWalker = class {
|
|
2537
|
+
constructor(callbacks) {
|
|
2538
|
+
this.callbacks = callbacks;
|
|
2539
|
+
}
|
|
2540
|
+
state = "pre-json";
|
|
2541
|
+
stack = [];
|
|
2542
|
+
// String parsing state — survives across chunks so escapes can split.
|
|
2543
|
+
escaped = false;
|
|
2544
|
+
/** When > 0, we're collecting `\\uXXXX` hex digits. 4→0 as digits arrive. */
|
|
2545
|
+
unicodePending = 0;
|
|
2546
|
+
/** Accumulator for `\\uXXXX` digits. Cleared when the escape resolves. */
|
|
2547
|
+
unicodeBuf = "";
|
|
2548
|
+
// Per-string accumulators.
|
|
2549
|
+
keyBuf = "";
|
|
2550
|
+
deltaBuf = "";
|
|
2551
|
+
/** Cached JSON Pointer to the value currently being read. Recomputed
|
|
2552
|
+
* on entry to `read-value-string` (the only state that needs it). */
|
|
2553
|
+
currentPath = "";
|
|
2554
|
+
/** Set true when a structural boundary lands. Caller reads via
|
|
2555
|
+
* `consumeBoundary()` after each chunk to decide on partial_object emit. */
|
|
2556
|
+
boundaryPending = false;
|
|
2557
|
+
/** Walk `text` char-by-char, advancing state and accumulating deltas.
|
|
2558
|
+
* Calls `onStringDelta` at end-of-chunk for any pending value text. */
|
|
2559
|
+
processChunk(text) {
|
|
2560
|
+
for (let i = 0; i < text.length; i++) {
|
|
2561
|
+
if (this.state === "done") return;
|
|
2562
|
+
this.processChar(text[i]);
|
|
2563
|
+
}
|
|
2564
|
+
if (this.deltaBuf.length > 0 && this.state === "read-value-string") {
|
|
2565
|
+
this.callbacks.onStringDelta?.(this.currentPath, this.deltaBuf);
|
|
2566
|
+
this.deltaBuf = "";
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
/** Read-and-clear the structural-boundary flag. Caller checks this
|
|
2570
|
+
* after `processChunk` to decide whether to emit `partial_object`. */
|
|
2571
|
+
consumeBoundary() {
|
|
2572
|
+
const v = this.boundaryPending;
|
|
2573
|
+
this.boundaryPending = false;
|
|
2574
|
+
return v;
|
|
2575
|
+
}
|
|
2576
|
+
/** True when the walker has decided it's done with this stream
|
|
2577
|
+
* (root closed or fatal malformed input). Caller may use this to
|
|
2578
|
+
* short-circuit further work. */
|
|
2579
|
+
get isDone() {
|
|
2580
|
+
return this.state === "done";
|
|
2581
|
+
}
|
|
2582
|
+
// ── Per-char dispatch ───────────────────────────────────────────────────
|
|
2583
|
+
processChar(ch) {
|
|
2584
|
+
switch (this.state) {
|
|
2585
|
+
case "pre-json":
|
|
2586
|
+
if (ch === "{") {
|
|
2587
|
+
if (!this.pushFrame({ type: "object" })) return;
|
|
2588
|
+
this.state = "expect-key-or-close";
|
|
2589
|
+
}
|
|
2590
|
+
return;
|
|
2591
|
+
case "expect-key-or-close":
|
|
2592
|
+
if (isWhitespace(ch)) return;
|
|
2593
|
+
if (ch === "}") {
|
|
2594
|
+
this.popFrame();
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
if (ch === '"') {
|
|
2598
|
+
this.keyBuf = "";
|
|
2599
|
+
this.escaped = false;
|
|
2600
|
+
this.unicodePending = 0;
|
|
2601
|
+
this.unicodeBuf = "";
|
|
2602
|
+
this.state = "read-key";
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
this.state = "done";
|
|
2606
|
+
return;
|
|
2607
|
+
case "read-key":
|
|
2608
|
+
this.processStringChar(
|
|
2609
|
+
ch,
|
|
2610
|
+
/* isValue */
|
|
2611
|
+
false
|
|
2612
|
+
);
|
|
2613
|
+
return;
|
|
2614
|
+
case "expect-colon":
|
|
2615
|
+
if (isWhitespace(ch)) return;
|
|
2616
|
+
if (ch === ":") {
|
|
2617
|
+
this.state = "expect-value";
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
this.state = "done";
|
|
2621
|
+
return;
|
|
2622
|
+
case "expect-value":
|
|
2623
|
+
if (isWhitespace(ch)) return;
|
|
2624
|
+
if (ch === "{") {
|
|
2625
|
+
if (!this.pushFrame({ type: "object" })) return;
|
|
2626
|
+
this.state = "expect-key-or-close";
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
if (ch === "[") {
|
|
2630
|
+
if (!this.pushFrame({ type: "array", currentIndex: 0 })) return;
|
|
2631
|
+
this.state = "expect-value";
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
if (ch === "]") {
|
|
2635
|
+
this.popFrame();
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
if (ch === '"') {
|
|
2639
|
+
this.currentPath = computePath(this.stack);
|
|
2640
|
+
this.deltaBuf = "";
|
|
2641
|
+
this.escaped = false;
|
|
2642
|
+
this.unicodePending = 0;
|
|
2643
|
+
this.unicodeBuf = "";
|
|
2644
|
+
this.state = "read-value-string";
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
if (isValueStartChar(ch)) {
|
|
2648
|
+
this.state = "read-value-other";
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
this.state = "done";
|
|
2652
|
+
return;
|
|
2653
|
+
case "read-value-string":
|
|
2654
|
+
this.processStringChar(
|
|
2655
|
+
ch,
|
|
2656
|
+
/* isValue */
|
|
2657
|
+
true
|
|
2658
|
+
);
|
|
2659
|
+
return;
|
|
2660
|
+
case "read-value-other":
|
|
2661
|
+
if (ch === "," || ch === "}" || ch === "]" || isWhitespace(ch)) {
|
|
2662
|
+
this.state = "expect-comma-close";
|
|
2663
|
+
this.processChar(ch);
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
return;
|
|
2667
|
+
case "expect-comma-close":
|
|
2668
|
+
if (isWhitespace(ch)) return;
|
|
2669
|
+
if (ch === ",") {
|
|
2670
|
+
this.boundaryPending = true;
|
|
2671
|
+
const top = this.stack[this.stack.length - 1];
|
|
2672
|
+
if (!top) {
|
|
2673
|
+
this.state = "done";
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
2676
|
+
if (top.type === "object") {
|
|
2677
|
+
top.currentKey = void 0;
|
|
2678
|
+
this.state = "expect-key-or-close";
|
|
2679
|
+
} else {
|
|
2680
|
+
top.currentIndex += 1;
|
|
2681
|
+
this.state = "expect-value";
|
|
2682
|
+
}
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
if (ch === "}" || ch === "]") {
|
|
2686
|
+
this.popFrame();
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
this.state = "done";
|
|
2690
|
+
return;
|
|
2691
|
+
case "done":
|
|
2692
|
+
return;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
// ── String state machine (shared between key and value reads) ───────────
|
|
2696
|
+
/**
|
|
2697
|
+
* One char inside a string. Translates JSON escapes, accumulates
|
|
2698
|
+
* `\\uXXXX` digits across chunks, and routes the resolved char to
|
|
2699
|
+
* either `keyBuf` (when reading a key) or `deltaBuf` (when reading a
|
|
2700
|
+
* value).
|
|
2701
|
+
*/
|
|
2702
|
+
processStringChar(ch, isValue) {
|
|
2703
|
+
if (this.unicodePending > 0) {
|
|
2704
|
+
if (isHex(ch)) {
|
|
2705
|
+
this.unicodeBuf += ch;
|
|
2706
|
+
this.unicodePending -= 1;
|
|
2707
|
+
if (this.unicodePending === 0) {
|
|
2708
|
+
const code = parseInt(this.unicodeBuf, 16);
|
|
2709
|
+
this.unicodeBuf = "";
|
|
2710
|
+
this.appendChar(String.fromCharCode(code), isValue);
|
|
2711
|
+
}
|
|
2712
|
+
} else {
|
|
2713
|
+
this.state = "done";
|
|
2714
|
+
}
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
if (this.escaped) {
|
|
2718
|
+
this.escaped = false;
|
|
2719
|
+
switch (ch) {
|
|
2720
|
+
case '"':
|
|
2721
|
+
this.appendChar('"', isValue);
|
|
2722
|
+
return;
|
|
2723
|
+
case "\\":
|
|
2724
|
+
this.appendChar("\\", isValue);
|
|
2725
|
+
return;
|
|
2726
|
+
case "/":
|
|
2727
|
+
this.appendChar("/", isValue);
|
|
2728
|
+
return;
|
|
2729
|
+
case "b":
|
|
2730
|
+
this.appendChar("\b", isValue);
|
|
2731
|
+
return;
|
|
2732
|
+
case "f":
|
|
2733
|
+
this.appendChar("\f", isValue);
|
|
2734
|
+
return;
|
|
2735
|
+
case "n":
|
|
2736
|
+
this.appendChar("\n", isValue);
|
|
2737
|
+
return;
|
|
2738
|
+
case "r":
|
|
2739
|
+
this.appendChar("\r", isValue);
|
|
2740
|
+
return;
|
|
2741
|
+
case "t":
|
|
2742
|
+
this.appendChar(" ", isValue);
|
|
2743
|
+
return;
|
|
2744
|
+
case "u":
|
|
2745
|
+
this.unicodePending = 4;
|
|
2746
|
+
this.unicodeBuf = "";
|
|
2747
|
+
return;
|
|
2748
|
+
default:
|
|
2749
|
+
this.state = "done";
|
|
2750
|
+
return;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
if (ch === "\\") {
|
|
2754
|
+
this.escaped = true;
|
|
2755
|
+
return;
|
|
2756
|
+
}
|
|
2757
|
+
if (ch === '"') {
|
|
2758
|
+
if (isValue) {
|
|
2759
|
+
if (this.deltaBuf.length > 0) {
|
|
2760
|
+
this.callbacks.onStringDelta?.(this.currentPath, this.deltaBuf);
|
|
2761
|
+
this.deltaBuf = "";
|
|
2762
|
+
}
|
|
2763
|
+
this.state = "expect-comma-close";
|
|
2764
|
+
} else {
|
|
2765
|
+
const top = this.stack[this.stack.length - 1];
|
|
2766
|
+
if (!top || top.type !== "object") {
|
|
2767
|
+
this.state = "done";
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
top.currentKey = this.keyBuf;
|
|
2771
|
+
this.keyBuf = "";
|
|
2772
|
+
this.state = "expect-colon";
|
|
2773
|
+
}
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
this.appendChar(ch, isValue);
|
|
2777
|
+
}
|
|
2778
|
+
appendChar(c, isValue) {
|
|
2779
|
+
if (isValue) {
|
|
2780
|
+
this.deltaBuf += c;
|
|
2781
|
+
} else {
|
|
2782
|
+
this.keyBuf += c;
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
// ── Frame stack ─────────────────────────────────────────────────────────
|
|
2786
|
+
/** Push a frame onto the stack. Returns false if the stack is at the
|
|
2787
|
+
* depth cap — in which case the walker is also moved to `done` so the
|
|
2788
|
+
* caller's subsequent state assignment must be guarded. */
|
|
2789
|
+
pushFrame(frame) {
|
|
2790
|
+
if (this.stack.length >= MAX_DEPTH2) {
|
|
2791
|
+
this.state = "done";
|
|
2792
|
+
return false;
|
|
2793
|
+
}
|
|
2794
|
+
this.stack.push(frame);
|
|
2795
|
+
return true;
|
|
2796
|
+
}
|
|
2797
|
+
/** Pop a frame on `}` or `]`, fire boundary, transition to next state.
|
|
2798
|
+
* Defensive on empty stack — the state machine never invokes this
|
|
2799
|
+
* without an enclosing container today (callers gate on state, and
|
|
2800
|
+
* state transitions out of `pre-json` / `done` only after pushing),
|
|
2801
|
+
* but a future state-machine bug could land us here with nothing to
|
|
2802
|
+
* pop. Fail closed (move to `done`, no spurious boundary fire). */
|
|
2803
|
+
popFrame() {
|
|
2804
|
+
if (this.stack.length === 0) {
|
|
2805
|
+
this.state = "done";
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
this.stack.pop();
|
|
2809
|
+
this.boundaryPending = true;
|
|
2810
|
+
if (this.stack.length === 0) {
|
|
2811
|
+
this.state = "done";
|
|
2812
|
+
} else {
|
|
2813
|
+
this.state = "expect-comma-close";
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
};
|
|
2817
|
+
function isWhitespace(ch) {
|
|
2818
|
+
return ch === " " || ch === " " || ch === "\n" || ch === "\r";
|
|
2819
|
+
}
|
|
2820
|
+
function isHex(ch) {
|
|
2821
|
+
return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
|
|
2822
|
+
}
|
|
2823
|
+
function isValueStartChar(ch) {
|
|
2824
|
+
return ch === "-" || ch >= "0" && ch <= "9" || ch === "t" || ch === "f" || ch === "n";
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2507
2827
|
// src/event-utils.ts
|
|
2508
2828
|
var COST_BEARING_LEAF_TYPES = [
|
|
2509
2829
|
"agent_call_end",
|
|
@@ -2560,6 +2880,12 @@ var AXL_EVENT_TYPES = [
|
|
|
2560
2880
|
"pipeline",
|
|
2561
2881
|
// Progressive structured output — added in PR 2; reserved here.
|
|
2562
2882
|
"partial_object",
|
|
2883
|
+
// Character-level deltas inside string values of progressive structured
|
|
2884
|
+
// output. Same gating as `partial_object` (schema set, no tools, root is
|
|
2885
|
+
// ZodObject); fires per text-delta chunk when the walker is inside a
|
|
2886
|
+
// string value at a known JSON Pointer path. Designed for chat-style
|
|
2887
|
+
// typewriter rendering of long string fields.
|
|
2888
|
+
"string_delta",
|
|
2563
2889
|
// Verification
|
|
2564
2890
|
"verify",
|
|
2565
2891
|
// Observability
|
|
@@ -2657,6 +2983,27 @@ var AxlEventBus = class {
|
|
|
2657
2983
|
* number of distinct askIds that emitted at least one partial.
|
|
2658
2984
|
*/
|
|
2659
2985
|
latestPartialByAsk = /* @__PURE__ */ new Map();
|
|
2986
|
+
/**
|
|
2987
|
+
* Per-ask, per-path running accumulator for `string_delta` events,
|
|
2988
|
+
* updated on every `_push(string_delta)` regardless of consumer state.
|
|
2989
|
+
* Used to seed late `stringStream` subscribers with the current text-
|
|
2990
|
+
* so-far per (askId, path) pair, so a UI binding to `/summary` mid-
|
|
2991
|
+
* stream renders the field's current state on first event instead of
|
|
2992
|
+
* waiting for the next chunk.
|
|
2993
|
+
*
|
|
2994
|
+
* Cleared per-askId on `pipeline(failed)` (discarded attempt — old
|
|
2995
|
+
* deltas must not leak into the next attempt's view) and on `ask_end`
|
|
2996
|
+
* (frees memory; stream-event accumulators don't outlive the ask).
|
|
2997
|
+
*
|
|
2998
|
+
* Memory bound: O(active asks × distinct paths × total chars per
|
|
2999
|
+
* field). For typical chat workflows (1 ask, 1-3 streaming string
|
|
3000
|
+
* fields) this is a few KB. The clear-on-`ask_end` cap keeps a
|
|
3001
|
+
* long-running runtime from accumulating per-ask entries forever
|
|
3002
|
+
* (the way `latestPartialByAsk` does — partial snapshots are tiny by
|
|
3003
|
+
* comparison so that map's growth is acceptable; string accumulators
|
|
3004
|
+
* can be 4 KB+ each, hence the more aggressive cleanup here).
|
|
3005
|
+
*/
|
|
3006
|
+
stringStreamByAsk = /* @__PURE__ */ new Map();
|
|
2660
3007
|
/**
|
|
2661
3008
|
* Hook fired when an iterator obtained from `[Symbol.asyncIterator]`
|
|
2662
3009
|
* is `await using`-disposed. Set via the second constructor argument.
|
|
@@ -2960,6 +3307,141 @@ var AxlEventBus = class {
|
|
|
2960
3307
|
}
|
|
2961
3308
|
};
|
|
2962
3309
|
}
|
|
3310
|
+
/**
|
|
3311
|
+
* Listener-based view over `string_delta` events with optional
|
|
3312
|
+
* filtering by `path` and/or `askId`. Yields `StringStreamEvent`s
|
|
3313
|
+
* carrying `delta` (the new chars) and `accumulated` (the full text-
|
|
3314
|
+
* so-far for that ask + path).
|
|
3315
|
+
*
|
|
3316
|
+
* Designed for chat-style typewriter rendering of long string fields:
|
|
3317
|
+
* a React component binding to `path: '/summary'` sets its text to
|
|
3318
|
+
* `event.accumulated` on every yield, getting smooth char-by-char
|
|
3319
|
+
* progression with no parser of its own.
|
|
3320
|
+
*
|
|
3321
|
+
* Late-subscriber seeding: at subscribe time, the bus's per-ask
|
|
3322
|
+
* accumulator is walked and one synthetic `StringStreamEvent` is
|
|
3323
|
+
* yielded per matching (askId, path) entry, with `delta ===
|
|
3324
|
+
* accumulated === <full text-so-far>`. After that, live deltas flow
|
|
3325
|
+
* normally. A consumer who attaches mid-ask sees the field's current
|
|
3326
|
+
* state on first iteration, then incremental updates.
|
|
3327
|
+
*
|
|
3328
|
+
* Like `partialObjects`, this view is listener-based — it does NOT
|
|
3329
|
+
* race the main async iterator (`for await (const e of bus)`).
|
|
3330
|
+
* Running both concurrently each receives every matching event.
|
|
3331
|
+
*
|
|
3332
|
+
* Schema-retry awareness: on `pipeline(status: 'failed')`, the
|
|
3333
|
+
* accumulator and any pending events for the affected ask are
|
|
3334
|
+
* dropped, so a consumer iterating during attempt-N never observes
|
|
3335
|
+
* stale text after attempt-N+1 begins. Mirrors `partialObjects`'
|
|
3336
|
+
* per-attempt reset.
|
|
3337
|
+
*
|
|
3338
|
+
* Termination: yields any pending events first (live + seeded), then
|
|
3339
|
+
* `done: true` once the bus is finished via `_finish()`.
|
|
3340
|
+
*/
|
|
3341
|
+
stringStream(opts) {
|
|
3342
|
+
const self = this;
|
|
3343
|
+
const filterPath = opts?.path;
|
|
3344
|
+
const filterAskId = opts?.askId;
|
|
3345
|
+
if (filterPath !== void 0 && filterPath !== "" && !filterPath.startsWith("/")) {
|
|
3346
|
+
throw new Error(
|
|
3347
|
+
`AxlEventBus.stringStream({ path }) \u2014 path must start with '/' (RFC 6901 JSON Pointer); got ${JSON.stringify(filterPath)}. Examples: '/summary', '/sources/0/title'.`
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
return {
|
|
3351
|
+
[Symbol.asyncIterator]() {
|
|
3352
|
+
const pending = [];
|
|
3353
|
+
let resolveWaiter = null;
|
|
3354
|
+
let finished = self.finished;
|
|
3355
|
+
let unsubscribed = false;
|
|
3356
|
+
const wakeWaiter = () => {
|
|
3357
|
+
const r = resolveWaiter;
|
|
3358
|
+
resolveWaiter = null;
|
|
3359
|
+
r?.();
|
|
3360
|
+
};
|
|
3361
|
+
const matchesFilter = (askId, path) => {
|
|
3362
|
+
if (filterAskId !== void 0 && askId !== filterAskId) return false;
|
|
3363
|
+
if (filterPath !== void 0 && path !== filterPath) return false;
|
|
3364
|
+
return true;
|
|
3365
|
+
};
|
|
3366
|
+
for (const [askId, paths] of self.stringStreamByAsk) {
|
|
3367
|
+
for (const [path, acc] of paths) {
|
|
3368
|
+
if (!matchesFilter(askId, path)) continue;
|
|
3369
|
+
pending.push({
|
|
3370
|
+
askId,
|
|
3371
|
+
agent: acc.agent,
|
|
3372
|
+
path,
|
|
3373
|
+
delta: acc.text,
|
|
3374
|
+
accumulated: acc.text,
|
|
3375
|
+
attempt: acc.attempt
|
|
3376
|
+
});
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
const handler = (event) => {
|
|
3380
|
+
if (event.type !== "string_delta") return;
|
|
3381
|
+
const askId = event.askId ?? "";
|
|
3382
|
+
if (!matchesFilter(askId, event.data.path)) return;
|
|
3383
|
+
const paths = self.stringStreamByAsk.get(askId);
|
|
3384
|
+
const acc = paths?.get(event.data.path);
|
|
3385
|
+
const accumulated = acc?.text ?? event.data.delta;
|
|
3386
|
+
pending.push({
|
|
3387
|
+
askId,
|
|
3388
|
+
agent: event.agent,
|
|
3389
|
+
path: event.data.path,
|
|
3390
|
+
delta: event.data.delta,
|
|
3391
|
+
accumulated,
|
|
3392
|
+
attempt: event.attempt
|
|
3393
|
+
});
|
|
3394
|
+
wakeWaiter();
|
|
3395
|
+
};
|
|
3396
|
+
self.bus.on("string_delta", handler);
|
|
3397
|
+
const pipelineHandler = (event) => {
|
|
3398
|
+
if (event.type !== "pipeline") return;
|
|
3399
|
+
if (event.status !== "failed") return;
|
|
3400
|
+
const askId = event.askId ?? "";
|
|
3401
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
3402
|
+
if (pending[i].askId === askId) pending.splice(i, 1);
|
|
3403
|
+
}
|
|
3404
|
+
};
|
|
3405
|
+
self.bus.on("pipeline", pipelineHandler);
|
|
3406
|
+
const unsubscribe = () => {
|
|
3407
|
+
if (unsubscribed) return;
|
|
3408
|
+
unsubscribed = true;
|
|
3409
|
+
self.bus.off("string_delta", handler);
|
|
3410
|
+
self.bus.off("pipeline", pipelineHandler);
|
|
3411
|
+
unsubscribeFinish();
|
|
3412
|
+
};
|
|
3413
|
+
const unsubscribeFinish = self._onFinish(() => {
|
|
3414
|
+
finished = true;
|
|
3415
|
+
self.bus.off("string_delta", handler);
|
|
3416
|
+
self.bus.off("pipeline", pipelineHandler);
|
|
3417
|
+
wakeWaiter();
|
|
3418
|
+
});
|
|
3419
|
+
return {
|
|
3420
|
+
async next() {
|
|
3421
|
+
while (true) {
|
|
3422
|
+
if (pending.length > 0) {
|
|
3423
|
+
return { value: pending.shift(), done: false };
|
|
3424
|
+
}
|
|
3425
|
+
if (finished) {
|
|
3426
|
+
unsubscribe();
|
|
3427
|
+
return { value: void 0, done: true };
|
|
3428
|
+
}
|
|
3429
|
+
await new Promise((res) => {
|
|
3430
|
+
resolveWaiter = res;
|
|
3431
|
+
});
|
|
3432
|
+
}
|
|
3433
|
+
},
|
|
3434
|
+
async return() {
|
|
3435
|
+
unsubscribe();
|
|
3436
|
+
return { value: void 0, done: true };
|
|
3437
|
+
},
|
|
3438
|
+
[Symbol.asyncIterator]() {
|
|
3439
|
+
return this;
|
|
3440
|
+
}
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
2963
3445
|
/** Iterator over structural lifecycle events only — skips per-token chatter
|
|
2964
3446
|
* and progressive `partial_object` emissions. Useful for waterfall UIs
|
|
2965
3447
|
* and any consumer that wants the "what happened" timeline. */
|
|
@@ -2996,14 +3478,6 @@ var AxlEventBus = class {
|
|
|
2996
3478
|
*/
|
|
2997
3479
|
_push(event) {
|
|
2998
3480
|
if (this.finished) return;
|
|
2999
|
-
try {
|
|
3000
|
-
this.bus.emit(event.type, event);
|
|
3001
|
-
} catch (err) {
|
|
3002
|
-
console.error(
|
|
3003
|
-
`[axl] AxlEventBus listener for "${event.type}" threw; ignoring:`,
|
|
3004
|
-
err instanceof Error ? err.message : String(err)
|
|
3005
|
-
);
|
|
3006
|
-
}
|
|
3007
3481
|
if (event.type === "partial_object" && event.data?.object !== void 0) {
|
|
3008
3482
|
const askId = event.askId ?? "";
|
|
3009
3483
|
this.latestPartialByAsk.set(askId, {
|
|
@@ -3012,8 +3486,33 @@ var AxlEventBus = class {
|
|
|
3012
3486
|
object: event.data.object,
|
|
3013
3487
|
attempt: event.attempt
|
|
3014
3488
|
});
|
|
3489
|
+
} else if (event.type === "string_delta") {
|
|
3490
|
+
const askId = event.askId ?? "";
|
|
3491
|
+
let paths = this.stringStreamByAsk.get(askId);
|
|
3492
|
+
if (!paths) {
|
|
3493
|
+
paths = /* @__PURE__ */ new Map();
|
|
3494
|
+
this.stringStreamByAsk.set(askId, paths);
|
|
3495
|
+
}
|
|
3496
|
+
const existing = paths.get(event.data.path);
|
|
3497
|
+
paths.set(event.data.path, {
|
|
3498
|
+
agent: event.agent,
|
|
3499
|
+
text: (existing?.text ?? "") + event.data.delta,
|
|
3500
|
+
attempt: event.attempt
|
|
3501
|
+
});
|
|
3015
3502
|
} else if (event.type === "pipeline" && event.status === "failed") {
|
|
3016
|
-
|
|
3503
|
+
const askId = event.askId ?? "";
|
|
3504
|
+
this.latestPartialByAsk.delete(askId);
|
|
3505
|
+
this.stringStreamByAsk.delete(askId);
|
|
3506
|
+
} else if (event.type === "ask_end") {
|
|
3507
|
+
this.stringStreamByAsk.delete(event.askId ?? "");
|
|
3508
|
+
}
|
|
3509
|
+
try {
|
|
3510
|
+
this.bus.emit(event.type, event);
|
|
3511
|
+
} catch (err) {
|
|
3512
|
+
console.error(
|
|
3513
|
+
`[axl] AxlEventBus listener for "${event.type}" threw; ignoring:`,
|
|
3514
|
+
err instanceof Error ? err.message : String(err)
|
|
3515
|
+
);
|
|
3017
3516
|
}
|
|
3018
3517
|
const waiter = this.waiters.shift();
|
|
3019
3518
|
if (waiter) {
|
|
@@ -3231,6 +3730,9 @@ var REDACTION_RULES = {
|
|
|
3231
3730
|
handoff_return: passthrough,
|
|
3232
3731
|
pipeline: (e) => e.status === "failed" ? { ...e, reason: REDACTED } : e,
|
|
3233
3732
|
partial_object: (e) => ({ ...e, data: { ...e.data, object: REDACTED } }),
|
|
3733
|
+
// `path` is structural (schema-shape, no PII); `delta` is user content.
|
|
3734
|
+
// Scrub only the delta — preserving path keeps "which fields stream" telemetry.
|
|
3735
|
+
string_delta: (e) => ({ ...e, data: { ...e.data, delta: REDACTED } }),
|
|
3234
3736
|
verify: (e) => e.data.lastError !== void 0 ? { ...e, data: { ...e.data, lastError: REDACTED } } : e,
|
|
3235
3737
|
log: (e) => {
|
|
3236
3738
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
@@ -4074,36 +4576,26 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4074
4576
|
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
4075
4577
|
let streamProviderMetadata;
|
|
4076
4578
|
let thinkingContent = "";
|
|
4077
|
-
const
|
|
4579
|
+
const streamingObjectEnabled = !!options?.schema && toolDefs.length === 0 && options.schema instanceof import_zod.z.ZodObject;
|
|
4078
4580
|
const currentAttempt = schemaRetries + 1;
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4581
|
+
const walker = streamingObjectEnabled ? new StreamingWalker({
|
|
4582
|
+
onStringDelta: (path, delta) => {
|
|
4583
|
+
this.emitEvent({
|
|
4584
|
+
type: "string_delta",
|
|
4585
|
+
agent: agent2._name,
|
|
4586
|
+
attempt: currentAttempt,
|
|
4587
|
+
data: { path, delta }
|
|
4588
|
+
});
|
|
4589
|
+
}
|
|
4590
|
+
}) : void 0;
|
|
4082
4591
|
for await (const chunk of provider.stream(currentMessages, chatOptions)) {
|
|
4083
4592
|
if (chunk.type === "text_delta") {
|
|
4084
4593
|
content2 += chunk.content;
|
|
4085
4594
|
this.emitEvent({ type: "token", data: chunk.content });
|
|
4086
4595
|
this.onToken?.(chunk.content, callbackMeta);
|
|
4087
|
-
if (
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
escaped = false;
|
|
4091
|
-
continue;
|
|
4092
|
-
}
|
|
4093
|
-
if (ch === "\\") {
|
|
4094
|
-
escaped = true;
|
|
4095
|
-
continue;
|
|
4096
|
-
}
|
|
4097
|
-
if (ch === '"') {
|
|
4098
|
-
inString = !inString;
|
|
4099
|
-
continue;
|
|
4100
|
-
}
|
|
4101
|
-
if (!inString && (ch === "," || ch === "}" || ch === "]")) {
|
|
4102
|
-
boundaryPending = true;
|
|
4103
|
-
}
|
|
4104
|
-
}
|
|
4105
|
-
if (boundaryPending) {
|
|
4106
|
-
boundaryPending = false;
|
|
4596
|
+
if (walker) {
|
|
4597
|
+
walker.processChunk(chunk.content);
|
|
4598
|
+
if (walker.consumeBoundary()) {
|
|
4107
4599
|
let parsed;
|
|
4108
4600
|
try {
|
|
4109
4601
|
parsed = parsePartialJson(extractJson(content2));
|
|
@@ -6912,6 +7404,20 @@ var AxlStream = class extends import_node_stream.Readable {
|
|
|
6912
7404
|
get partialObjects() {
|
|
6913
7405
|
return this.events.partialObjects;
|
|
6914
7406
|
}
|
|
7407
|
+
/** Listener-based view over `string_delta` events, with optional filter
|
|
7408
|
+
* on `path` and/or `askId`. Yields `StringStreamEvent`s carrying the new
|
|
7409
|
+
* `delta` and full text-so-far `accumulated`. Designed for chat-style
|
|
7410
|
+
* typewriter rendering of long string fields — bind a UI component to
|
|
7411
|
+
* one path and set its text to `event.accumulated` per yield.
|
|
7412
|
+
*
|
|
7413
|
+
* Late subscribers receive a synthetic event seeded from the bus's
|
|
7414
|
+
* per-ask accumulator (so the field's current state renders on the
|
|
7415
|
+
* first iteration, not after the next char arrives). Doesn't race the
|
|
7416
|
+
* main async iterator. See `AxlEventBus.stringStream` for full
|
|
7417
|
+
* semantics. */
|
|
7418
|
+
stringStream(opts) {
|
|
7419
|
+
return this.events.stringStream(opts);
|
|
7420
|
+
}
|
|
6915
7421
|
pipe(destination, options) {
|
|
6916
7422
|
const shouldEnd = options?.end !== false;
|
|
6917
7423
|
this.events.on("token", (event) => {
|
|
@@ -7567,7 +8073,6 @@ var NoopSpanManager = class {
|
|
|
7567
8073
|
};
|
|
7568
8074
|
|
|
7569
8075
|
// src/telemetry/index.ts
|
|
7570
|
-
init_span_manager();
|
|
7571
8076
|
async function createSpanManager(config) {
|
|
7572
8077
|
if (!config?.enabled) {
|
|
7573
8078
|
return new NoopSpanManager();
|
|
@@ -8595,7 +9100,9 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
8595
9100
|
workflowCalls.set(event.workflow, (workflowCalls.get(event.workflow) ?? 0) + 1);
|
|
8596
9101
|
}
|
|
8597
9102
|
if (capturedTraces) {
|
|
8598
|
-
if (event.type === "token" || event.type === "partial_object")
|
|
9103
|
+
if (event.type === "token" || event.type === "partial_object" || event.type === "string_delta") {
|
|
9104
|
+
return;
|
|
9105
|
+
}
|
|
8599
9106
|
if (event.type === "agent_call_start" && event.data) {
|
|
8600
9107
|
const d = event.data;
|
|
8601
9108
|
if ("messages" in d) {
|
|
@@ -8919,6 +9426,49 @@ async function registerConditions(conditions) {
|
|
|
8919
9426
|
}
|
|
8920
9427
|
}
|
|
8921
9428
|
|
|
9429
|
+
// src/string-stream-from-events.ts
|
|
9430
|
+
async function* stringStreamFromEvents(source, opts) {
|
|
9431
|
+
const filterPath = opts?.path;
|
|
9432
|
+
const filterAskId = opts?.askId;
|
|
9433
|
+
if (filterPath !== void 0 && filterPath !== "" && !filterPath.startsWith("/")) {
|
|
9434
|
+
throw new Error(
|
|
9435
|
+
`stringStreamFromEvents({ path }) \u2014 path must start with '/' (RFC 6901 JSON Pointer); got ${JSON.stringify(filterPath)}. Examples: '/summary', '/sources/0/title'.`
|
|
9436
|
+
);
|
|
9437
|
+
}
|
|
9438
|
+
const acc = /* @__PURE__ */ new Map();
|
|
9439
|
+
for await (const event of source) {
|
|
9440
|
+
if (event.type === "string_delta") {
|
|
9441
|
+
const askId = event.askId ?? "";
|
|
9442
|
+
let paths = acc.get(askId);
|
|
9443
|
+
if (!paths) {
|
|
9444
|
+
paths = /* @__PURE__ */ new Map();
|
|
9445
|
+
acc.set(askId, paths);
|
|
9446
|
+
}
|
|
9447
|
+
const existing = paths.get(event.data.path);
|
|
9448
|
+
const accumulated = (existing?.text ?? "") + event.data.delta;
|
|
9449
|
+
paths.set(event.data.path, {
|
|
9450
|
+
agent: event.agent,
|
|
9451
|
+
text: accumulated,
|
|
9452
|
+
attempt: event.attempt
|
|
9453
|
+
});
|
|
9454
|
+
if (filterAskId !== void 0 && askId !== filterAskId) continue;
|
|
9455
|
+
if (filterPath !== void 0 && event.data.path !== filterPath) continue;
|
|
9456
|
+
yield {
|
|
9457
|
+
askId,
|
|
9458
|
+
agent: event.agent,
|
|
9459
|
+
path: event.data.path,
|
|
9460
|
+
delta: event.data.delta,
|
|
9461
|
+
accumulated,
|
|
9462
|
+
attempt: event.attempt
|
|
9463
|
+
};
|
|
9464
|
+
} else if (event.type === "pipeline" && event.status === "failed") {
|
|
9465
|
+
acc.delete(event.askId ?? "");
|
|
9466
|
+
} else if (event.type === "ask_end") {
|
|
9467
|
+
acc.delete(event.askId ?? "");
|
|
9468
|
+
}
|
|
9469
|
+
}
|
|
9470
|
+
}
|
|
9471
|
+
|
|
8922
9472
|
// src/state/redis.ts
|
|
8923
9473
|
var RedisStore = class _RedisStore {
|
|
8924
9474
|
constructor(client) {
|
|
@@ -9324,6 +9874,7 @@ function cosineSimilarity2(a, b) {
|
|
|
9324
9874
|
registerConditions,
|
|
9325
9875
|
resolveRuntime,
|
|
9326
9876
|
resolveThinkingOptions,
|
|
9877
|
+
stringStreamFromEvents,
|
|
9327
9878
|
tool,
|
|
9328
9879
|
workflow,
|
|
9329
9880
|
zodToJsonSchema
|