llmz 0.6.4 → 0.6.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/DOCS.md +89 -0
- package/dist/chat.d.ts +29 -4
- package/dist/context.d.ts +12 -0
- package/dist/{custom-client-NUoxrFQh.js → custom-client-CJIThG2H.js} +14 -1
- package/dist/{custom-client-BPmEoWKu.cjs → custom-client-Cr33K60k.cjs} +14 -1
- package/dist/{execute-BEaq3jkV.cjs → execute-BmEjJjka.cjs} +102 -45
- package/dist/{execute-DYvFRs54.js → execute-sn9RT_IN.js} +102 -45
- package/dist/index.cjs +6 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/index.workerd.js +116 -43
- package/dist/runtime/generate.d.ts +4 -3
- package/dist/runtime/types.d.ts +10 -0
- package/dist/{tool-CAo-BQHs.js → tool-Bv8N4IPj.js} +4 -1
- package/dist/{tool-DdPZb0JW.cjs → tool-ReA9gxMp.cjs} +4 -1
- package/dist/tool.d.ts +2 -0
- package/dist/types.d.ts +8 -1
- package/package.json +1 -1
package/DOCS.md
CHANGED
|
@@ -1582,6 +1582,95 @@ await execute({
|
|
|
1582
1582
|
|
|
1583
1583
|
---
|
|
1584
1584
|
|
|
1585
|
+
### Mid-Stream Model Fallback (midStreamFallback)
|
|
1586
|
+
|
|
1587
|
+
By default, LLMz streams responses: message bodies (`■send` blocks) and completed `Chat.handler` sends are dispatched as soon as they are produced, and the ■run block starts executing while the model is still generating the rest of the response. This gives the lowest time-to-first-token but runs code before the response is complete.
|
|
1588
|
+
|
|
1589
|
+
Set `options.midStreamFallback: true` to allow Cognitive to restart a failed stream on another model. There is **no buffered delivery and no final delivery queue**: with fallback enabled, delta text and completed `Chat.handler` sends both stream immediately during generation. Only **TOOL/CODE execution** waits for a stream to complete successfully. When Cognitive abandons an attempt, a reset-only delta (`restart: true`) is delivered through the existing `Chat.onMessageDelta`; it invalidates **all** messages of the current iteration — **including messages already committed through `handler`** — so consumers must retract and replace them:
|
|
1590
|
+
|
|
1591
|
+
```typescript
|
|
1592
|
+
await execute({
|
|
1593
|
+
// ...
|
|
1594
|
+
chat: new MyChat({
|
|
1595
|
+
onMessageDelta: (delta) => {
|
|
1596
|
+
if (delta.restart) {
|
|
1597
|
+
// invalidates ALL current-iteration messages, including completed handler
|
|
1598
|
+
// sends: retract/replace them before the replacement streams
|
|
1599
|
+
return retractIteration(delta.iterationId)
|
|
1600
|
+
} else {
|
|
1601
|
+
// stream this message's text as it arrives (same iterationId as below)
|
|
1602
|
+
updateMessage(delta)
|
|
1603
|
+
}
|
|
1604
|
+
},
|
|
1605
|
+
}),
|
|
1606
|
+
options: { midStreamFallback: true },
|
|
1607
|
+
})
|
|
1608
|
+
```
|
|
1609
|
+
|
|
1610
|
+
`retractIteration`/`updateMessage` are placeholder names for your consumer's message store. **This option is not safe to enable blindly**: a reset invalidates messages that were already delivered through `handler`. If your transport cannot retract or replace an already-sent message — an external irreversible send such as SMS, email, or a third-party webhook — those sends cannot be undone, so keep `midStreamFallback` disabled unless your consumer can retract and replace invalidated messages.
|
|
1611
|
+
|
|
1612
|
+
#### MessageDelta contract
|
|
1613
|
+
|
|
1614
|
+
`Chat.onMessageDelta` receives a single typed `MessageDelta` discriminated union:
|
|
1615
|
+
|
|
1616
|
+
- **Ordinary text chunk** (`restart: false`) — the next piece of one message's body; all fields are required:
|
|
1617
|
+
```typescript
|
|
1618
|
+
{
|
|
1619
|
+
restart: false,
|
|
1620
|
+
iterationId: string, // stable across ALL attempts of this LLM generation iteration
|
|
1621
|
+
id: string, // message id — DIFFERENT per attempt (unique when fallback is on)
|
|
1622
|
+
component: string,
|
|
1623
|
+
props: Record<string, unknown>, // final by the time the body starts streaming
|
|
1624
|
+
delta: string, // new chunk of body text
|
|
1625
|
+
content: string, // full body text accumulated so far, including this chunk
|
|
1626
|
+
}
|
|
1627
|
+
```
|
|
1628
|
+
- **Reset-only delta** (`restart: true`) — carries no text; signals the current generation attempt was abandoned and a replacement is being attempted:
|
|
1629
|
+
```typescript
|
|
1630
|
+
{
|
|
1631
|
+
restart: true,
|
|
1632
|
+
iterationId: string, // the iteration whose messages (incl. handler sends) must be retracted/replaced
|
|
1633
|
+
attempt: number,
|
|
1634
|
+
fromModel: string,
|
|
1635
|
+
toModel: string,
|
|
1636
|
+
reason: string,
|
|
1637
|
+
}
|
|
1638
|
+
```
|
|
1639
|
+
|
|
1640
|
+
IDs: `iterationId` is **stable across attempts** — every restart of the same iteration keeps the same `iterationId` — while `id` is **different per attempt**. After a reset, replacement chunks arrive under new `id`s within the same `iterationId`.
|
|
1641
|
+
|
|
1642
|
+
Consumer rules:
|
|
1643
|
+
|
|
1644
|
+
- On `restart: true`, retract/replace **all** current-iteration messages bearing that `iterationId` — streamed text **and** messages already delivered through `handler`.
|
|
1645
|
+
- On an ordinary chunk, update the message for `delta.id` (create it if new, otherwise append).
|
|
1646
|
+
- A reset is emitted and awaited **before** replacement output begins, **even if the replacement yields no message at all**. Return/await the retraction promise in your handler. If it throws or rejects, generation fails without delivering replacement output or executing code; ordinary text-preview errors remain best-effort.
|
|
1647
|
+
- A reset only invalidates the messages of its own `iterationId`; it **never** invalidates messages from earlier, completed iterations.
|
|
1648
|
+
- Only TOOL/CODE execution is gated on a successful stream: generated code and tool calls never run from an abandoned attempt.
|
|
1649
|
+
|
|
1650
|
+
Outside of fallback (the default), `onMessageDelta` behavior is unchanged: ordinary chunks carry `iterationId`/`id`/`component`/`props`/`delta`/`content` with a single stable message id, and no restart/reset deltas are produced.
|
|
1651
|
+
|
|
1652
|
+
#### Completed-send correlation
|
|
1653
|
+
|
|
1654
|
+
`Chat.handler(component, metadata)` receives `{ iterationId: string, id: string }` for **every send**, including empty-body and non-text components, without requiring `onMessageDelta`. For streamed messages, `metadata.id` matches the text delta's `id`; it changes across attempts while `iterationId` stays stable. Existing one-argument handlers continue to work.
|
|
1655
|
+
|
|
1656
|
+
Record **all** persisted message IDs created while awaiting that handler under its metadata (one component may produce multiple persisted messages). A restart can then retract everything for the matching iteration, even after a message's stream tracking has completed. Non-streaming sends also receive metadata. Tool-yielded messages get unique per-yield IDs and the runtime iteration ID; standalone `Tool.execute` calls without an iteration use their `callId` as the scope.
|
|
1657
|
+
|
|
1658
|
+
#### Migrating consumers
|
|
1659
|
+
|
|
1660
|
+
- **No final delivery queue**: messages are not withheld until the stream ends. Deltas and completed `handler` sends arrive during generation; drop any "promote on success" design — treat every delivered message as provisional until the iteration completes without a reset.
|
|
1661
|
+
- **Retract, don't just clear previews**: on `restart: true`, retraction must cascade to every current-iteration message, including ones already committed through `handler`. If your UI or transport cannot do that, keep `midStreamFallback` disabled.
|
|
1662
|
+
- **Clear on errors and cancellation**: a failed or cancelled generation can leave current-iteration messages on screen, incomplete, or unresettable. Explicitly clear/roll back current-iteration state when execution errors or ends — there is no exactly-once guarantee, and caller retries can duplicate effects.
|
|
1663
|
+
- **Irreversible transports**: external sends that cannot be undone (SMS, email, webhooks) must not blindly enable fallback without a reset-capable gateway.
|
|
1664
|
+
|
|
1665
|
+
#### Caveats
|
|
1666
|
+
|
|
1667
|
+
- **No extra retry loop**: Cognitive owns fallback, bounded by the model chain. LLMz does not retry abandoned attempts itself.
|
|
1668
|
+
- **Cancellation**: cancellation still aborts the original generation request as usual; consumers must clear current-iteration state on abort (see above). Cancellation deadlines are not reset, and the existing stream inactivity guard remains active during handoffs.
|
|
1669
|
+
|
|
1670
|
+
This option defaults to false and applies only to streaming clients. Non-streaming Cognitive requests already fall back transparently. It can be combined with `maxTimeToFirstToken` and `transcriptionModel`; do not enable it globally underneath LLMz without also enabling LLMz's fallback option and a reset-capable consumer.
|
|
1671
|
+
|
|
1672
|
+
Restarts emit `llm_call_restarted` traces with the attempt, source/destination models, and reason. Token timings remain relative to the original request and describe the surviving attempt (including time spent on prior attempts). Usage and cost retain Cognitive's final reported metadata; LLMz does not infer or sum abandoned-attempt billing.
|
|
1673
|
+
|
|
1585
1674
|
## API Reference
|
|
1586
1675
|
|
|
1587
1676
|
### Core Functions
|
package/dist/chat.d.ts
CHANGED
|
@@ -24,7 +24,14 @@ import { TranscriptArray, Transcript } from './transcript.js';
|
|
|
24
24
|
* }
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
|
-
|
|
27
|
+
/** Correlates every generated send with its deltas and restart scope. */
|
|
28
|
+
export type MessageMetadata = {
|
|
29
|
+
/** Stable across generation attempts; standalone tool calls use their callId. */
|
|
30
|
+
iterationId: string;
|
|
31
|
+
/** Logical send ID; matches MessageDelta.id when text deltas exist. Unique across attempts. */
|
|
32
|
+
id: string;
|
|
33
|
+
};
|
|
34
|
+
export type MessageHandler = (input: RenderedComponent, metadata: MessageMetadata) => Promise<void> | void;
|
|
28
35
|
/**
|
|
29
36
|
* A chunk of a message body streamed live from the LLM, before the message is complete.
|
|
30
37
|
*
|
|
@@ -32,10 +39,14 @@ export type MessageHandler = (input: RenderedComponent) => Promise<void> | void;
|
|
|
32
39
|
* Each chunk is forwarded to {@link Chat} `onMessageDelta` as soon as it is parsed, so the
|
|
33
40
|
* client can render the message progressively (e.g. typewriter effect in a chat UI).
|
|
34
41
|
*
|
|
35
|
-
*
|
|
36
|
-
* are
|
|
42
|
+
* In mid-stream fallback mode, both deltas and completed `handler` messages
|
|
43
|
+
* are live and retractable: restart deltas invalidate every message from that
|
|
44
|
+
* iteration. Consumers must retract them; only code execution waits for success.
|
|
37
45
|
*/
|
|
38
46
|
export type MessageDelta = {
|
|
47
|
+
restart: false;
|
|
48
|
+
/** Generation whose provisional messages must be cleared on a restart. */
|
|
49
|
+
iterationId: string;
|
|
39
50
|
/** Identifies the message being streamed. Unique within an `execute()` call. */
|
|
40
51
|
id: string;
|
|
41
52
|
/** Component name of the message being streamed (e.g. 'message', 'button'). */
|
|
@@ -46,10 +57,24 @@ export type MessageDelta = {
|
|
|
46
57
|
delta: string;
|
|
47
58
|
/** The full body text accumulated so far, including this chunk. */
|
|
48
59
|
content: string;
|
|
60
|
+
} | {
|
|
61
|
+
/**
|
|
62
|
+
* Reset-only delta, emitted before replacement text (even if none follows).
|
|
63
|
+
* Clear ALL messages for iterationId, including completed `handler` sends.
|
|
64
|
+
* Other iterations are unaffected. Irreversible sends cannot be undone.
|
|
65
|
+
*/
|
|
66
|
+
restart: true;
|
|
67
|
+
iterationId: string;
|
|
68
|
+
/** 1-based index of the new attempt. */
|
|
69
|
+
attempt: number;
|
|
70
|
+
fromModel: string;
|
|
71
|
+
toModel: string;
|
|
72
|
+
reason: string;
|
|
49
73
|
};
|
|
50
74
|
/**
|
|
51
75
|
* Function type for handling message chunks as they are streamed from the LLM.
|
|
52
|
-
*
|
|
76
|
+
* Text-preview errors are ignored. Restart-handler errors stop generation:
|
|
77
|
+
* replacement output must not be delivered if retraction fails.
|
|
53
78
|
*/
|
|
54
79
|
export type MessageDeltaHandler = (delta: MessageDelta) => Promise<void> | void;
|
|
55
80
|
/**
|
package/dist/context.d.ts
CHANGED
|
@@ -564,6 +564,17 @@ export declare class Context implements Serializable<Context.JSON> {
|
|
|
564
564
|
* before the cognitive service falls back to the next model/provider.
|
|
565
565
|
*/
|
|
566
566
|
maxTimeToFirstToken?: number;
|
|
567
|
+
/**
|
|
568
|
+
* Allow Cognitive to restart a failed stream on another model. With fallback
|
|
569
|
+
* enabled, there is no buffered delivery and no final delivery queue: delta
|
|
570
|
+
* text and completed Chat.handler sends stream immediately during generation.
|
|
571
|
+
* A reset-only delta (restart: true) on Chat.onMessageDelta invalidates ALL
|
|
572
|
+
* current-iteration messages, including completed sends — consumers must
|
|
573
|
+
* retract/replace them, which is unsafe for irreversible external transports.
|
|
574
|
+
* Only TOOL/CODE execution waits for a successful stream. Streaming-only;
|
|
575
|
+
* defaults to false, which leaves progressive sending unchanged.
|
|
576
|
+
*/
|
|
577
|
+
midStreamFallback?: boolean;
|
|
567
578
|
/**
|
|
568
579
|
* STT model used by the cognitive service to transcribe audio attachments
|
|
569
580
|
* when the target LLM does not support audio natively. Defaults to 'fast'.
|
|
@@ -600,6 +611,7 @@ export declare class Context implements Serializable<Context.JSON> {
|
|
|
600
611
|
timeout?: number;
|
|
601
612
|
maxTokens?: number;
|
|
602
613
|
maxTimeToFirstToken?: number;
|
|
614
|
+
midStreamFallback?: boolean;
|
|
603
615
|
transcriptionModel?: SttModels;
|
|
604
616
|
});
|
|
605
617
|
toJSON(): {
|
|
@@ -4,7 +4,7 @@ import { f as flatRest, l as extractType, m as baseCreate, p as copyArray, t as
|
|
|
4
4
|
import { _ as getSymbolsIn, b as keysIn, g as getAllKeysIn, o as getTokenizer, x as assignValue, y as baseSlice } from "./utils-UBeAFeEI.js";
|
|
5
5
|
import { t as getPrototype } from "./_getPrototype-CG7Dxrrz.js";
|
|
6
6
|
import { t as isPlainObject } from "./isPlainObject-D39AzPO2.js";
|
|
7
|
-
import { t as Tool } from "./tool-
|
|
7
|
+
import { t as Tool } from "./tool-Bv8N4IPj.js";
|
|
8
8
|
import { o as LoopExceededError } from "./errors-Cg1G0Awu.js";
|
|
9
9
|
import { t as Exit } from "./exit-CDdxDBRT.js";
|
|
10
10
|
import { n as assertValidComponent } from "./component-DGG7d_Mz.js";
|
|
@@ -2254,6 +2254,17 @@ var Context = class {
|
|
|
2254
2254
|
*/
|
|
2255
2255
|
maxTimeToFirstToken;
|
|
2256
2256
|
/**
|
|
2257
|
+
* Allow Cognitive to restart a failed stream on another model. With fallback
|
|
2258
|
+
* enabled, there is no buffered delivery and no final delivery queue: delta
|
|
2259
|
+
* text and completed Chat.handler sends stream immediately during generation.
|
|
2260
|
+
* A reset-only delta (restart: true) on Chat.onMessageDelta invalidates ALL
|
|
2261
|
+
* current-iteration messages, including completed sends — consumers must
|
|
2262
|
+
* retract/replace them, which is unsafe for irreversible external transports.
|
|
2263
|
+
* Only TOOL/CODE execution waits for a successful stream. Streaming-only;
|
|
2264
|
+
* defaults to false, which leaves progressive sending unchanged.
|
|
2265
|
+
*/
|
|
2266
|
+
midStreamFallback;
|
|
2267
|
+
/**
|
|
2257
2268
|
* STT model used by the cognitive service to transcribe audio attachments
|
|
2258
2269
|
* when the target LLM does not support audio natively. Defaults to 'fast'.
|
|
2259
2270
|
*/
|
|
@@ -2509,10 +2520,12 @@ var Context = class {
|
|
|
2509
2520
|
this.snapshot = props.snapshot;
|
|
2510
2521
|
this.maxTokens = props.maxTokens;
|
|
2511
2522
|
this.maxTimeToFirstToken = props.maxTimeToFirstToken;
|
|
2523
|
+
this.midStreamFallback = props.midStreamFallback;
|
|
2512
2524
|
this.transcriptionModel = props.transcriptionModel;
|
|
2513
2525
|
if (this.loop < 1 || this.loop > 100) throw new Error("Invalid loop. Expected a number between 1 and 100.");
|
|
2514
2526
|
if (this.maxTokens !== void 0 && (!Number.isFinite(this.maxTokens) || this.maxTokens < 1)) throw new Error("Invalid maxTokens. Expected a positive number.");
|
|
2515
2527
|
if (this.maxTimeToFirstToken !== void 0 && (!Number.isFinite(this.maxTimeToFirstToken) || this.maxTimeToFirstToken < 1)) throw new Error("Invalid maxTimeToFirstToken. Expected a positive number of milliseconds.");
|
|
2528
|
+
if (this.midStreamFallback !== void 0 && typeof this.midStreamFallback !== "boolean") throw new Error("Invalid midStreamFallback. Expected a boolean.");
|
|
2516
2529
|
}
|
|
2517
2530
|
toJSON() {
|
|
2518
2531
|
var _this$snapshot4;
|
|
@@ -4,7 +4,7 @@ const require_dual_modes = require("./dual-modes-wAOAlaBS.cjs");
|
|
|
4
4
|
const require_utils = require("./utils-ZpJm00ed.cjs");
|
|
5
5
|
const require__getPrototype = require("./_getPrototype-DQDlNVVM.cjs");
|
|
6
6
|
const require_isPlainObject = require("./isPlainObject-CSxXVKCk.cjs");
|
|
7
|
-
const require_tool = require("./tool-
|
|
7
|
+
const require_tool = require("./tool-ReA9gxMp.cjs");
|
|
8
8
|
const require_errors = require("./errors-C962uJdS.cjs");
|
|
9
9
|
const require_exit = require("./exit-BGi0Kk0y.cjs");
|
|
10
10
|
const require_component = require("./component-BwixNVH8.cjs");
|
|
@@ -2254,6 +2254,17 @@ var Context = class {
|
|
|
2254
2254
|
*/
|
|
2255
2255
|
maxTimeToFirstToken;
|
|
2256
2256
|
/**
|
|
2257
|
+
* Allow Cognitive to restart a failed stream on another model. With fallback
|
|
2258
|
+
* enabled, there is no buffered delivery and no final delivery queue: delta
|
|
2259
|
+
* text and completed Chat.handler sends stream immediately during generation.
|
|
2260
|
+
* A reset-only delta (restart: true) on Chat.onMessageDelta invalidates ALL
|
|
2261
|
+
* current-iteration messages, including completed sends — consumers must
|
|
2262
|
+
* retract/replace them, which is unsafe for irreversible external transports.
|
|
2263
|
+
* Only TOOL/CODE execution waits for a successful stream. Streaming-only;
|
|
2264
|
+
* defaults to false, which leaves progressive sending unchanged.
|
|
2265
|
+
*/
|
|
2266
|
+
midStreamFallback;
|
|
2267
|
+
/**
|
|
2257
2268
|
* STT model used by the cognitive service to transcribe audio attachments
|
|
2258
2269
|
* when the target LLM does not support audio natively. Defaults to 'fast'.
|
|
2259
2270
|
*/
|
|
@@ -2509,10 +2520,12 @@ var Context = class {
|
|
|
2509
2520
|
this.snapshot = props.snapshot;
|
|
2510
2521
|
this.maxTokens = props.maxTokens;
|
|
2511
2522
|
this.maxTimeToFirstToken = props.maxTimeToFirstToken;
|
|
2523
|
+
this.midStreamFallback = props.midStreamFallback;
|
|
2512
2524
|
this.transcriptionModel = props.transcriptionModel;
|
|
2513
2525
|
if (this.loop < 1 || this.loop > 100) throw new Error("Invalid loop. Expected a number between 1 and 100.");
|
|
2514
2526
|
if (this.maxTokens !== void 0 && (!Number.isFinite(this.maxTokens) || this.maxTokens < 1)) throw new Error("Invalid maxTokens. Expected a positive number.");
|
|
2515
2527
|
if (this.maxTimeToFirstToken !== void 0 && (!Number.isFinite(this.maxTimeToFirstToken) || this.maxTimeToFirstToken < 1)) throw new Error("Invalid maxTimeToFirstToken. Expected a positive number of milliseconds.");
|
|
2528
|
+
if (this.midStreamFallback !== void 0 && typeof this.midStreamFallback !== "boolean") throw new Error("Invalid midStreamFallback. Expected a boolean.");
|
|
2516
2529
|
}
|
|
2517
2530
|
toJSON() {
|
|
2518
2531
|
var _this$snapshot4;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require__baseForOwn = require("./_baseForOwn-buOZDFbK.cjs");
|
|
2
2
|
const require_dual_modes = require("./dual-modes-wAOAlaBS.cjs");
|
|
3
|
-
const require_custom_client = require("./custom-client-
|
|
3
|
+
const require_custom_client = require("./custom-client-Cr33K60k.cjs");
|
|
4
4
|
const require_utils = require("./utils-ZpJm00ed.cjs");
|
|
5
5
|
const require_jsx = require("./jsx-BWvT6ukg.cjs");
|
|
6
6
|
const require_stack_traces = require("./stack-traces-ntw_qhoy.cjs");
|
|
@@ -121,7 +121,7 @@ const RESPONSE_LENGTH_BUFFER = {
|
|
|
121
121
|
PERCENTAGE: .1
|
|
122
122
|
};
|
|
123
123
|
/** Maximum time to wait between two stream chunks before considering the stream stalled. */
|
|
124
|
-
const STREAM_INACTIVITY_TIMEOUT =
|
|
124
|
+
const STREAM_INACTIVITY_TIMEOUT = 18e4;
|
|
125
125
|
const getModelOutputLimit = (inputLength) => clamp(RESPONSE_LENGTH_BUFFER.PERCENTAGE * inputLength, RESPONSE_LENGTH_BUFFER.MIN_TOKENS, RESPONSE_LENGTH_BUFFER.MAX_TOKENS);
|
|
126
126
|
/**
|
|
127
127
|
* Models sometimes wrap their whole response in a code fence. The fence has to
|
|
@@ -197,6 +197,19 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
197
197
|
/** Milliseconds between the stream request and the first/last streamed tokens. */
|
|
198
198
|
let timeToFirstToken;
|
|
199
199
|
let timeToLastToken;
|
|
200
|
+
const midStreamFallback = ctx.midStreamFallback === true;
|
|
201
|
+
let attempt = 1;
|
|
202
|
+
const messageMetadata = (itemId) => ({
|
|
203
|
+
iterationId: iteration.id,
|
|
204
|
+
id: midStreamFallback ? `${iteration.id}:${attempt}:${itemId}` : `${iteration.id}:${itemId}`
|
|
205
|
+
});
|
|
206
|
+
const preview = async (delta) => {
|
|
207
|
+
try {
|
|
208
|
+
await (onSendDelta === null || onSendDelta === void 0 ? void 0 : onSendDelta(delta));
|
|
209
|
+
} catch (err) {
|
|
210
|
+
if (delta.restart) throw new require_errors.CognitiveError(`LLM stream restart handler failed: ${require_utils.getErrorMessage(err)}`);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
200
213
|
const liveItems = /* @__PURE__ */ new Map();
|
|
201
214
|
const liveContent = /* @__PURE__ */ new Map();
|
|
202
215
|
let codeGenerationTraced = false;
|
|
@@ -217,37 +230,39 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
217
230
|
if ((item === null || item === void 0 ? void 0 : item.kind) !== "send") continue;
|
|
218
231
|
const content = (liveContent.get(item.id) ?? "") + event.delta;
|
|
219
232
|
liveContent.set(item.id, content);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
const delta = {
|
|
234
|
+
restart: false,
|
|
235
|
+
...messageMetadata(item.id),
|
|
236
|
+
component: item.name,
|
|
237
|
+
props: item.props,
|
|
238
|
+
delta: event.delta,
|
|
239
|
+
content
|
|
240
|
+
};
|
|
241
|
+
await preview(delta);
|
|
229
242
|
} else if (event.type === "item-complete") {
|
|
230
243
|
if (event.item.kind === "send" && onSend) await onSend({
|
|
231
244
|
name: event.item.name,
|
|
232
245
|
props: event.item.props,
|
|
233
246
|
body: event.item.body
|
|
234
|
-
});
|
|
247
|
+
}, messageMetadata(event.item.id));
|
|
235
248
|
else if (event.item.kind === "run" && event.item.status === "complete" && !runCompleted) {
|
|
236
249
|
runCompleted = true;
|
|
237
|
-
|
|
250
|
+
const code = (event.item.body ?? "").trim();
|
|
251
|
+
onRunComplete === null || onRunComplete === void 0 || onRunComplete(code);
|
|
238
252
|
}
|
|
239
253
|
}
|
|
240
254
|
};
|
|
241
255
|
if (typeof cognitive.generateTextStream === "function") {
|
|
242
|
-
|
|
243
|
-
|
|
256
|
+
let parser = new require_dual_modes.StreamingMessageParser();
|
|
257
|
+
let fence = new LeadingFenceFilter();
|
|
244
258
|
const streamController = createJoinedAbortController([controller.signal]);
|
|
245
259
|
const requestedAt = Date.now();
|
|
246
260
|
const stream = cognitive.generateTextStream({
|
|
247
261
|
...input,
|
|
248
|
-
...ctx.maxTimeToFirstToken ? { options: {
|
|
262
|
+
...ctx.maxTimeToFirstToken || midStreamFallback ? { options: {
|
|
249
263
|
...input.options,
|
|
250
|
-
maxTimeToFirstToken: ctx.maxTimeToFirstToken
|
|
264
|
+
...ctx.maxTimeToFirstToken ? { maxTimeToFirstToken: ctx.maxTimeToFirstToken } : {},
|
|
265
|
+
...midStreamFallback ? { midStreamFallback: true } : {}
|
|
251
266
|
} } : {}
|
|
252
267
|
}, { signal: streamController.signal });
|
|
253
268
|
const inactivityTimeout = Math.max(STREAM_INACTIVITY_TIMEOUT, ctx.maxTimeToFirstToken ?? 0);
|
|
@@ -266,28 +281,66 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
266
281
|
}
|
|
267
282
|
};
|
|
268
283
|
raw = "";
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
chunk
|
|
274
|
-
|
|
275
|
-
|
|
284
|
+
let streamCompleted = false;
|
|
285
|
+
try {
|
|
286
|
+
while (true) {
|
|
287
|
+
var _chunk$value, _chunk$value2, _chunk$value3, _chunk$value4;
|
|
288
|
+
let chunk;
|
|
289
|
+
try {
|
|
290
|
+
chunk = await nextChunk();
|
|
291
|
+
} catch (thrown) {
|
|
292
|
+
throw new require_errors.CognitiveError(`LLM generation failed: ${require_utils.getErrorMessage(thrown)}`);
|
|
293
|
+
}
|
|
294
|
+
if (chunk.done) {
|
|
295
|
+
streamCompleted = true;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
if (((_chunk$value = chunk.value) === null || _chunk$value === void 0 ? void 0 : _chunk$value.restart) && !midStreamFallback) {
|
|
299
|
+
streamController.abort("Unexpected LLM stream restart");
|
|
300
|
+
throw new require_errors.CognitiveError("LLM stream restarted without options.midStreamFallback enabled");
|
|
301
|
+
}
|
|
302
|
+
if ((_chunk$value2 = chunk.value) === null || _chunk$value2 === void 0 ? void 0 : _chunk$value2.restart) {
|
|
303
|
+
traces.push({
|
|
304
|
+
type: "llm_call_restarted",
|
|
305
|
+
started_at: Date.now(),
|
|
306
|
+
...chunk.value.restart
|
|
307
|
+
});
|
|
308
|
+
raw = "";
|
|
309
|
+
parser = new require_dual_modes.StreamingMessageParser();
|
|
310
|
+
fence = new LeadingFenceFilter();
|
|
311
|
+
liveItems.clear();
|
|
312
|
+
liveContent.clear();
|
|
313
|
+
codeGenerationTraced = false;
|
|
314
|
+
runCompleted = false;
|
|
315
|
+
responseMetadata = void 0;
|
|
316
|
+
attempt = chunk.value.restart.attempt;
|
|
317
|
+
await preview({
|
|
318
|
+
...chunk.value.restart,
|
|
319
|
+
restart: true,
|
|
320
|
+
iterationId: iteration.id
|
|
321
|
+
});
|
|
322
|
+
timeToFirstToken = void 0;
|
|
323
|
+
timeToLastToken = void 0;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if ((_chunk$value3 = chunk.value) === null || _chunk$value3 === void 0 ? void 0 : _chunk$value3.metadata) responseMetadata = chunk.value.metadata;
|
|
327
|
+
const delta = (_chunk$value4 = chunk.value) === null || _chunk$value4 === void 0 ? void 0 : _chunk$value4.output;
|
|
328
|
+
if (!delta) continue;
|
|
329
|
+
timeToLastToken = Date.now() - requestedAt;
|
|
330
|
+
timeToFirstToken ??= timeToLastToken;
|
|
331
|
+
raw += delta;
|
|
332
|
+
await dispatchSends(parser.push(fence.push(delta)));
|
|
276
333
|
}
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
334
|
+
if (!responseMetadata) throw new require_errors.CognitiveError("LLM streaming completed without metadata");
|
|
335
|
+
const remaining = fence.flush();
|
|
336
|
+
if (remaining) await dispatchSends(parser.push(remaining));
|
|
337
|
+
await dispatchSends(parser.finish());
|
|
338
|
+
assistantResponse = require_dual_modes.toParsedAssistantResponse(parser.items, raw);
|
|
339
|
+
} finally {
|
|
340
|
+
streamController.abort("LLM stream closed");
|
|
341
|
+
if (!streamCompleted) stream.return(void 0).catch((err) => {});
|
|
285
342
|
}
|
|
286
|
-
if (
|
|
287
|
-
const remaining = fence.flush();
|
|
288
|
-
if (remaining) await dispatchSends(parser.push(remaining));
|
|
289
|
-
await dispatchSends(parser.finish());
|
|
290
|
-
assistantResponse = require_dual_modes.toParsedAssistantResponse(parser.items, raw);
|
|
343
|
+
if (midStreamFallback) controller.signal.throwIfAborted();
|
|
291
344
|
} else {
|
|
292
345
|
const response = await cognitive.generateText(input, { signal: controller.signal }).catch((thrown) => {
|
|
293
346
|
throw new require_errors.CognitiveError(`LLM generation failed: ${require_utils.getErrorMessage(thrown)}`);
|
|
@@ -296,7 +349,7 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
296
349
|
responseMetadata = response.metadata;
|
|
297
350
|
raw = response.output;
|
|
298
351
|
assistantResponse = ctx.version.parseAssistantResponse(raw);
|
|
299
|
-
for (const send of assistantResponse.sends) await (onSend === null || onSend === void 0 ? void 0 : onSend(send));
|
|
352
|
+
for (const [index, send] of assistantResponse.sends.entries()) await (onSend === null || onSend === void 0 ? void 0 : onSend(send, messageMetadata(`send-${index}`)));
|
|
300
353
|
}
|
|
301
354
|
iteration.code = assistantResponse.code;
|
|
302
355
|
iteration.sends = assistantResponse.sends;
|
|
@@ -329,7 +382,7 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
329
382
|
type: "llm_call_success",
|
|
330
383
|
started_at: startedAt,
|
|
331
384
|
ended_at: iteration.llm.ended_at,
|
|
332
|
-
model: model
|
|
385
|
+
model: iteration.llm.model,
|
|
333
386
|
code: iteration.code ?? ""
|
|
334
387
|
});
|
|
335
388
|
};
|
|
@@ -572,7 +625,10 @@ function wrapTool({ chat, tool, traces, object, iteration, beforeHook, afterHook
|
|
|
572
625
|
toolCallId
|
|
573
626
|
}));
|
|
574
627
|
if (typeof (beforeRes === null || beforeRes === void 0 ? void 0 : beforeRes.input) !== "undefined") effectiveInput = beforeRes.input;
|
|
575
|
-
output = await tool.execute(effectiveInput, {
|
|
628
|
+
output = await tool.execute(effectiveInput, {
|
|
629
|
+
callId: toolCallId,
|
|
630
|
+
iterationId: iteration.id
|
|
631
|
+
}, chat);
|
|
576
632
|
const afterRes = await (afterHook === null || afterHook === void 0 ? void 0 : afterHook({
|
|
577
633
|
iteration,
|
|
578
634
|
tool,
|
|
@@ -677,7 +733,7 @@ const executeContext = async (props) => {
|
|
|
677
733
|
return result;
|
|
678
734
|
};
|
|
679
735
|
const executeContextInternal = async (props) => {
|
|
680
|
-
var _props$options, _props$options2, _props$options3, _props$options4, _props$options5;
|
|
736
|
+
var _props$options, _props$options2, _props$options3, _props$options4, _props$options5, _props$options6;
|
|
681
737
|
const controller = createJoinedAbortController([props.signal]);
|
|
682
738
|
const { onIterationStart, onIterationEnd, onTrace, onExit, onBeforeExecution, onAfterTool, onBeforeTool } = props;
|
|
683
739
|
const client = props.client ?? new _botpress_client.Client();
|
|
@@ -691,7 +747,8 @@ const executeContextInternal = async (props) => {
|
|
|
691
747
|
timeout: (_props$options2 = props.options) === null || _props$options2 === void 0 ? void 0 : _props$options2.timeout,
|
|
692
748
|
maxTokens: (_props$options3 = props.options) === null || _props$options3 === void 0 ? void 0 : _props$options3.maxTokens,
|
|
693
749
|
maxTimeToFirstToken: (_props$options4 = props.options) === null || _props$options4 === void 0 ? void 0 : _props$options4.maxTimeToFirstToken,
|
|
694
|
-
|
|
750
|
+
midStreamFallback: (_props$options5 = props.options) === null || _props$options5 === void 0 ? void 0 : _props$options5.midStreamFallback,
|
|
751
|
+
transcriptionModel: (_props$options6 = props.options) === null || _props$options6 === void 0 ? void 0 : _props$options6.transcriptionModel,
|
|
695
752
|
exits: props.exits,
|
|
696
753
|
snapshot: props.snapshot,
|
|
697
754
|
model: props.model,
|
|
@@ -884,7 +941,7 @@ const executeIteration = async ({ iteration, ctx, cognitive, controller, onExit,
|
|
|
884
941
|
};
|
|
885
942
|
});
|
|
886
943
|
};
|
|
887
|
-
const canExecuteEarly = typeof onBeforeExecution !== "function";
|
|
944
|
+
const canExecuteEarly = typeof onBeforeExecution !== "function" && !ctx.midStreamFallback;
|
|
888
945
|
let earlyExecution;
|
|
889
946
|
await generateCode({
|
|
890
947
|
iteration,
|
|
@@ -902,7 +959,7 @@ const executeIteration = async ({ iteration, ctx, cognitive, controller, onExit,
|
|
|
902
959
|
promise: runCode(code)
|
|
903
960
|
};
|
|
904
961
|
} : void 0,
|
|
905
|
-
onSend: async (send) => {
|
|
962
|
+
onSend: async (send, messageMetadata) => {
|
|
906
963
|
if (!ctx.chat) return;
|
|
907
964
|
const sendStartedAt = Date.now();
|
|
908
965
|
const component = require_jsx.createJsxComponent({
|
|
@@ -911,7 +968,7 @@ const executeIteration = async ({ iteration, ctx, cognitive, controller, onExit,
|
|
|
911
968
|
children: send.body ? [send.body] : []
|
|
912
969
|
});
|
|
913
970
|
try {
|
|
914
|
-
await ctx.chat.handler(component);
|
|
971
|
+
await ctx.chat.handler(component, messageMetadata);
|
|
915
972
|
} catch (err) {
|
|
916
973
|
throw new Error(`Error while sending message (■send=${send.name}): ${require_utils.getErrorMessage(err)}`);
|
|
917
974
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as StreamingMessageParser, d as isEqual, h as toNumber, i as toParsedAssistantResponse } from "./dual-modes-D_p-jedw.js";
|
|
2
|
-
import { d as Snapshot, f as parseExit, l as PartialExecutionResult, n as Context, s as ErrorExecutionResult, t as _CustomModelClient, u as SuccessExecutionResult } from "./custom-client-
|
|
2
|
+
import { d as Snapshot, f as parseExit, l as PartialExecutionResult, n as Context, s as ErrorExecutionResult, t as _CustomModelClient, u as SuccessExecutionResult } from "./custom-client-CJIThG2H.js";
|
|
3
3
|
import { d as stripInvalidIdentifiers, i as getErrorMessage, s as init } from "./utils-UBeAFeEI.js";
|
|
4
4
|
import { t as createJsxComponent } from "./jsx-CIgdGGVs.js";
|
|
5
5
|
import { t as cleanStackTrace } from "./stack-traces-BIE_5Xwf.js";
|
|
@@ -119,7 +119,7 @@ const RESPONSE_LENGTH_BUFFER = {
|
|
|
119
119
|
PERCENTAGE: .1
|
|
120
120
|
};
|
|
121
121
|
/** Maximum time to wait between two stream chunks before considering the stream stalled. */
|
|
122
|
-
const STREAM_INACTIVITY_TIMEOUT =
|
|
122
|
+
const STREAM_INACTIVITY_TIMEOUT = 18e4;
|
|
123
123
|
const getModelOutputLimit = (inputLength) => clamp(RESPONSE_LENGTH_BUFFER.PERCENTAGE * inputLength, RESPONSE_LENGTH_BUFFER.MIN_TOKENS, RESPONSE_LENGTH_BUFFER.MAX_TOKENS);
|
|
124
124
|
/**
|
|
125
125
|
* Models sometimes wrap their whole response in a code fence. The fence has to
|
|
@@ -195,6 +195,19 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
195
195
|
/** Milliseconds between the stream request and the first/last streamed tokens. */
|
|
196
196
|
let timeToFirstToken;
|
|
197
197
|
let timeToLastToken;
|
|
198
|
+
const midStreamFallback = ctx.midStreamFallback === true;
|
|
199
|
+
let attempt = 1;
|
|
200
|
+
const messageMetadata = (itemId) => ({
|
|
201
|
+
iterationId: iteration.id,
|
|
202
|
+
id: midStreamFallback ? `${iteration.id}:${attempt}:${itemId}` : `${iteration.id}:${itemId}`
|
|
203
|
+
});
|
|
204
|
+
const preview = async (delta) => {
|
|
205
|
+
try {
|
|
206
|
+
await (onSendDelta === null || onSendDelta === void 0 ? void 0 : onSendDelta(delta));
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (delta.restart) throw new CognitiveError(`LLM stream restart handler failed: ${getErrorMessage(err)}`);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
198
211
|
const liveItems = /* @__PURE__ */ new Map();
|
|
199
212
|
const liveContent = /* @__PURE__ */ new Map();
|
|
200
213
|
let codeGenerationTraced = false;
|
|
@@ -215,37 +228,39 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
215
228
|
if ((item === null || item === void 0 ? void 0 : item.kind) !== "send") continue;
|
|
216
229
|
const content = (liveContent.get(item.id) ?? "") + event.delta;
|
|
217
230
|
liveContent.set(item.id, content);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
231
|
+
const delta = {
|
|
232
|
+
restart: false,
|
|
233
|
+
...messageMetadata(item.id),
|
|
234
|
+
component: item.name,
|
|
235
|
+
props: item.props,
|
|
236
|
+
delta: event.delta,
|
|
237
|
+
content
|
|
238
|
+
};
|
|
239
|
+
await preview(delta);
|
|
227
240
|
} else if (event.type === "item-complete") {
|
|
228
241
|
if (event.item.kind === "send" && onSend) await onSend({
|
|
229
242
|
name: event.item.name,
|
|
230
243
|
props: event.item.props,
|
|
231
244
|
body: event.item.body
|
|
232
|
-
});
|
|
245
|
+
}, messageMetadata(event.item.id));
|
|
233
246
|
else if (event.item.kind === "run" && event.item.status === "complete" && !runCompleted) {
|
|
234
247
|
runCompleted = true;
|
|
235
|
-
|
|
248
|
+
const code = (event.item.body ?? "").trim();
|
|
249
|
+
onRunComplete === null || onRunComplete === void 0 || onRunComplete(code);
|
|
236
250
|
}
|
|
237
251
|
}
|
|
238
252
|
};
|
|
239
253
|
if (typeof cognitive.generateTextStream === "function") {
|
|
240
|
-
|
|
241
|
-
|
|
254
|
+
let parser = new StreamingMessageParser();
|
|
255
|
+
let fence = new LeadingFenceFilter();
|
|
242
256
|
const streamController = createJoinedAbortController([controller.signal]);
|
|
243
257
|
const requestedAt = Date.now();
|
|
244
258
|
const stream = cognitive.generateTextStream({
|
|
245
259
|
...input,
|
|
246
|
-
...ctx.maxTimeToFirstToken ? { options: {
|
|
260
|
+
...ctx.maxTimeToFirstToken || midStreamFallback ? { options: {
|
|
247
261
|
...input.options,
|
|
248
|
-
maxTimeToFirstToken: ctx.maxTimeToFirstToken
|
|
262
|
+
...ctx.maxTimeToFirstToken ? { maxTimeToFirstToken: ctx.maxTimeToFirstToken } : {},
|
|
263
|
+
...midStreamFallback ? { midStreamFallback: true } : {}
|
|
249
264
|
} } : {}
|
|
250
265
|
}, { signal: streamController.signal });
|
|
251
266
|
const inactivityTimeout = Math.max(STREAM_INACTIVITY_TIMEOUT, ctx.maxTimeToFirstToken ?? 0);
|
|
@@ -264,28 +279,66 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
264
279
|
}
|
|
265
280
|
};
|
|
266
281
|
raw = "";
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
chunk
|
|
272
|
-
|
|
273
|
-
|
|
282
|
+
let streamCompleted = false;
|
|
283
|
+
try {
|
|
284
|
+
while (true) {
|
|
285
|
+
var _chunk$value, _chunk$value2, _chunk$value3, _chunk$value4;
|
|
286
|
+
let chunk;
|
|
287
|
+
try {
|
|
288
|
+
chunk = await nextChunk();
|
|
289
|
+
} catch (thrown) {
|
|
290
|
+
throw new CognitiveError(`LLM generation failed: ${getErrorMessage(thrown)}`);
|
|
291
|
+
}
|
|
292
|
+
if (chunk.done) {
|
|
293
|
+
streamCompleted = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if (((_chunk$value = chunk.value) === null || _chunk$value === void 0 ? void 0 : _chunk$value.restart) && !midStreamFallback) {
|
|
297
|
+
streamController.abort("Unexpected LLM stream restart");
|
|
298
|
+
throw new CognitiveError("LLM stream restarted without options.midStreamFallback enabled");
|
|
299
|
+
}
|
|
300
|
+
if ((_chunk$value2 = chunk.value) === null || _chunk$value2 === void 0 ? void 0 : _chunk$value2.restart) {
|
|
301
|
+
traces.push({
|
|
302
|
+
type: "llm_call_restarted",
|
|
303
|
+
started_at: Date.now(),
|
|
304
|
+
...chunk.value.restart
|
|
305
|
+
});
|
|
306
|
+
raw = "";
|
|
307
|
+
parser = new StreamingMessageParser();
|
|
308
|
+
fence = new LeadingFenceFilter();
|
|
309
|
+
liveItems.clear();
|
|
310
|
+
liveContent.clear();
|
|
311
|
+
codeGenerationTraced = false;
|
|
312
|
+
runCompleted = false;
|
|
313
|
+
responseMetadata = void 0;
|
|
314
|
+
attempt = chunk.value.restart.attempt;
|
|
315
|
+
await preview({
|
|
316
|
+
...chunk.value.restart,
|
|
317
|
+
restart: true,
|
|
318
|
+
iterationId: iteration.id
|
|
319
|
+
});
|
|
320
|
+
timeToFirstToken = void 0;
|
|
321
|
+
timeToLastToken = void 0;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if ((_chunk$value3 = chunk.value) === null || _chunk$value3 === void 0 ? void 0 : _chunk$value3.metadata) responseMetadata = chunk.value.metadata;
|
|
325
|
+
const delta = (_chunk$value4 = chunk.value) === null || _chunk$value4 === void 0 ? void 0 : _chunk$value4.output;
|
|
326
|
+
if (!delta) continue;
|
|
327
|
+
timeToLastToken = Date.now() - requestedAt;
|
|
328
|
+
timeToFirstToken ??= timeToLastToken;
|
|
329
|
+
raw += delta;
|
|
330
|
+
await dispatchSends(parser.push(fence.push(delta)));
|
|
274
331
|
}
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
332
|
+
if (!responseMetadata) throw new CognitiveError("LLM streaming completed without metadata");
|
|
333
|
+
const remaining = fence.flush();
|
|
334
|
+
if (remaining) await dispatchSends(parser.push(remaining));
|
|
335
|
+
await dispatchSends(parser.finish());
|
|
336
|
+
assistantResponse = toParsedAssistantResponse(parser.items, raw);
|
|
337
|
+
} finally {
|
|
338
|
+
streamController.abort("LLM stream closed");
|
|
339
|
+
if (!streamCompleted) stream.return(void 0).catch((err) => {});
|
|
283
340
|
}
|
|
284
|
-
if (
|
|
285
|
-
const remaining = fence.flush();
|
|
286
|
-
if (remaining) await dispatchSends(parser.push(remaining));
|
|
287
|
-
await dispatchSends(parser.finish());
|
|
288
|
-
assistantResponse = toParsedAssistantResponse(parser.items, raw);
|
|
341
|
+
if (midStreamFallback) controller.signal.throwIfAborted();
|
|
289
342
|
} else {
|
|
290
343
|
const response = await cognitive.generateText(input, { signal: controller.signal }).catch((thrown) => {
|
|
291
344
|
throw new CognitiveError(`LLM generation failed: ${getErrorMessage(thrown)}`);
|
|
@@ -294,7 +347,7 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
294
347
|
responseMetadata = response.metadata;
|
|
295
348
|
raw = response.output;
|
|
296
349
|
assistantResponse = ctx.version.parseAssistantResponse(raw);
|
|
297
|
-
for (const send of assistantResponse.sends) await (onSend === null || onSend === void 0 ? void 0 : onSend(send));
|
|
350
|
+
for (const [index, send] of assistantResponse.sends.entries()) await (onSend === null || onSend === void 0 ? void 0 : onSend(send, messageMetadata(`send-${index}`)));
|
|
298
351
|
}
|
|
299
352
|
iteration.code = assistantResponse.code;
|
|
300
353
|
iteration.sends = assistantResponse.sends;
|
|
@@ -327,7 +380,7 @@ const generateCode = async ({ iteration, ctx, cognitive, controller, metadata, o
|
|
|
327
380
|
type: "llm_call_success",
|
|
328
381
|
started_at: startedAt,
|
|
329
382
|
ended_at: iteration.llm.ended_at,
|
|
330
|
-
model: model
|
|
383
|
+
model: iteration.llm.model,
|
|
331
384
|
code: iteration.code ?? ""
|
|
332
385
|
});
|
|
333
386
|
};
|
|
@@ -570,7 +623,10 @@ function wrapTool({ chat, tool, traces, object, iteration, beforeHook, afterHook
|
|
|
570
623
|
toolCallId
|
|
571
624
|
}));
|
|
572
625
|
if (typeof (beforeRes === null || beforeRes === void 0 ? void 0 : beforeRes.input) !== "undefined") effectiveInput = beforeRes.input;
|
|
573
|
-
output = await tool.execute(effectiveInput, {
|
|
626
|
+
output = await tool.execute(effectiveInput, {
|
|
627
|
+
callId: toolCallId,
|
|
628
|
+
iterationId: iteration.id
|
|
629
|
+
}, chat);
|
|
574
630
|
const afterRes = await (afterHook === null || afterHook === void 0 ? void 0 : afterHook({
|
|
575
631
|
iteration,
|
|
576
632
|
tool,
|
|
@@ -675,7 +731,7 @@ const executeContext = async (props) => {
|
|
|
675
731
|
return result;
|
|
676
732
|
};
|
|
677
733
|
const executeContextInternal = async (props) => {
|
|
678
|
-
var _props$options, _props$options2, _props$options3, _props$options4, _props$options5;
|
|
734
|
+
var _props$options, _props$options2, _props$options3, _props$options4, _props$options5, _props$options6;
|
|
679
735
|
const controller = createJoinedAbortController([props.signal]);
|
|
680
736
|
const { onIterationStart, onIterationEnd, onTrace, onExit, onBeforeExecution, onAfterTool, onBeforeTool } = props;
|
|
681
737
|
const client = props.client ?? new Client();
|
|
@@ -689,7 +745,8 @@ const executeContextInternal = async (props) => {
|
|
|
689
745
|
timeout: (_props$options2 = props.options) === null || _props$options2 === void 0 ? void 0 : _props$options2.timeout,
|
|
690
746
|
maxTokens: (_props$options3 = props.options) === null || _props$options3 === void 0 ? void 0 : _props$options3.maxTokens,
|
|
691
747
|
maxTimeToFirstToken: (_props$options4 = props.options) === null || _props$options4 === void 0 ? void 0 : _props$options4.maxTimeToFirstToken,
|
|
692
|
-
|
|
748
|
+
midStreamFallback: (_props$options5 = props.options) === null || _props$options5 === void 0 ? void 0 : _props$options5.midStreamFallback,
|
|
749
|
+
transcriptionModel: (_props$options6 = props.options) === null || _props$options6 === void 0 ? void 0 : _props$options6.transcriptionModel,
|
|
693
750
|
exits: props.exits,
|
|
694
751
|
snapshot: props.snapshot,
|
|
695
752
|
model: props.model,
|
|
@@ -882,7 +939,7 @@ const executeIteration = async ({ iteration, ctx, cognitive, controller, onExit,
|
|
|
882
939
|
};
|
|
883
940
|
});
|
|
884
941
|
};
|
|
885
|
-
const canExecuteEarly = typeof onBeforeExecution !== "function";
|
|
942
|
+
const canExecuteEarly = typeof onBeforeExecution !== "function" && !ctx.midStreamFallback;
|
|
886
943
|
let earlyExecution;
|
|
887
944
|
await generateCode({
|
|
888
945
|
iteration,
|
|
@@ -900,7 +957,7 @@ const executeIteration = async ({ iteration, ctx, cognitive, controller, onExit,
|
|
|
900
957
|
promise: runCode(code)
|
|
901
958
|
};
|
|
902
959
|
} : void 0,
|
|
903
|
-
onSend: async (send) => {
|
|
960
|
+
onSend: async (send, messageMetadata) => {
|
|
904
961
|
if (!ctx.chat) return;
|
|
905
962
|
const sendStartedAt = Date.now();
|
|
906
963
|
const component = createJsxComponent({
|
|
@@ -909,7 +966,7 @@ const executeIteration = async ({ iteration, ctx, cognitive, controller, onExit,
|
|
|
909
966
|
children: send.body ? [send.body] : []
|
|
910
967
|
});
|
|
911
968
|
try {
|
|
912
|
-
await ctx.chat.handler(component);
|
|
969
|
+
await ctx.chat.handler(component, messageMetadata);
|
|
913
970
|
} catch (err) {
|
|
914
971
|
throw new Error(`Error while sending message (■send=${send.name}): ${getErrorMessage(err)}`);
|
|
915
972
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_dual_modes = require("./dual-modes-wAOAlaBS.cjs");
|
|
3
|
-
const require_custom_client = require("./custom-client-
|
|
3
|
+
const require_custom_client = require("./custom-client-Cr33K60k.cjs");
|
|
4
4
|
const require_utils = require("./utils-ZpJm00ed.cjs");
|
|
5
|
-
const require_tool = require("./tool-
|
|
5
|
+
const require_tool = require("./tool-ReA9gxMp.cjs");
|
|
6
6
|
const require_errors = require("./errors-C962uJdS.cjs");
|
|
7
7
|
const require_typings = require("./typings-BwAz_TM9.cjs");
|
|
8
8
|
const require_exit = require("./exit-BGi0Kk0y.cjs");
|
|
@@ -11,7 +11,7 @@ const require_truncator = require("./truncator-B6aaJaN1.cjs");
|
|
|
11
11
|
const require_quickjs_variant = require("./quickjs-variant-CsIwTVFC.cjs");
|
|
12
12
|
let _bpinternal_zui = require("@bpinternal/zui");
|
|
13
13
|
//#region package.json
|
|
14
|
-
var version = "0.6.
|
|
14
|
+
var version = "0.6.6";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/hoist.ts
|
|
17
17
|
/**
|
|
@@ -1774,7 +1774,7 @@ const utils = {
|
|
|
1774
1774
|
* })
|
|
1775
1775
|
*/
|
|
1776
1776
|
const execute = async (props) => {
|
|
1777
|
-
const { executeContext } = await Promise.resolve().then(() => require("./execute-
|
|
1777
|
+
const { executeContext } = await Promise.resolve().then(() => require("./execute-BmEjJjka.cjs"));
|
|
1778
1778
|
return executeContext(props);
|
|
1779
1779
|
};
|
|
1780
1780
|
/**
|
|
@@ -1783,9 +1783,9 @@ const execute = async (props) => {
|
|
|
1783
1783
|
* It's recommended to call this function at the beginning of your application without awaiting it (void init())
|
|
1784
1784
|
*/
|
|
1785
1785
|
const init = async () => {
|
|
1786
|
-
await Promise.resolve().then(() => require("./execute-
|
|
1786
|
+
await Promise.resolve().then(() => require("./execute-BmEjJjka.cjs"));
|
|
1787
1787
|
await Promise.resolve().then(() => require("./component-BwixNVH8.cjs")).then((n) => n.component_exports);
|
|
1788
|
-
await Promise.resolve().then(() => require("./tool-
|
|
1788
|
+
await Promise.resolve().then(() => require("./tool-ReA9gxMp.cjs")).then((n) => n.tool_exports);
|
|
1789
1789
|
await Promise.resolve().then(() => require("./exit-BGi0Kk0y.cjs")).then((n) => n.exit_exports);
|
|
1790
1790
|
await Promise.resolve().then(() => require("./jsx-BWvT6ukg.cjs")).then((n) => n.jsx_exports);
|
|
1791
1791
|
await Promise.resolve().then(() => require("./vm-BjkJqSQH.cjs"));
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { Component, type RenderedComponent, type LeafComponentDefinition, type C
|
|
|
8
8
|
export { type Citation, CitationsManager } from './citations.js';
|
|
9
9
|
export { DefaultComponents } from './component.default.js';
|
|
10
10
|
export { Snapshot } from './snapshots.js';
|
|
11
|
-
export { Chat, type MessageHandler, type MessageDelta, type MessageDeltaHandler } from './chat.js';
|
|
11
|
+
export { Chat, type MessageHandler, type MessageMetadata, type MessageDelta, type MessageDeltaHandler } from './chat.js';
|
|
12
12
|
import { ExecutionResult } from './result.js';
|
|
13
13
|
import { type ExecutionProps } from './runtime/types.js';
|
|
14
14
|
import { stripTruncationTags, truncateWrappedContent, wrapContent } from './truncator.js';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as StreamingMessageParser, c as NAME_REGEX, o as tryParseJson, r as generateInstructions, s as MARKER } from "./dual-modes-D_p-jedw.js";
|
|
2
|
-
import { a as ThinkExit, c as ExecutionResult, d as Snapshot, f as parseExit, i as ListenExit, l as PartialExecutionResult, o as getValue, r as DefaultExit, s as ErrorExecutionResult, t as _CustomModelClient, u as SuccessExecutionResult } from "./custom-client-
|
|
2
|
+
import { a as ThinkExit, c as ExecutionResult, d as Snapshot, f as parseExit, i as ListenExit, l as PartialExecutionResult, o as getValue, r as DefaultExit, s as ErrorExecutionResult, t as _CustomModelClient, u as SuccessExecutionResult } from "./custom-client-CJIThG2H.js";
|
|
3
3
|
import { a as getMultilineComment, c as isJsonSchema, l as isValidIdentifier, m as toValidObjectName, n as escapeString, p as toValidFunctionName, t as configureTokenizer, u as isZuiSchema, v as upperFirst } from "./utils-UBeAFeEI.js";
|
|
4
|
-
import { t as Tool } from "./tool-
|
|
4
|
+
import { t as Tool } from "./tool-Bv8N4IPj.js";
|
|
5
5
|
import { c as SnapshotSignal, l as ThinkSignal, o as LoopExceededError, r as CodeFormattingError } from "./errors-Cg1G0Awu.js";
|
|
6
6
|
import { a as isPunct, i as isGroup, o as parse, r as formatTypings, s as tokenize, t as getTypings } from "./typings-CrBa7Tyy.js";
|
|
7
7
|
import { t as Exit } from "./exit-CDdxDBRT.js";
|
|
@@ -10,7 +10,7 @@ import { i as wrapContent, n as truncateWrappedContent, t as stripTruncationTags
|
|
|
10
10
|
import { t as configureQuickJS } from "./quickjs-variant-BYhMBpwU.js";
|
|
11
11
|
import { transforms, z } from "@bpinternal/zui";
|
|
12
12
|
//#region package.json
|
|
13
|
-
var version = "0.6.
|
|
13
|
+
var version = "0.6.6";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/hoist.ts
|
|
16
16
|
/**
|
|
@@ -1773,7 +1773,7 @@ const utils = {
|
|
|
1773
1773
|
* })
|
|
1774
1774
|
*/
|
|
1775
1775
|
const execute = async (props) => {
|
|
1776
|
-
const { executeContext } = await import("./execute-
|
|
1776
|
+
const { executeContext } = await import("./execute-sn9RT_IN.js");
|
|
1777
1777
|
return executeContext(props);
|
|
1778
1778
|
};
|
|
1779
1779
|
/**
|
|
@@ -1782,9 +1782,9 @@ const execute = async (props) => {
|
|
|
1782
1782
|
* It's recommended to call this function at the beginning of your application without awaiting it (void init())
|
|
1783
1783
|
*/
|
|
1784
1784
|
const init = async () => {
|
|
1785
|
-
await import("./execute-
|
|
1785
|
+
await import("./execute-sn9RT_IN.js");
|
|
1786
1786
|
await import("./component-DGG7d_Mz.js").then((n) => n.r);
|
|
1787
|
-
await import("./tool-
|
|
1787
|
+
await import("./tool-Bv8N4IPj.js").then((n) => n.n);
|
|
1788
1788
|
await import("./exit-CDdxDBRT.js").then((n) => n.n);
|
|
1789
1789
|
await import("./jsx-CIgdGGVs.js").then((n) => n.i);
|
|
1790
1790
|
await import("./vm-C5vf1LH3.js");
|
package/dist/index.workerd.js
CHANGED
|
@@ -52,7 +52,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
52
52
|
}) : target, mod));
|
|
53
53
|
//#endregion
|
|
54
54
|
//#region package.json
|
|
55
|
-
var version = "0.6.
|
|
55
|
+
var version = "0.6.6";
|
|
56
56
|
//#endregion
|
|
57
57
|
//#region ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_freeGlobal.js
|
|
58
58
|
var freeGlobal;
|
|
@@ -7682,7 +7682,10 @@ var init_tool = __esmMin((() => {
|
|
|
7682
7682
|
if (done) return value;
|
|
7683
7683
|
if (yieldIndex >= yieldedCount) {
|
|
7684
7684
|
setYieldedCount(yieldIndex + 1);
|
|
7685
|
-
await chat?.handler?.(value
|
|
7685
|
+
await chat?.handler?.(value, {
|
|
7686
|
+
iterationId: ctx.iterationId ?? ctx.callId,
|
|
7687
|
+
id: `${ctx.callId}:yield-${yieldIndex}`
|
|
7688
|
+
});
|
|
7686
7689
|
}
|
|
7687
7690
|
yieldIndex++;
|
|
7688
7691
|
}
|
|
@@ -12215,6 +12218,17 @@ var init_context = __esmMin((() => {
|
|
|
12215
12218
|
*/
|
|
12216
12219
|
maxTimeToFirstToken;
|
|
12217
12220
|
/**
|
|
12221
|
+
* Allow Cognitive to restart a failed stream on another model. With fallback
|
|
12222
|
+
* enabled, there is no buffered delivery and no final delivery queue: delta
|
|
12223
|
+
* text and completed Chat.handler sends stream immediately during generation.
|
|
12224
|
+
* A reset-only delta (restart: true) on Chat.onMessageDelta invalidates ALL
|
|
12225
|
+
* current-iteration messages, including completed sends — consumers must
|
|
12226
|
+
* retract/replace them, which is unsafe for irreversible external transports.
|
|
12227
|
+
* Only TOOL/CODE execution waits for a successful stream. Streaming-only;
|
|
12228
|
+
* defaults to false, which leaves progressive sending unchanged.
|
|
12229
|
+
*/
|
|
12230
|
+
midStreamFallback;
|
|
12231
|
+
/**
|
|
12218
12232
|
* STT model used by the cognitive service to transcribe audio attachments
|
|
12219
12233
|
* when the target LLM does not support audio natively. Defaults to 'fast'.
|
|
12220
12234
|
*/
|
|
@@ -12449,10 +12463,12 @@ var init_context = __esmMin((() => {
|
|
|
12449
12463
|
this.snapshot = props.snapshot;
|
|
12450
12464
|
this.maxTokens = props.maxTokens;
|
|
12451
12465
|
this.maxTimeToFirstToken = props.maxTimeToFirstToken;
|
|
12466
|
+
this.midStreamFallback = props.midStreamFallback;
|
|
12452
12467
|
this.transcriptionModel = props.transcriptionModel;
|
|
12453
12468
|
if (this.loop < 1 || this.loop > 100) throw new Error("Invalid loop. Expected a number between 1 and 100.");
|
|
12454
12469
|
if (this.maxTokens !== void 0 && (!Number.isFinite(this.maxTokens) || this.maxTokens < 1)) throw new Error("Invalid maxTokens. Expected a positive number.");
|
|
12455
12470
|
if (this.maxTimeToFirstToken !== void 0 && (!Number.isFinite(this.maxTimeToFirstToken) || this.maxTimeToFirstToken < 1)) throw new Error("Invalid maxTimeToFirstToken. Expected a positive number of milliseconds.");
|
|
12471
|
+
if (this.midStreamFallback !== void 0 && typeof this.midStreamFallback !== "boolean") throw new Error("Invalid midStreamFallback. Expected a boolean.");
|
|
12456
12472
|
}
|
|
12457
12473
|
toJSON() {
|
|
12458
12474
|
return {
|
|
@@ -17023,7 +17039,7 @@ var init_generate = __esmMin((() => {
|
|
|
17023
17039
|
MAX_TOKENS: 16e3,
|
|
17024
17040
|
PERCENTAGE: .1
|
|
17025
17041
|
};
|
|
17026
|
-
STREAM_INACTIVITY_TIMEOUT =
|
|
17042
|
+
STREAM_INACTIVITY_TIMEOUT = 18e4;
|
|
17027
17043
|
getModelOutputLimit = (inputLength) => clamp(RESPONSE_LENGTH_BUFFER.PERCENTAGE * inputLength, RESPONSE_LENGTH_BUFFER.MIN_TOKENS, RESPONSE_LENGTH_BUFFER.MAX_TOKENS);
|
|
17028
17044
|
LeadingFenceFilter = class {
|
|
17029
17045
|
_buffer = "";
|
|
@@ -17093,6 +17109,19 @@ var init_generate = __esmMin((() => {
|
|
|
17093
17109
|
/** Milliseconds between the stream request and the first/last streamed tokens. */
|
|
17094
17110
|
let timeToFirstToken;
|
|
17095
17111
|
let timeToLastToken;
|
|
17112
|
+
const midStreamFallback = ctx.midStreamFallback === true;
|
|
17113
|
+
let attempt = 1;
|
|
17114
|
+
const messageMetadata = (itemId) => ({
|
|
17115
|
+
iterationId: iteration.id,
|
|
17116
|
+
id: midStreamFallback ? `${iteration.id}:${attempt}:${itemId}` : `${iteration.id}:${itemId}`
|
|
17117
|
+
});
|
|
17118
|
+
const preview = async (delta) => {
|
|
17119
|
+
try {
|
|
17120
|
+
await onSendDelta?.(delta);
|
|
17121
|
+
} catch (err) {
|
|
17122
|
+
if (delta.restart) throw new CognitiveError(`LLM stream restart handler failed: ${getErrorMessage(err)}`);
|
|
17123
|
+
}
|
|
17124
|
+
};
|
|
17096
17125
|
const liveItems = /* @__PURE__ */ new Map();
|
|
17097
17126
|
const liveContent = /* @__PURE__ */ new Map();
|
|
17098
17127
|
let codeGenerationTraced = false;
|
|
@@ -17113,37 +17142,39 @@ var init_generate = __esmMin((() => {
|
|
|
17113
17142
|
if (item?.kind !== "send") continue;
|
|
17114
17143
|
const content = (liveContent.get(item.id) ?? "") + event.delta;
|
|
17115
17144
|
liveContent.set(item.id, content);
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17145
|
+
const delta = {
|
|
17146
|
+
restart: false,
|
|
17147
|
+
...messageMetadata(item.id),
|
|
17148
|
+
component: item.name,
|
|
17149
|
+
props: item.props,
|
|
17150
|
+
delta: event.delta,
|
|
17151
|
+
content
|
|
17152
|
+
};
|
|
17153
|
+
await preview(delta);
|
|
17125
17154
|
} else if (event.type === "item-complete") {
|
|
17126
17155
|
if (event.item.kind === "send" && onSend) await onSend({
|
|
17127
17156
|
name: event.item.name,
|
|
17128
17157
|
props: event.item.props,
|
|
17129
17158
|
body: event.item.body
|
|
17130
|
-
});
|
|
17159
|
+
}, messageMetadata(event.item.id));
|
|
17131
17160
|
else if (event.item.kind === "run" && event.item.status === "complete" && !runCompleted) {
|
|
17132
17161
|
runCompleted = true;
|
|
17133
|
-
|
|
17162
|
+
const code = (event.item.body ?? "").trim();
|
|
17163
|
+
onRunComplete?.(code);
|
|
17134
17164
|
}
|
|
17135
17165
|
}
|
|
17136
17166
|
};
|
|
17137
17167
|
if (typeof cognitive.generateTextStream === "function") {
|
|
17138
|
-
|
|
17139
|
-
|
|
17168
|
+
let parser = new StreamingMessageParser();
|
|
17169
|
+
let fence = new LeadingFenceFilter();
|
|
17140
17170
|
const streamController = createJoinedAbortController([controller.signal]);
|
|
17141
17171
|
const requestedAt = Date.now();
|
|
17142
17172
|
const stream = cognitive.generateTextStream({
|
|
17143
17173
|
...input,
|
|
17144
|
-
...ctx.maxTimeToFirstToken ? { options: {
|
|
17174
|
+
...ctx.maxTimeToFirstToken || midStreamFallback ? { options: {
|
|
17145
17175
|
...input.options,
|
|
17146
|
-
maxTimeToFirstToken: ctx.maxTimeToFirstToken
|
|
17176
|
+
...ctx.maxTimeToFirstToken ? { maxTimeToFirstToken: ctx.maxTimeToFirstToken } : {},
|
|
17177
|
+
...midStreamFallback ? { midStreamFallback: true } : {}
|
|
17147
17178
|
} } : {}
|
|
17148
17179
|
}, { signal: streamController.signal });
|
|
17149
17180
|
const inactivityTimeout = Math.max(STREAM_INACTIVITY_TIMEOUT, ctx.maxTimeToFirstToken ?? 0);
|
|
@@ -17162,27 +17193,65 @@ var init_generate = __esmMin((() => {
|
|
|
17162
17193
|
}
|
|
17163
17194
|
};
|
|
17164
17195
|
raw = "";
|
|
17165
|
-
|
|
17166
|
-
|
|
17167
|
-
|
|
17168
|
-
chunk
|
|
17169
|
-
|
|
17170
|
-
|
|
17196
|
+
let streamCompleted = false;
|
|
17197
|
+
try {
|
|
17198
|
+
while (true) {
|
|
17199
|
+
let chunk;
|
|
17200
|
+
try {
|
|
17201
|
+
chunk = await nextChunk();
|
|
17202
|
+
} catch (thrown) {
|
|
17203
|
+
throw new CognitiveError(`LLM generation failed: ${getErrorMessage(thrown)}`);
|
|
17204
|
+
}
|
|
17205
|
+
if (chunk.done) {
|
|
17206
|
+
streamCompleted = true;
|
|
17207
|
+
break;
|
|
17208
|
+
}
|
|
17209
|
+
if (chunk.value?.restart && !midStreamFallback) {
|
|
17210
|
+
streamController.abort("Unexpected LLM stream restart");
|
|
17211
|
+
throw new CognitiveError("LLM stream restarted without options.midStreamFallback enabled");
|
|
17212
|
+
}
|
|
17213
|
+
if (chunk.value?.restart) {
|
|
17214
|
+
traces.push({
|
|
17215
|
+
type: "llm_call_restarted",
|
|
17216
|
+
started_at: Date.now(),
|
|
17217
|
+
...chunk.value.restart
|
|
17218
|
+
});
|
|
17219
|
+
raw = "";
|
|
17220
|
+
parser = new StreamingMessageParser();
|
|
17221
|
+
fence = new LeadingFenceFilter();
|
|
17222
|
+
liveItems.clear();
|
|
17223
|
+
liveContent.clear();
|
|
17224
|
+
codeGenerationTraced = false;
|
|
17225
|
+
runCompleted = false;
|
|
17226
|
+
responseMetadata = void 0;
|
|
17227
|
+
attempt = chunk.value.restart.attempt;
|
|
17228
|
+
await preview({
|
|
17229
|
+
...chunk.value.restart,
|
|
17230
|
+
restart: true,
|
|
17231
|
+
iterationId: iteration.id
|
|
17232
|
+
});
|
|
17233
|
+
timeToFirstToken = void 0;
|
|
17234
|
+
timeToLastToken = void 0;
|
|
17235
|
+
continue;
|
|
17236
|
+
}
|
|
17237
|
+
if (chunk.value?.metadata) responseMetadata = chunk.value.metadata;
|
|
17238
|
+
const delta = chunk.value?.output;
|
|
17239
|
+
if (!delta) continue;
|
|
17240
|
+
timeToLastToken = Date.now() - requestedAt;
|
|
17241
|
+
timeToFirstToken ??= timeToLastToken;
|
|
17242
|
+
raw += delta;
|
|
17243
|
+
await dispatchSends(parser.push(fence.push(delta)));
|
|
17171
17244
|
}
|
|
17172
|
-
if (
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17245
|
+
if (!responseMetadata) throw new CognitiveError("LLM streaming completed without metadata");
|
|
17246
|
+
const remaining = fence.flush();
|
|
17247
|
+
if (remaining) await dispatchSends(parser.push(remaining));
|
|
17248
|
+
await dispatchSends(parser.finish());
|
|
17249
|
+
assistantResponse = toParsedAssistantResponse(parser.items, raw);
|
|
17250
|
+
} finally {
|
|
17251
|
+
streamController.abort("LLM stream closed");
|
|
17252
|
+
if (!streamCompleted) stream.return(void 0).catch((err) => {});
|
|
17180
17253
|
}
|
|
17181
|
-
if (
|
|
17182
|
-
const remaining = fence.flush();
|
|
17183
|
-
if (remaining) await dispatchSends(parser.push(remaining));
|
|
17184
|
-
await dispatchSends(parser.finish());
|
|
17185
|
-
assistantResponse = toParsedAssistantResponse(parser.items, raw);
|
|
17254
|
+
if (midStreamFallback) controller.signal.throwIfAborted();
|
|
17186
17255
|
} else {
|
|
17187
17256
|
const response = await cognitive.generateText(input, { signal: controller.signal }).catch((thrown) => {
|
|
17188
17257
|
throw new CognitiveError(`LLM generation failed: ${getErrorMessage(thrown)}`);
|
|
@@ -17191,7 +17260,7 @@ var init_generate = __esmMin((() => {
|
|
|
17191
17260
|
responseMetadata = response.metadata;
|
|
17192
17261
|
raw = response.output;
|
|
17193
17262
|
assistantResponse = ctx.version.parseAssistantResponse(raw);
|
|
17194
|
-
for (const send of assistantResponse.sends) await onSend?.(send);
|
|
17263
|
+
for (const [index, send] of assistantResponse.sends.entries()) await onSend?.(send, messageMetadata(`send-${index}`));
|
|
17195
17264
|
}
|
|
17196
17265
|
iteration.code = assistantResponse.code;
|
|
17197
17266
|
iteration.sends = assistantResponse.sends;
|
|
@@ -17224,7 +17293,7 @@ var init_generate = __esmMin((() => {
|
|
|
17224
17293
|
type: "llm_call_success",
|
|
17225
17294
|
started_at: startedAt,
|
|
17226
17295
|
ended_at: iteration.llm.ended_at,
|
|
17227
|
-
model: model
|
|
17296
|
+
model: iteration.llm.model,
|
|
17228
17297
|
code: iteration.code ?? ""
|
|
17229
17298
|
});
|
|
17230
17299
|
};
|
|
@@ -17471,7 +17540,10 @@ function wrapTool({ chat, tool, traces, object, iteration, beforeHook, afterHook
|
|
|
17471
17540
|
toolCallId
|
|
17472
17541
|
});
|
|
17473
17542
|
if (typeof beforeRes?.input !== "undefined") effectiveInput = beforeRes.input;
|
|
17474
|
-
output = await tool.execute(effectiveInput, {
|
|
17543
|
+
output = await tool.execute(effectiveInput, {
|
|
17544
|
+
callId: toolCallId,
|
|
17545
|
+
iterationId: iteration.id
|
|
17546
|
+
}, chat);
|
|
17475
17547
|
const afterRes = await afterHook?.({
|
|
17476
17548
|
iteration,
|
|
17477
17549
|
tool,
|
|
@@ -17614,6 +17686,7 @@ var init_execute = __esmMin((() => {
|
|
|
17614
17686
|
timeout: props.options?.timeout,
|
|
17615
17687
|
maxTokens: props.options?.maxTokens,
|
|
17616
17688
|
maxTimeToFirstToken: props.options?.maxTimeToFirstToken,
|
|
17689
|
+
midStreamFallback: props.options?.midStreamFallback,
|
|
17617
17690
|
transcriptionModel: props.options?.transcriptionModel,
|
|
17618
17691
|
exits: props.exits,
|
|
17619
17692
|
snapshot: props.snapshot,
|
|
@@ -17805,7 +17878,7 @@ var init_execute = __esmMin((() => {
|
|
|
17805
17878
|
};
|
|
17806
17879
|
});
|
|
17807
17880
|
};
|
|
17808
|
-
const canExecuteEarly = typeof onBeforeExecution !== "function";
|
|
17881
|
+
const canExecuteEarly = typeof onBeforeExecution !== "function" && !ctx.midStreamFallback;
|
|
17809
17882
|
let earlyExecution;
|
|
17810
17883
|
await generateCode({
|
|
17811
17884
|
iteration,
|
|
@@ -17823,7 +17896,7 @@ var init_execute = __esmMin((() => {
|
|
|
17823
17896
|
promise: runCode(code)
|
|
17824
17897
|
};
|
|
17825
17898
|
} : void 0,
|
|
17826
|
-
onSend: async (send) => {
|
|
17899
|
+
onSend: async (send, messageMetadata) => {
|
|
17827
17900
|
if (!ctx.chat) return;
|
|
17828
17901
|
const sendStartedAt = Date.now();
|
|
17829
17902
|
const component = createJsxComponent({
|
|
@@ -17832,7 +17905,7 @@ var init_execute = __esmMin((() => {
|
|
|
17832
17905
|
children: send.body ? [send.body] : []
|
|
17833
17906
|
});
|
|
17834
17907
|
try {
|
|
17835
|
-
await ctx.chat.handler(component);
|
|
17908
|
+
await ctx.chat.handler(component, messageMetadata);
|
|
17836
17909
|
} catch (err) {
|
|
17837
17910
|
throw new Error(`Error while sending message (■send=${send.name}): ${getErrorMessage(err)}`);
|
|
17838
17911
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MessageDelta } from '../chat.js';
|
|
1
|
+
import type { MessageDelta, MessageMetadata } from '../chat.js';
|
|
2
2
|
import { Context, Iteration } from '../context.js';
|
|
3
3
|
import type { ParsedSend } from '../prompts/prompt.js';
|
|
4
4
|
import { RuntimeCognitive } from './types.js';
|
|
@@ -12,10 +12,11 @@ type GenerateCodeProps = {
|
|
|
12
12
|
* Called for each completed `■send` block. On streaming clients this fires
|
|
13
13
|
* while the model is still generating — messages are delivered progressively.
|
|
14
14
|
*/
|
|
15
|
-
onSend?: (send: ParsedSend) => Promise<void>;
|
|
15
|
+
onSend?: (send: ParsedSend, metadata: MessageMetadata) => Promise<void>;
|
|
16
16
|
/**
|
|
17
17
|
* Called for each `■send` body chunk as it is parsed from the stream
|
|
18
|
-
* (streaming clients only)
|
|
18
|
+
* (streaming clients only), or with a restart delta before replacement output.
|
|
19
|
+
* Text errors are best-effort; restart errors terminate generation.
|
|
19
20
|
*/
|
|
20
21
|
onSendDelta?: (delta: MessageDelta) => Promise<void> | void;
|
|
21
22
|
/**
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -122,6 +122,16 @@ type Options = Partial<Pick<Context, 'loop' | 'timeout'>> & {
|
|
|
122
122
|
* `model` is an array of fallback models.
|
|
123
123
|
*/
|
|
124
124
|
maxTimeToFirstToken?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Allow Cognitive to restart failed streams on another model. No buffered
|
|
127
|
+
* delivery and no final delivery queue: delta text and completed Chat.handler
|
|
128
|
+
* sends stream immediately during generation. A reset-only delta (restart:
|
|
129
|
+
* true) on Chat.onMessageDelta invalidates ALL current-iteration messages,
|
|
130
|
+
* including completed sends — consumers must retract/replace them (not safe for
|
|
131
|
+
* irreversible transports). Only TOOL/CODE execution waits for a successful
|
|
132
|
+
* stream. Streaming-only; defaults to false, preserving progressive sending.
|
|
133
|
+
*/
|
|
134
|
+
midStreamFallback?: boolean;
|
|
125
135
|
/**
|
|
126
136
|
* STT model used to transcribe audio attachments (voice messages) when the
|
|
127
137
|
* target LLM does not support audio natively. Audio-capable models receive
|
|
@@ -500,7 +500,10 @@ var Tool = class Tool {
|
|
|
500
500
|
if (yieldIndex >= yieldedCount) {
|
|
501
501
|
var _chat$handler;
|
|
502
502
|
setYieldedCount(yieldIndex + 1);
|
|
503
|
-
await (chat === null || chat === void 0 || (_chat$handler = chat.handler) === null || _chat$handler === void 0 ? void 0 : _chat$handler.call(chat, value
|
|
503
|
+
await (chat === null || chat === void 0 || (_chat$handler = chat.handler) === null || _chat$handler === void 0 ? void 0 : _chat$handler.call(chat, value, {
|
|
504
|
+
iterationId: ctx.iterationId ?? ctx.callId,
|
|
505
|
+
id: `${ctx.callId}:yield-${yieldIndex}`
|
|
506
|
+
}));
|
|
504
507
|
}
|
|
505
508
|
yieldIndex++;
|
|
506
509
|
}
|
|
@@ -500,7 +500,10 @@ var Tool = class Tool {
|
|
|
500
500
|
if (yieldIndex >= yieldedCount) {
|
|
501
501
|
var _chat$handler;
|
|
502
502
|
setYieldedCount(yieldIndex + 1);
|
|
503
|
-
await (chat === null || chat === void 0 || (_chat$handler = chat.handler) === null || _chat$handler === void 0 ? void 0 : _chat$handler.call(chat, value
|
|
503
|
+
await (chat === null || chat === void 0 || (_chat$handler = chat.handler) === null || _chat$handler === void 0 ? void 0 : _chat$handler.call(chat, value, {
|
|
504
|
+
iterationId: ctx.iterationId ?? ctx.callId,
|
|
505
|
+
id: `${ctx.callId}:yield-${yieldIndex}`
|
|
506
|
+
}));
|
|
504
507
|
}
|
|
505
508
|
yieldIndex++;
|
|
506
509
|
}
|
package/dist/tool.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ type SmartPartial<T> = IsObject<T> extends true ? Partial<T> : T;
|
|
|
49
49
|
type ToolCallContext = {
|
|
50
50
|
/** Unique identifier for this specific tool call */
|
|
51
51
|
callId: string;
|
|
52
|
+
/** Set by the runtime; standalone calls use callId as their message scope. */
|
|
53
|
+
iterationId?: string;
|
|
52
54
|
};
|
|
53
55
|
export declare namespace Tool {
|
|
54
56
|
type JSON = {
|
package/dist/types.d.ts
CHANGED
|
@@ -55,6 +55,13 @@ export declare namespace Traces {
|
|
|
55
55
|
model: string;
|
|
56
56
|
code: string;
|
|
57
57
|
}>;
|
|
58
|
+
/** Diagnostic only: content and effects from the preceding attempt are discarded. */
|
|
59
|
+
type LLMCallRestart = TraceTemplate<'llm_call_restarted', {
|
|
60
|
+
attempt: number;
|
|
61
|
+
fromModel: string;
|
|
62
|
+
toModel: string;
|
|
63
|
+
reason: string;
|
|
64
|
+
}>;
|
|
58
65
|
/**
|
|
59
66
|
* Emitted on streaming clients the moment the model starts writing a `■run`
|
|
60
67
|
* block — before the code is fully generated. Useful to show a
|
|
@@ -77,7 +84,7 @@ export declare namespace Traces {
|
|
|
77
84
|
started_at: number;
|
|
78
85
|
ended_at?: number;
|
|
79
86
|
} & Content;
|
|
80
|
-
type Trace = Comment | Log | ToolCall | ToolSlow | PropertyMutation | YieldTrace | LLMCallStart | LLMCallSuccess | CodeGenerationStart | ThinkSignal | CodeExecution | CodeExecutionException | InvalidCodeExceptionTrace | AbortTrace;
|
|
87
|
+
type Trace = Comment | Log | ToolCall | ToolSlow | PropertyMutation | YieldTrace | LLMCallStart | LLMCallSuccess | LLMCallRestart | CodeGenerationStart | ThinkSignal | CodeExecution | CodeExecutionException | InvalidCodeExceptionTrace | AbortTrace;
|
|
81
88
|
}
|
|
82
89
|
export type Trace = Traces.Trace;
|
|
83
90
|
export type VMExecutionResult = {
|