@warmdrift/kgauto-compiler 2.0.0-alpha.33 → 2.0.0-alpha.34
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/dist/glassbox/index.d.mts +3 -3
- package/dist/glassbox/index.d.ts +3 -3
- package/dist/glassbox-routes/index.d.mts +2 -2
- package/dist/glassbox-routes/index.d.ts +2 -2
- package/dist/index.d.mts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +325 -17
- package/dist/index.mjs +325 -17
- package/dist/{ir-DubC20e3.d.mts → ir-BAAHLfFL.d.mts} +37 -0
- package/dist/{ir-GnYPkpGo.d.ts → ir-B_ygNURd.d.ts} +37 -0
- package/dist/profiles.d.mts +1 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/{types-2QzI0Ep1.d.mts → types-BoqPXoUb.d.mts} +1 -1
- package/dist/{types-DYcO82xh.d.ts → types-CXyKj6-K.d.ts} +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { G as GlassboxEvent } from '../types-
|
|
2
|
-
export { A as AdvisoryFiredData, C as CompileDoneData, a as CompileStartData, E as ExecuteAttemptData, b as ExecuteSuccessData, F as FallbackWalkedData, c as GLASSBOX_STREAM_TTL_MS, d as GlassboxEventKind, e as GlassboxPubSub } from '../types-
|
|
3
|
-
import '../ir-
|
|
1
|
+
import { G as GlassboxEvent } from '../types-BoqPXoUb.mjs';
|
|
2
|
+
export { A as AdvisoryFiredData, C as CompileDoneData, a as CompileStartData, E as ExecuteAttemptData, b as ExecuteSuccessData, F as FallbackWalkedData, c as GLASSBOX_STREAM_TTL_MS, d as GlassboxEventKind, e as GlassboxPubSub } from '../types-BoqPXoUb.mjs';
|
|
3
|
+
import '../ir-BAAHLfFL.mjs';
|
|
4
4
|
import '../dialect.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/glassbox/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { G as GlassboxEvent } from '../types-
|
|
2
|
-
export { A as AdvisoryFiredData, C as CompileDoneData, a as CompileStartData, E as ExecuteAttemptData, b as ExecuteSuccessData, F as FallbackWalkedData, c as GLASSBOX_STREAM_TTL_MS, d as GlassboxEventKind, e as GlassboxPubSub } from '../types-
|
|
3
|
-
import '../ir-
|
|
1
|
+
import { G as GlassboxEvent } from '../types-CXyKj6-K.js';
|
|
2
|
+
export { A as AdvisoryFiredData, C as CompileDoneData, a as CompileStartData, E as ExecuteAttemptData, b as ExecuteSuccessData, F as FallbackWalkedData, c as GLASSBOX_STREAM_TTL_MS, d as GlassboxEventKind, e as GlassboxPubSub } from '../types-CXyKj6-K.js';
|
|
3
|
+
import '../ir-B_ygNURd.js';
|
|
4
4
|
import '../dialect.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as GlassboxEvent } from '../types-
|
|
2
|
-
import { i as Adapter, v as SectionKind } from '../ir-
|
|
1
|
+
import { G as GlassboxEvent } from '../types-BoqPXoUb.mjs';
|
|
2
|
+
import { i as Adapter, v as SectionKind } from '../ir-BAAHLfFL.mjs';
|
|
3
3
|
import '../dialect.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as GlassboxEvent } from '../types-
|
|
2
|
-
import { i as Adapter, v as SectionKind } from '../ir-
|
|
1
|
+
import { G as GlassboxEvent } from '../types-CXyKj6-K.js';
|
|
2
|
+
import { i as Adapter, v as SectionKind } from '../ir-B_ygNURd.js';
|
|
3
3
|
import '../dialect.js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CompilePolicy, N as NormalizedResponse, A as ApiKeys, P as ProviderOverrides, a as CompiledRequest, b as PromptIR, c as CallOptions, d as CallResult, S as SystemModelMessage, e as CompileResult, f as SectionRewrite, R as RecordInput, g as RecordOutcomeInput, O as OutcomeResult, h as OracleScore, B as BestPracticeAdvisory, i as Adapter, j as PerAxisMetrics, k as Provider, l as ChainEntry, G as Grounding } from './ir-
|
|
2
|
-
export { m as CallAttempt, n as CallError, o as ChainWithGrounding, p as Constraints, F as FallbackReason, H as HistoryCachePolicy, I as IntentDeclaration, M as Message, q as MutationApplied, r as NormalizedTokens, s as OutcomeKind, t as PerAxisMetricsByModel, u as PromptSection, v as SectionKind, T as ToolCall, w as ToolDefinition } from './ir-
|
|
1
|
+
import { C as CompilePolicy, N as NormalizedResponse, A as ApiKeys, P as ProviderOverrides, a as CompiledRequest, b as PromptIR, c as CallOptions, d as CallResult, S as SystemModelMessage, e as CompileResult, f as SectionRewrite, R as RecordInput, g as RecordOutcomeInput, O as OutcomeResult, h as OracleScore, B as BestPracticeAdvisory, i as Adapter, j as PerAxisMetrics, k as Provider, l as ChainEntry, G as Grounding } from './ir-BAAHLfFL.mjs';
|
|
2
|
+
export { m as CallAttempt, n as CallError, o as ChainWithGrounding, p as Constraints, F as FallbackReason, H as HistoryCachePolicy, I as IntentDeclaration, M as Message, q as MutationApplied, r as NormalizedTokens, s as OutcomeKind, t as PerAxisMetricsByModel, u as PromptSection, v as SectionKind, T as ToolCall, w as ToolDefinition } from './ir-BAAHLfFL.mjs';
|
|
3
3
|
import { ModelProfile } from './profiles.mjs';
|
|
4
4
|
export { ALIASES, CacheStrategy, CliffRule, LoweringSpec, RecoveryRule, StructuredOutputCapability, SystemPromptMode, allProfiles, getProfile, profilesByProvider, tryGetProfile } from './profiles.mjs';
|
|
5
5
|
import { IntentArchetypeName } from './dialect.mjs';
|
|
@@ -54,6 +54,15 @@ interface ExecuteOptions {
|
|
|
54
54
|
apiKeys?: ApiKeys;
|
|
55
55
|
fetchImpl?: typeof fetch;
|
|
56
56
|
providerOverrides?: ProviderOverrides;
|
|
57
|
+
/**
|
|
58
|
+
* alpha.34. When supplied AND the chosen provider supports streaming, the
|
|
59
|
+
* execute() per-provider branch switches to SSE wire format and invokes
|
|
60
|
+
* `onChunk(delta)` once per text-delta event. Single-shot behavior
|
|
61
|
+
* preserved when omitted (current consumers unaffected). Wired from
|
|
62
|
+
* `CallOptions.onChunk` by `call()` after gating on `profile.streaming`
|
|
63
|
+
* and `!opts.noStream`.
|
|
64
|
+
*/
|
|
65
|
+
onChunk?: (chunk: string) => void;
|
|
57
66
|
}
|
|
58
67
|
interface ExecuteOk {
|
|
59
68
|
ok: true;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CompilePolicy, N as NormalizedResponse, A as ApiKeys, P as ProviderOverrides, a as CompiledRequest, b as PromptIR, c as CallOptions, d as CallResult, S as SystemModelMessage, e as CompileResult, f as SectionRewrite, R as RecordInput, g as RecordOutcomeInput, O as OutcomeResult, h as OracleScore, B as BestPracticeAdvisory, i as Adapter, j as PerAxisMetrics, k as Provider, l as ChainEntry, G as Grounding } from './ir-
|
|
2
|
-
export { m as CallAttempt, n as CallError, o as ChainWithGrounding, p as Constraints, F as FallbackReason, H as HistoryCachePolicy, I as IntentDeclaration, M as Message, q as MutationApplied, r as NormalizedTokens, s as OutcomeKind, t as PerAxisMetricsByModel, u as PromptSection, v as SectionKind, T as ToolCall, w as ToolDefinition } from './ir-
|
|
1
|
+
import { C as CompilePolicy, N as NormalizedResponse, A as ApiKeys, P as ProviderOverrides, a as CompiledRequest, b as PromptIR, c as CallOptions, d as CallResult, S as SystemModelMessage, e as CompileResult, f as SectionRewrite, R as RecordInput, g as RecordOutcomeInput, O as OutcomeResult, h as OracleScore, B as BestPracticeAdvisory, i as Adapter, j as PerAxisMetrics, k as Provider, l as ChainEntry, G as Grounding } from './ir-B_ygNURd.js';
|
|
2
|
+
export { m as CallAttempt, n as CallError, o as ChainWithGrounding, p as Constraints, F as FallbackReason, H as HistoryCachePolicy, I as IntentDeclaration, M as Message, q as MutationApplied, r as NormalizedTokens, s as OutcomeKind, t as PerAxisMetricsByModel, u as PromptSection, v as SectionKind, T as ToolCall, w as ToolDefinition } from './ir-B_ygNURd.js';
|
|
3
3
|
import { ModelProfile } from './profiles.js';
|
|
4
4
|
export { ALIASES, CacheStrategy, CliffRule, LoweringSpec, RecoveryRule, StructuredOutputCapability, SystemPromptMode, allProfiles, getProfile, profilesByProvider, tryGetProfile } from './profiles.js';
|
|
5
5
|
import { IntentArchetypeName } from './dialect.js';
|
|
@@ -54,6 +54,15 @@ interface ExecuteOptions {
|
|
|
54
54
|
apiKeys?: ApiKeys;
|
|
55
55
|
fetchImpl?: typeof fetch;
|
|
56
56
|
providerOverrides?: ProviderOverrides;
|
|
57
|
+
/**
|
|
58
|
+
* alpha.34. When supplied AND the chosen provider supports streaming, the
|
|
59
|
+
* execute() per-provider branch switches to SSE wire format and invokes
|
|
60
|
+
* `onChunk(delta)` once per text-delta event. Single-shot behavior
|
|
61
|
+
* preserved when omitted (current consumers unaffected). Wired from
|
|
62
|
+
* `CallOptions.onChunk` by `call()` after gating on `profile.streaming`
|
|
63
|
+
* and `!opts.noStream`.
|
|
64
|
+
*/
|
|
65
|
+
onChunk?: (chunk: string) => void;
|
|
57
66
|
}
|
|
58
67
|
interface ExecuteOk {
|
|
59
68
|
ok: true;
|
package/dist/index.js
CHANGED
|
@@ -3361,8 +3361,295 @@ function getReachabilityDiagnostic(opts = {}) {
|
|
|
3361
3361
|
return out;
|
|
3362
3362
|
}
|
|
3363
3363
|
|
|
3364
|
-
// src/
|
|
3364
|
+
// src/streaming.ts
|
|
3365
3365
|
var ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
|
|
3366
|
+
async function streamAnthropic(request, apiKey, opts) {
|
|
3367
|
+
const { provider: _provider, ...body } = request;
|
|
3368
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
3369
|
+
let res;
|
|
3370
|
+
try {
|
|
3371
|
+
res = await fetchFn(ANTHROPIC_URL, {
|
|
3372
|
+
method: "POST",
|
|
3373
|
+
headers: {
|
|
3374
|
+
"x-api-key": apiKey,
|
|
3375
|
+
"anthropic-version": "2023-06-01",
|
|
3376
|
+
"content-type": "application/json"
|
|
3377
|
+
},
|
|
3378
|
+
body: JSON.stringify({ ...body, stream: true })
|
|
3379
|
+
});
|
|
3380
|
+
} catch (err) {
|
|
3381
|
+
return retryableError(0, "network_error", String(err), null);
|
|
3382
|
+
}
|
|
3383
|
+
if (!res.ok) {
|
|
3384
|
+
const errBody = await res.json().catch(() => ({}));
|
|
3385
|
+
return classifyHttpError(res.status, errBody);
|
|
3386
|
+
}
|
|
3387
|
+
let text = "";
|
|
3388
|
+
let inputTokens = 0;
|
|
3389
|
+
let outputTokens = 0;
|
|
3390
|
+
let cacheReadTokens;
|
|
3391
|
+
let cacheCreatedTokens;
|
|
3392
|
+
let stopReason;
|
|
3393
|
+
const toolBlocks = /* @__PURE__ */ new Map();
|
|
3394
|
+
try {
|
|
3395
|
+
await parseSSEStream(res, (event) => {
|
|
3396
|
+
if (!event.data) return;
|
|
3397
|
+
let payload;
|
|
3398
|
+
try {
|
|
3399
|
+
payload = JSON.parse(event.data);
|
|
3400
|
+
} catch {
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
const type = payload.type;
|
|
3404
|
+
if (type === "message_start") {
|
|
3405
|
+
const msg = payload.message;
|
|
3406
|
+
const usage = msg?.usage;
|
|
3407
|
+
if (usage) {
|
|
3408
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
3409
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
3410
|
+
if (typeof usage.cache_read_input_tokens === "number")
|
|
3411
|
+
cacheReadTokens = usage.cache_read_input_tokens;
|
|
3412
|
+
if (typeof usage.cache_creation_input_tokens === "number")
|
|
3413
|
+
cacheCreatedTokens = usage.cache_creation_input_tokens;
|
|
3414
|
+
}
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
if (type === "content_block_start") {
|
|
3418
|
+
const p = payload;
|
|
3419
|
+
const idx = p.index ?? 0;
|
|
3420
|
+
const block = p.content_block;
|
|
3421
|
+
if (block?.type === "tool_use" && block.id && block.name) {
|
|
3422
|
+
toolBlocks.set(idx, { id: block.id, name: block.name, argsJson: "" });
|
|
3423
|
+
}
|
|
3424
|
+
return;
|
|
3425
|
+
}
|
|
3426
|
+
if (type === "content_block_delta") {
|
|
3427
|
+
const p = payload;
|
|
3428
|
+
const delta = p.delta;
|
|
3429
|
+
if (!delta) return;
|
|
3430
|
+
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
3431
|
+
text += delta.text;
|
|
3432
|
+
opts.onChunk(delta.text);
|
|
3433
|
+
} else if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
3434
|
+
const idx = p.index ?? 0;
|
|
3435
|
+
const tool = toolBlocks.get(idx);
|
|
3436
|
+
if (tool) tool.argsJson += delta.partial_json;
|
|
3437
|
+
}
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
if (type === "message_delta") {
|
|
3441
|
+
const p = payload;
|
|
3442
|
+
if (p.delta?.stop_reason) stopReason = p.delta.stop_reason;
|
|
3443
|
+
if (typeof p.usage?.output_tokens === "number") outputTokens = p.usage.output_tokens;
|
|
3444
|
+
return;
|
|
3445
|
+
}
|
|
3446
|
+
});
|
|
3447
|
+
} catch (err) {
|
|
3448
|
+
return retryableError(0, "stream_interrupted", String(err), null);
|
|
3449
|
+
}
|
|
3450
|
+
const toolCalls = Array.from(toolBlocks.values()).map((b) => ({
|
|
3451
|
+
id: b.id,
|
|
3452
|
+
name: b.name,
|
|
3453
|
+
args: tryParseJson(b.argsJson) ?? {}
|
|
3454
|
+
}));
|
|
3455
|
+
const tokens = {
|
|
3456
|
+
input: inputTokens,
|
|
3457
|
+
output: outputTokens,
|
|
3458
|
+
total: inputTokens + outputTokens,
|
|
3459
|
+
cached: cacheReadTokens,
|
|
3460
|
+
cacheCreated: cacheCreatedTokens
|
|
3461
|
+
};
|
|
3462
|
+
const response = {
|
|
3463
|
+
text,
|
|
3464
|
+
structuredOutput: null,
|
|
3465
|
+
toolCalls,
|
|
3466
|
+
tokens,
|
|
3467
|
+
finishReason: stopReason,
|
|
3468
|
+
raw: { streamed: true, provider: "anthropic" }
|
|
3469
|
+
};
|
|
3470
|
+
return { ok: true, status: res.status, response };
|
|
3471
|
+
}
|
|
3472
|
+
async function streamOpenAILike(url, request, apiKey, providerLabel, opts) {
|
|
3473
|
+
const { provider: _provider, ...body } = request;
|
|
3474
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
3475
|
+
const reqBody = {
|
|
3476
|
+
...body,
|
|
3477
|
+
stream: true,
|
|
3478
|
+
stream_options: {
|
|
3479
|
+
...body.stream_options ?? {},
|
|
3480
|
+
include_usage: true
|
|
3481
|
+
}
|
|
3482
|
+
};
|
|
3483
|
+
let res;
|
|
3484
|
+
try {
|
|
3485
|
+
res = await fetchFn(url, {
|
|
3486
|
+
method: "POST",
|
|
3487
|
+
headers: {
|
|
3488
|
+
authorization: `Bearer ${apiKey}`,
|
|
3489
|
+
"content-type": "application/json"
|
|
3490
|
+
},
|
|
3491
|
+
body: JSON.stringify(reqBody)
|
|
3492
|
+
});
|
|
3493
|
+
} catch (err) {
|
|
3494
|
+
return retryableError(0, "network_error", String(err), null);
|
|
3495
|
+
}
|
|
3496
|
+
if (!res.ok) {
|
|
3497
|
+
const errBody = await res.json().catch(() => ({}));
|
|
3498
|
+
return classifyHttpError(res.status, errBody);
|
|
3499
|
+
}
|
|
3500
|
+
let text = "";
|
|
3501
|
+
let finishReason;
|
|
3502
|
+
let inputTokens = 0;
|
|
3503
|
+
let outputTokens = 0;
|
|
3504
|
+
let totalTokens;
|
|
3505
|
+
let cachedTokens;
|
|
3506
|
+
const toolBuffers = /* @__PURE__ */ new Map();
|
|
3507
|
+
try {
|
|
3508
|
+
await parseSSEStream(res, (event) => {
|
|
3509
|
+
if (!event.data) return;
|
|
3510
|
+
if (event.data === "[DONE]") return;
|
|
3511
|
+
let payload;
|
|
3512
|
+
try {
|
|
3513
|
+
payload = JSON.parse(event.data);
|
|
3514
|
+
} catch {
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
const choices = payload.choices;
|
|
3518
|
+
if (choices && choices.length > 0) {
|
|
3519
|
+
const choice = choices[0];
|
|
3520
|
+
const deltaContent = choice.delta?.content;
|
|
3521
|
+
if (typeof deltaContent === "string" && deltaContent.length > 0) {
|
|
3522
|
+
text += deltaContent;
|
|
3523
|
+
opts.onChunk(deltaContent);
|
|
3524
|
+
}
|
|
3525
|
+
const toolDeltas = choice.delta?.tool_calls;
|
|
3526
|
+
if (toolDeltas) {
|
|
3527
|
+
for (const tcDelta of toolDeltas) {
|
|
3528
|
+
const idx = tcDelta.index ?? 0;
|
|
3529
|
+
let buf = toolBuffers.get(idx);
|
|
3530
|
+
if (!buf) {
|
|
3531
|
+
buf = { id: tcDelta.id ?? `tc-${idx}`, name: "", argsJson: "" };
|
|
3532
|
+
toolBuffers.set(idx, buf);
|
|
3533
|
+
}
|
|
3534
|
+
if (tcDelta.id) buf.id = tcDelta.id;
|
|
3535
|
+
if (tcDelta.function?.name) buf.name += tcDelta.function.name;
|
|
3536
|
+
if (tcDelta.function?.arguments) buf.argsJson += tcDelta.function.arguments;
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
if (choice.finish_reason) finishReason = choice.finish_reason;
|
|
3540
|
+
}
|
|
3541
|
+
const usage = payload.usage;
|
|
3542
|
+
if (usage) {
|
|
3543
|
+
if (typeof usage.prompt_tokens === "number") inputTokens = usage.prompt_tokens;
|
|
3544
|
+
if (typeof usage.completion_tokens === "number") outputTokens = usage.completion_tokens;
|
|
3545
|
+
if (typeof usage.total_tokens === "number") totalTokens = usage.total_tokens;
|
|
3546
|
+
const details = usage.prompt_tokens_details;
|
|
3547
|
+
if (typeof details?.cached_tokens === "number") cachedTokens = details.cached_tokens;
|
|
3548
|
+
}
|
|
3549
|
+
});
|
|
3550
|
+
} catch (err) {
|
|
3551
|
+
return retryableError(0, "stream_interrupted", String(err), null);
|
|
3552
|
+
}
|
|
3553
|
+
const toolCalls = Array.from(toolBuffers.values()).filter((b) => b.name.length > 0).map((b) => ({
|
|
3554
|
+
id: b.id,
|
|
3555
|
+
name: b.name,
|
|
3556
|
+
args: tryParseJson(b.argsJson) ?? {}
|
|
3557
|
+
}));
|
|
3558
|
+
const tokens = {
|
|
3559
|
+
input: inputTokens,
|
|
3560
|
+
output: outputTokens,
|
|
3561
|
+
total: totalTokens ?? inputTokens + outputTokens,
|
|
3562
|
+
cached: cachedTokens
|
|
3563
|
+
};
|
|
3564
|
+
const response = {
|
|
3565
|
+
text,
|
|
3566
|
+
structuredOutput: null,
|
|
3567
|
+
toolCalls,
|
|
3568
|
+
tokens,
|
|
3569
|
+
finishReason,
|
|
3570
|
+
raw: { streamed: true, provider: providerLabel }
|
|
3571
|
+
};
|
|
3572
|
+
return { ok: true, status: res.status, response };
|
|
3573
|
+
}
|
|
3574
|
+
async function parseSSEStream(response, handler) {
|
|
3575
|
+
const body = response.body;
|
|
3576
|
+
if (!body) throw new Error("Response has no body for SSE parse");
|
|
3577
|
+
const reader = body.getReader();
|
|
3578
|
+
const decoder = new TextDecoder("utf-8");
|
|
3579
|
+
let buffer = "";
|
|
3580
|
+
for (; ; ) {
|
|
3581
|
+
const { value, done } = await reader.read();
|
|
3582
|
+
if (done) break;
|
|
3583
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3584
|
+
let sep;
|
|
3585
|
+
while (sep = buffer.indexOf("\n\n"), sep !== -1) {
|
|
3586
|
+
const block = buffer.slice(0, sep);
|
|
3587
|
+
buffer = buffer.slice(sep + 2);
|
|
3588
|
+
const event = parseSSEBlock(block);
|
|
3589
|
+
if (event) handler(event);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
if (buffer.length > 0) {
|
|
3593
|
+
const event = parseSSEBlock(buffer);
|
|
3594
|
+
if (event) handler(event);
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
function parseSSEBlock(block) {
|
|
3598
|
+
const lines = block.split(/\r?\n/);
|
|
3599
|
+
let eventName;
|
|
3600
|
+
const dataLines = [];
|
|
3601
|
+
for (const line of lines) {
|
|
3602
|
+
if (line.startsWith(":")) continue;
|
|
3603
|
+
if (line.startsWith("event:")) {
|
|
3604
|
+
eventName = line.slice(6).trim();
|
|
3605
|
+
} else if (line.startsWith("data:")) {
|
|
3606
|
+
dataLines.push(line.slice(5).trim());
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
if (dataLines.length === 0) return void 0;
|
|
3610
|
+
return { event: eventName, data: dataLines.join("\n") };
|
|
3611
|
+
}
|
|
3612
|
+
function tryParseJson(s) {
|
|
3613
|
+
if (typeof s !== "string" || s.length === 0) return void 0;
|
|
3614
|
+
try {
|
|
3615
|
+
const parsed = JSON.parse(s);
|
|
3616
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
3617
|
+
} catch {
|
|
3618
|
+
return void 0;
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
function classifyHttpError(status, body) {
|
|
3622
|
+
const message = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
3623
|
+
if (status === 429)
|
|
3624
|
+
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
3625
|
+
if (status === 408)
|
|
3626
|
+
return { ok: false, status, errorType: "retryable", errorCode: "timeout", message, raw: body };
|
|
3627
|
+
if (status >= 500)
|
|
3628
|
+
return { ok: false, status, errorType: "retryable", errorCode: "server_error", message, raw: body };
|
|
3629
|
+
if (status === 404)
|
|
3630
|
+
return { ok: false, status, errorType: "retryable", errorCode: "model_not_found", message, raw: body };
|
|
3631
|
+
if (status === 401 || status === 403)
|
|
3632
|
+
return { ok: false, status, errorType: "terminal", errorCode: "auth", message, raw: body };
|
|
3633
|
+
if (status === 400)
|
|
3634
|
+
return { ok: false, status, errorType: "terminal", errorCode: "invalid_request", message, raw: body };
|
|
3635
|
+
return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
|
|
3636
|
+
}
|
|
3637
|
+
function extractErrorMessage(body) {
|
|
3638
|
+
if (!body || typeof body !== "object") return void 0;
|
|
3639
|
+
const b = body;
|
|
3640
|
+
if (b.error && typeof b.error === "object") {
|
|
3641
|
+
const e = b.error;
|
|
3642
|
+
if (typeof e.message === "string") return e.message;
|
|
3643
|
+
}
|
|
3644
|
+
if (typeof b.message === "string") return b.message;
|
|
3645
|
+
return void 0;
|
|
3646
|
+
}
|
|
3647
|
+
function retryableError(status, code, message, raw) {
|
|
3648
|
+
return { ok: false, status, errorType: "retryable", errorCode: code, message, raw };
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
// src/execute.ts
|
|
3652
|
+
var ANTHROPIC_URL2 = "https://api.anthropic.com/v1/messages";
|
|
3366
3653
|
var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
|
|
3367
3654
|
var DEEPSEEK_URL = "https://api.deepseek.com/chat/completions";
|
|
3368
3655
|
async function execute(request, opts = {}) {
|
|
@@ -3387,12 +3674,18 @@ async function executeAnthropic(request, opts) {
|
|
|
3387
3674
|
if (!apiKey) {
|
|
3388
3675
|
return terminalError(401, "auth", "ANTHROPIC_API_KEY missing");
|
|
3389
3676
|
}
|
|
3677
|
+
if (opts.onChunk) {
|
|
3678
|
+
return streamAnthropic(request, apiKey, {
|
|
3679
|
+
onChunk: opts.onChunk,
|
|
3680
|
+
fetchImpl: opts.fetchImpl
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3390
3683
|
const { provider: _provider, ...body } = request;
|
|
3391
3684
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
3392
3685
|
let res;
|
|
3393
3686
|
let json;
|
|
3394
3687
|
try {
|
|
3395
|
-
res = await fetchFn(
|
|
3688
|
+
res = await fetchFn(ANTHROPIC_URL2, {
|
|
3396
3689
|
method: "POST",
|
|
3397
3690
|
headers: {
|
|
3398
3691
|
"x-api-key": apiKey,
|
|
@@ -3403,9 +3696,9 @@ async function executeAnthropic(request, opts) {
|
|
|
3403
3696
|
});
|
|
3404
3697
|
json = await res.json().catch(() => ({}));
|
|
3405
3698
|
} catch (err) {
|
|
3406
|
-
return
|
|
3699
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
3407
3700
|
}
|
|
3408
|
-
if (!res.ok) return
|
|
3701
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
3409
3702
|
return { ok: true, status: res.status, response: normalizeAnthropic(json) };
|
|
3410
3703
|
}
|
|
3411
3704
|
function normalizeAnthropic(raw) {
|
|
@@ -3439,9 +3732,9 @@ async function executeGoogle(request, opts) {
|
|
|
3439
3732
|
});
|
|
3440
3733
|
json = await res.json().catch(() => ({}));
|
|
3441
3734
|
} catch (err) {
|
|
3442
|
-
return
|
|
3735
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
3443
3736
|
}
|
|
3444
|
-
if (!res.ok) return
|
|
3737
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
3445
3738
|
return { ok: true, status: res.status, response: normalizeGoogle(json) };
|
|
3446
3739
|
}
|
|
3447
3740
|
function normalizeGoogle(raw) {
|
|
@@ -3468,6 +3761,12 @@ async function executeOpenAI(request, opts) {
|
|
|
3468
3761
|
if (!apiKey) {
|
|
3469
3762
|
return terminalError(401, "auth", "OPENAI_API_KEY missing");
|
|
3470
3763
|
}
|
|
3764
|
+
if (opts.onChunk) {
|
|
3765
|
+
return streamOpenAILike(OPENAI_URL, request, apiKey, "openai", {
|
|
3766
|
+
onChunk: opts.onChunk,
|
|
3767
|
+
fetchImpl: opts.fetchImpl
|
|
3768
|
+
});
|
|
3769
|
+
}
|
|
3471
3770
|
const { provider: _provider, ...body } = request;
|
|
3472
3771
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
3473
3772
|
let res;
|
|
@@ -3480,9 +3779,9 @@ async function executeOpenAI(request, opts) {
|
|
|
3480
3779
|
});
|
|
3481
3780
|
json = await res.json().catch(() => ({}));
|
|
3482
3781
|
} catch (err) {
|
|
3483
|
-
return
|
|
3782
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
3484
3783
|
}
|
|
3485
|
-
if (!res.ok) return
|
|
3784
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
3486
3785
|
return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
|
|
3487
3786
|
}
|
|
3488
3787
|
async function executeDeepSeek(request, opts) {
|
|
@@ -3490,6 +3789,12 @@ async function executeDeepSeek(request, opts) {
|
|
|
3490
3789
|
if (!apiKey) {
|
|
3491
3790
|
return terminalError(401, "auth", "DEEPSEEK_API_KEY missing");
|
|
3492
3791
|
}
|
|
3792
|
+
if (opts.onChunk) {
|
|
3793
|
+
return streamOpenAILike(DEEPSEEK_URL, request, apiKey, "deepseek", {
|
|
3794
|
+
onChunk: opts.onChunk,
|
|
3795
|
+
fetchImpl: opts.fetchImpl
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3493
3798
|
const { provider: _provider, ...body } = request;
|
|
3494
3799
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
3495
3800
|
let res;
|
|
@@ -3502,9 +3807,9 @@ async function executeDeepSeek(request, opts) {
|
|
|
3502
3807
|
});
|
|
3503
3808
|
json = await res.json().catch(() => ({}));
|
|
3504
3809
|
} catch (err) {
|
|
3505
|
-
return
|
|
3810
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
3506
3811
|
}
|
|
3507
|
-
if (!res.ok) return
|
|
3812
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
3508
3813
|
return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
|
|
3509
3814
|
}
|
|
3510
3815
|
function normalizeOpenAILike(raw) {
|
|
@@ -3514,7 +3819,7 @@ function normalizeOpenAILike(raw) {
|
|
|
3514
3819
|
const toolCalls = (choice?.message?.tool_calls ?? []).filter((tc) => tc.function?.name).map((tc, i) => ({
|
|
3515
3820
|
id: tc.id ?? `tc-${i}`,
|
|
3516
3821
|
name: tc.function.name,
|
|
3517
|
-
args:
|
|
3822
|
+
args: tryParseJson2(tc.function?.arguments) ?? {}
|
|
3518
3823
|
}));
|
|
3519
3824
|
const u = r.usage ?? {};
|
|
3520
3825
|
const tokens = {
|
|
@@ -3531,8 +3836,8 @@ function applyOverrides(request, overrides) {
|
|
|
3531
3836
|
if (!layer) return request;
|
|
3532
3837
|
return { ...request, ...layer };
|
|
3533
3838
|
}
|
|
3534
|
-
function
|
|
3535
|
-
const message =
|
|
3839
|
+
function classifyHttpError2(status, body) {
|
|
3840
|
+
const message = extractErrorMessage2(body) ?? `HTTP ${status}`;
|
|
3536
3841
|
if (status === 429) {
|
|
3537
3842
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
3538
3843
|
}
|
|
@@ -3553,7 +3858,7 @@ function classifyHttpError(status, body) {
|
|
|
3553
3858
|
}
|
|
3554
3859
|
return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
|
|
3555
3860
|
}
|
|
3556
|
-
function
|
|
3861
|
+
function extractErrorMessage2(body) {
|
|
3557
3862
|
if (!body || typeof body !== "object") return void 0;
|
|
3558
3863
|
const b = body;
|
|
3559
3864
|
if (b.error && typeof b.error === "object") {
|
|
@@ -3566,10 +3871,10 @@ function extractErrorMessage(body) {
|
|
|
3566
3871
|
function terminalError(status, code, message) {
|
|
3567
3872
|
return { ok: false, status, errorType: "terminal", errorCode: code, message, raw: null };
|
|
3568
3873
|
}
|
|
3569
|
-
function
|
|
3874
|
+
function retryableError2(status, code, message, raw) {
|
|
3570
3875
|
return { ok: false, status, errorType: "retryable", errorCode: code, message, raw };
|
|
3571
3876
|
}
|
|
3572
|
-
function
|
|
3877
|
+
function tryParseJson2(s) {
|
|
3573
3878
|
if (typeof s !== "string" || s.length === 0) return void 0;
|
|
3574
3879
|
try {
|
|
3575
3880
|
const parsed = JSON.parse(s);
|
|
@@ -4383,10 +4688,13 @@ async function call(ir, opts = {}) {
|
|
|
4383
4688
|
safeEmit(
|
|
4384
4689
|
() => emitExecuteAttempt(traceId, ir.appId, { model: targetModel, attemptIndex: i })
|
|
4385
4690
|
);
|
|
4691
|
+
const targetSupportsStreaming = targetProfile?.streaming === true;
|
|
4692
|
+
const streamingOnChunk = opts.onChunk && !opts.noStream && targetSupportsStreaming ? opts.onChunk : void 0;
|
|
4386
4693
|
const exec = await execute(activeCompile.request, {
|
|
4387
4694
|
apiKeys: opts.apiKeys,
|
|
4388
4695
|
fetchImpl: opts.fetchImpl,
|
|
4389
|
-
providerOverrides: opts.providerOverrides
|
|
4696
|
+
providerOverrides: opts.providerOverrides,
|
|
4697
|
+
onChunk: streamingOnChunk
|
|
4390
4698
|
});
|
|
4391
4699
|
const validated = exec.ok ? validateStructuredContract(exec, ir) : exec;
|
|
4392
4700
|
if (validated.ok) {
|
package/dist/index.mjs
CHANGED
|
@@ -1725,8 +1725,295 @@ var CallError = class extends Error {
|
|
|
1725
1725
|
}
|
|
1726
1726
|
};
|
|
1727
1727
|
|
|
1728
|
-
// src/
|
|
1728
|
+
// src/streaming.ts
|
|
1729
1729
|
var ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
|
|
1730
|
+
async function streamAnthropic(request, apiKey, opts) {
|
|
1731
|
+
const { provider: _provider, ...body } = request;
|
|
1732
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
1733
|
+
let res;
|
|
1734
|
+
try {
|
|
1735
|
+
res = await fetchFn(ANTHROPIC_URL, {
|
|
1736
|
+
method: "POST",
|
|
1737
|
+
headers: {
|
|
1738
|
+
"x-api-key": apiKey,
|
|
1739
|
+
"anthropic-version": "2023-06-01",
|
|
1740
|
+
"content-type": "application/json"
|
|
1741
|
+
},
|
|
1742
|
+
body: JSON.stringify({ ...body, stream: true })
|
|
1743
|
+
});
|
|
1744
|
+
} catch (err) {
|
|
1745
|
+
return retryableError(0, "network_error", String(err), null);
|
|
1746
|
+
}
|
|
1747
|
+
if (!res.ok) {
|
|
1748
|
+
const errBody = await res.json().catch(() => ({}));
|
|
1749
|
+
return classifyHttpError(res.status, errBody);
|
|
1750
|
+
}
|
|
1751
|
+
let text = "";
|
|
1752
|
+
let inputTokens = 0;
|
|
1753
|
+
let outputTokens = 0;
|
|
1754
|
+
let cacheReadTokens;
|
|
1755
|
+
let cacheCreatedTokens;
|
|
1756
|
+
let stopReason;
|
|
1757
|
+
const toolBlocks = /* @__PURE__ */ new Map();
|
|
1758
|
+
try {
|
|
1759
|
+
await parseSSEStream(res, (event) => {
|
|
1760
|
+
if (!event.data) return;
|
|
1761
|
+
let payload;
|
|
1762
|
+
try {
|
|
1763
|
+
payload = JSON.parse(event.data);
|
|
1764
|
+
} catch {
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
const type = payload.type;
|
|
1768
|
+
if (type === "message_start") {
|
|
1769
|
+
const msg = payload.message;
|
|
1770
|
+
const usage = msg?.usage;
|
|
1771
|
+
if (usage) {
|
|
1772
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
1773
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
1774
|
+
if (typeof usage.cache_read_input_tokens === "number")
|
|
1775
|
+
cacheReadTokens = usage.cache_read_input_tokens;
|
|
1776
|
+
if (typeof usage.cache_creation_input_tokens === "number")
|
|
1777
|
+
cacheCreatedTokens = usage.cache_creation_input_tokens;
|
|
1778
|
+
}
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
if (type === "content_block_start") {
|
|
1782
|
+
const p = payload;
|
|
1783
|
+
const idx = p.index ?? 0;
|
|
1784
|
+
const block = p.content_block;
|
|
1785
|
+
if (block?.type === "tool_use" && block.id && block.name) {
|
|
1786
|
+
toolBlocks.set(idx, { id: block.id, name: block.name, argsJson: "" });
|
|
1787
|
+
}
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
if (type === "content_block_delta") {
|
|
1791
|
+
const p = payload;
|
|
1792
|
+
const delta = p.delta;
|
|
1793
|
+
if (!delta) return;
|
|
1794
|
+
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
1795
|
+
text += delta.text;
|
|
1796
|
+
opts.onChunk(delta.text);
|
|
1797
|
+
} else if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
1798
|
+
const idx = p.index ?? 0;
|
|
1799
|
+
const tool = toolBlocks.get(idx);
|
|
1800
|
+
if (tool) tool.argsJson += delta.partial_json;
|
|
1801
|
+
}
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
if (type === "message_delta") {
|
|
1805
|
+
const p = payload;
|
|
1806
|
+
if (p.delta?.stop_reason) stopReason = p.delta.stop_reason;
|
|
1807
|
+
if (typeof p.usage?.output_tokens === "number") outputTokens = p.usage.output_tokens;
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
} catch (err) {
|
|
1812
|
+
return retryableError(0, "stream_interrupted", String(err), null);
|
|
1813
|
+
}
|
|
1814
|
+
const toolCalls = Array.from(toolBlocks.values()).map((b) => ({
|
|
1815
|
+
id: b.id,
|
|
1816
|
+
name: b.name,
|
|
1817
|
+
args: tryParseJson(b.argsJson) ?? {}
|
|
1818
|
+
}));
|
|
1819
|
+
const tokens = {
|
|
1820
|
+
input: inputTokens,
|
|
1821
|
+
output: outputTokens,
|
|
1822
|
+
total: inputTokens + outputTokens,
|
|
1823
|
+
cached: cacheReadTokens,
|
|
1824
|
+
cacheCreated: cacheCreatedTokens
|
|
1825
|
+
};
|
|
1826
|
+
const response = {
|
|
1827
|
+
text,
|
|
1828
|
+
structuredOutput: null,
|
|
1829
|
+
toolCalls,
|
|
1830
|
+
tokens,
|
|
1831
|
+
finishReason: stopReason,
|
|
1832
|
+
raw: { streamed: true, provider: "anthropic" }
|
|
1833
|
+
};
|
|
1834
|
+
return { ok: true, status: res.status, response };
|
|
1835
|
+
}
|
|
1836
|
+
async function streamOpenAILike(url, request, apiKey, providerLabel, opts) {
|
|
1837
|
+
const { provider: _provider, ...body } = request;
|
|
1838
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
1839
|
+
const reqBody = {
|
|
1840
|
+
...body,
|
|
1841
|
+
stream: true,
|
|
1842
|
+
stream_options: {
|
|
1843
|
+
...body.stream_options ?? {},
|
|
1844
|
+
include_usage: true
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
let res;
|
|
1848
|
+
try {
|
|
1849
|
+
res = await fetchFn(url, {
|
|
1850
|
+
method: "POST",
|
|
1851
|
+
headers: {
|
|
1852
|
+
authorization: `Bearer ${apiKey}`,
|
|
1853
|
+
"content-type": "application/json"
|
|
1854
|
+
},
|
|
1855
|
+
body: JSON.stringify(reqBody)
|
|
1856
|
+
});
|
|
1857
|
+
} catch (err) {
|
|
1858
|
+
return retryableError(0, "network_error", String(err), null);
|
|
1859
|
+
}
|
|
1860
|
+
if (!res.ok) {
|
|
1861
|
+
const errBody = await res.json().catch(() => ({}));
|
|
1862
|
+
return classifyHttpError(res.status, errBody);
|
|
1863
|
+
}
|
|
1864
|
+
let text = "";
|
|
1865
|
+
let finishReason;
|
|
1866
|
+
let inputTokens = 0;
|
|
1867
|
+
let outputTokens = 0;
|
|
1868
|
+
let totalTokens;
|
|
1869
|
+
let cachedTokens;
|
|
1870
|
+
const toolBuffers = /* @__PURE__ */ new Map();
|
|
1871
|
+
try {
|
|
1872
|
+
await parseSSEStream(res, (event) => {
|
|
1873
|
+
if (!event.data) return;
|
|
1874
|
+
if (event.data === "[DONE]") return;
|
|
1875
|
+
let payload;
|
|
1876
|
+
try {
|
|
1877
|
+
payload = JSON.parse(event.data);
|
|
1878
|
+
} catch {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const choices = payload.choices;
|
|
1882
|
+
if (choices && choices.length > 0) {
|
|
1883
|
+
const choice = choices[0];
|
|
1884
|
+
const deltaContent = choice.delta?.content;
|
|
1885
|
+
if (typeof deltaContent === "string" && deltaContent.length > 0) {
|
|
1886
|
+
text += deltaContent;
|
|
1887
|
+
opts.onChunk(deltaContent);
|
|
1888
|
+
}
|
|
1889
|
+
const toolDeltas = choice.delta?.tool_calls;
|
|
1890
|
+
if (toolDeltas) {
|
|
1891
|
+
for (const tcDelta of toolDeltas) {
|
|
1892
|
+
const idx = tcDelta.index ?? 0;
|
|
1893
|
+
let buf = toolBuffers.get(idx);
|
|
1894
|
+
if (!buf) {
|
|
1895
|
+
buf = { id: tcDelta.id ?? `tc-${idx}`, name: "", argsJson: "" };
|
|
1896
|
+
toolBuffers.set(idx, buf);
|
|
1897
|
+
}
|
|
1898
|
+
if (tcDelta.id) buf.id = tcDelta.id;
|
|
1899
|
+
if (tcDelta.function?.name) buf.name += tcDelta.function.name;
|
|
1900
|
+
if (tcDelta.function?.arguments) buf.argsJson += tcDelta.function.arguments;
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
if (choice.finish_reason) finishReason = choice.finish_reason;
|
|
1904
|
+
}
|
|
1905
|
+
const usage = payload.usage;
|
|
1906
|
+
if (usage) {
|
|
1907
|
+
if (typeof usage.prompt_tokens === "number") inputTokens = usage.prompt_tokens;
|
|
1908
|
+
if (typeof usage.completion_tokens === "number") outputTokens = usage.completion_tokens;
|
|
1909
|
+
if (typeof usage.total_tokens === "number") totalTokens = usage.total_tokens;
|
|
1910
|
+
const details = usage.prompt_tokens_details;
|
|
1911
|
+
if (typeof details?.cached_tokens === "number") cachedTokens = details.cached_tokens;
|
|
1912
|
+
}
|
|
1913
|
+
});
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
return retryableError(0, "stream_interrupted", String(err), null);
|
|
1916
|
+
}
|
|
1917
|
+
const toolCalls = Array.from(toolBuffers.values()).filter((b) => b.name.length > 0).map((b) => ({
|
|
1918
|
+
id: b.id,
|
|
1919
|
+
name: b.name,
|
|
1920
|
+
args: tryParseJson(b.argsJson) ?? {}
|
|
1921
|
+
}));
|
|
1922
|
+
const tokens = {
|
|
1923
|
+
input: inputTokens,
|
|
1924
|
+
output: outputTokens,
|
|
1925
|
+
total: totalTokens ?? inputTokens + outputTokens,
|
|
1926
|
+
cached: cachedTokens
|
|
1927
|
+
};
|
|
1928
|
+
const response = {
|
|
1929
|
+
text,
|
|
1930
|
+
structuredOutput: null,
|
|
1931
|
+
toolCalls,
|
|
1932
|
+
tokens,
|
|
1933
|
+
finishReason,
|
|
1934
|
+
raw: { streamed: true, provider: providerLabel }
|
|
1935
|
+
};
|
|
1936
|
+
return { ok: true, status: res.status, response };
|
|
1937
|
+
}
|
|
1938
|
+
async function parseSSEStream(response, handler) {
|
|
1939
|
+
const body = response.body;
|
|
1940
|
+
if (!body) throw new Error("Response has no body for SSE parse");
|
|
1941
|
+
const reader = body.getReader();
|
|
1942
|
+
const decoder = new TextDecoder("utf-8");
|
|
1943
|
+
let buffer = "";
|
|
1944
|
+
for (; ; ) {
|
|
1945
|
+
const { value, done } = await reader.read();
|
|
1946
|
+
if (done) break;
|
|
1947
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1948
|
+
let sep;
|
|
1949
|
+
while (sep = buffer.indexOf("\n\n"), sep !== -1) {
|
|
1950
|
+
const block = buffer.slice(0, sep);
|
|
1951
|
+
buffer = buffer.slice(sep + 2);
|
|
1952
|
+
const event = parseSSEBlock(block);
|
|
1953
|
+
if (event) handler(event);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
if (buffer.length > 0) {
|
|
1957
|
+
const event = parseSSEBlock(buffer);
|
|
1958
|
+
if (event) handler(event);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
function parseSSEBlock(block) {
|
|
1962
|
+
const lines = block.split(/\r?\n/);
|
|
1963
|
+
let eventName;
|
|
1964
|
+
const dataLines = [];
|
|
1965
|
+
for (const line of lines) {
|
|
1966
|
+
if (line.startsWith(":")) continue;
|
|
1967
|
+
if (line.startsWith("event:")) {
|
|
1968
|
+
eventName = line.slice(6).trim();
|
|
1969
|
+
} else if (line.startsWith("data:")) {
|
|
1970
|
+
dataLines.push(line.slice(5).trim());
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
if (dataLines.length === 0) return void 0;
|
|
1974
|
+
return { event: eventName, data: dataLines.join("\n") };
|
|
1975
|
+
}
|
|
1976
|
+
function tryParseJson(s) {
|
|
1977
|
+
if (typeof s !== "string" || s.length === 0) return void 0;
|
|
1978
|
+
try {
|
|
1979
|
+
const parsed = JSON.parse(s);
|
|
1980
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
1981
|
+
} catch {
|
|
1982
|
+
return void 0;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
function classifyHttpError(status, body) {
|
|
1986
|
+
const message = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
1987
|
+
if (status === 429)
|
|
1988
|
+
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
1989
|
+
if (status === 408)
|
|
1990
|
+
return { ok: false, status, errorType: "retryable", errorCode: "timeout", message, raw: body };
|
|
1991
|
+
if (status >= 500)
|
|
1992
|
+
return { ok: false, status, errorType: "retryable", errorCode: "server_error", message, raw: body };
|
|
1993
|
+
if (status === 404)
|
|
1994
|
+
return { ok: false, status, errorType: "retryable", errorCode: "model_not_found", message, raw: body };
|
|
1995
|
+
if (status === 401 || status === 403)
|
|
1996
|
+
return { ok: false, status, errorType: "terminal", errorCode: "auth", message, raw: body };
|
|
1997
|
+
if (status === 400)
|
|
1998
|
+
return { ok: false, status, errorType: "terminal", errorCode: "invalid_request", message, raw: body };
|
|
1999
|
+
return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
|
|
2000
|
+
}
|
|
2001
|
+
function extractErrorMessage(body) {
|
|
2002
|
+
if (!body || typeof body !== "object") return void 0;
|
|
2003
|
+
const b = body;
|
|
2004
|
+
if (b.error && typeof b.error === "object") {
|
|
2005
|
+
const e = b.error;
|
|
2006
|
+
if (typeof e.message === "string") return e.message;
|
|
2007
|
+
}
|
|
2008
|
+
if (typeof b.message === "string") return b.message;
|
|
2009
|
+
return void 0;
|
|
2010
|
+
}
|
|
2011
|
+
function retryableError(status, code, message, raw) {
|
|
2012
|
+
return { ok: false, status, errorType: "retryable", errorCode: code, message, raw };
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
// src/execute.ts
|
|
2016
|
+
var ANTHROPIC_URL2 = "https://api.anthropic.com/v1/messages";
|
|
1730
2017
|
var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
|
|
1731
2018
|
var DEEPSEEK_URL = "https://api.deepseek.com/chat/completions";
|
|
1732
2019
|
async function execute(request, opts = {}) {
|
|
@@ -1751,12 +2038,18 @@ async function executeAnthropic(request, opts) {
|
|
|
1751
2038
|
if (!apiKey) {
|
|
1752
2039
|
return terminalError(401, "auth", "ANTHROPIC_API_KEY missing");
|
|
1753
2040
|
}
|
|
2041
|
+
if (opts.onChunk) {
|
|
2042
|
+
return streamAnthropic(request, apiKey, {
|
|
2043
|
+
onChunk: opts.onChunk,
|
|
2044
|
+
fetchImpl: opts.fetchImpl
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
1754
2047
|
const { provider: _provider, ...body } = request;
|
|
1755
2048
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
1756
2049
|
let res;
|
|
1757
2050
|
let json;
|
|
1758
2051
|
try {
|
|
1759
|
-
res = await fetchFn(
|
|
2052
|
+
res = await fetchFn(ANTHROPIC_URL2, {
|
|
1760
2053
|
method: "POST",
|
|
1761
2054
|
headers: {
|
|
1762
2055
|
"x-api-key": apiKey,
|
|
@@ -1767,9 +2060,9 @@ async function executeAnthropic(request, opts) {
|
|
|
1767
2060
|
});
|
|
1768
2061
|
json = await res.json().catch(() => ({}));
|
|
1769
2062
|
} catch (err) {
|
|
1770
|
-
return
|
|
2063
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
1771
2064
|
}
|
|
1772
|
-
if (!res.ok) return
|
|
2065
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
1773
2066
|
return { ok: true, status: res.status, response: normalizeAnthropic(json) };
|
|
1774
2067
|
}
|
|
1775
2068
|
function normalizeAnthropic(raw) {
|
|
@@ -1803,9 +2096,9 @@ async function executeGoogle(request, opts) {
|
|
|
1803
2096
|
});
|
|
1804
2097
|
json = await res.json().catch(() => ({}));
|
|
1805
2098
|
} catch (err) {
|
|
1806
|
-
return
|
|
2099
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
1807
2100
|
}
|
|
1808
|
-
if (!res.ok) return
|
|
2101
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
1809
2102
|
return { ok: true, status: res.status, response: normalizeGoogle(json) };
|
|
1810
2103
|
}
|
|
1811
2104
|
function normalizeGoogle(raw) {
|
|
@@ -1832,6 +2125,12 @@ async function executeOpenAI(request, opts) {
|
|
|
1832
2125
|
if (!apiKey) {
|
|
1833
2126
|
return terminalError(401, "auth", "OPENAI_API_KEY missing");
|
|
1834
2127
|
}
|
|
2128
|
+
if (opts.onChunk) {
|
|
2129
|
+
return streamOpenAILike(OPENAI_URL, request, apiKey, "openai", {
|
|
2130
|
+
onChunk: opts.onChunk,
|
|
2131
|
+
fetchImpl: opts.fetchImpl
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
1835
2134
|
const { provider: _provider, ...body } = request;
|
|
1836
2135
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
1837
2136
|
let res;
|
|
@@ -1844,9 +2143,9 @@ async function executeOpenAI(request, opts) {
|
|
|
1844
2143
|
});
|
|
1845
2144
|
json = await res.json().catch(() => ({}));
|
|
1846
2145
|
} catch (err) {
|
|
1847
|
-
return
|
|
2146
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
1848
2147
|
}
|
|
1849
|
-
if (!res.ok) return
|
|
2148
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
1850
2149
|
return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
|
|
1851
2150
|
}
|
|
1852
2151
|
async function executeDeepSeek(request, opts) {
|
|
@@ -1854,6 +2153,12 @@ async function executeDeepSeek(request, opts) {
|
|
|
1854
2153
|
if (!apiKey) {
|
|
1855
2154
|
return terminalError(401, "auth", "DEEPSEEK_API_KEY missing");
|
|
1856
2155
|
}
|
|
2156
|
+
if (opts.onChunk) {
|
|
2157
|
+
return streamOpenAILike(DEEPSEEK_URL, request, apiKey, "deepseek", {
|
|
2158
|
+
onChunk: opts.onChunk,
|
|
2159
|
+
fetchImpl: opts.fetchImpl
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
1857
2162
|
const { provider: _provider, ...body } = request;
|
|
1858
2163
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
1859
2164
|
let res;
|
|
@@ -1866,9 +2171,9 @@ async function executeDeepSeek(request, opts) {
|
|
|
1866
2171
|
});
|
|
1867
2172
|
json = await res.json().catch(() => ({}));
|
|
1868
2173
|
} catch (err) {
|
|
1869
|
-
return
|
|
2174
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
1870
2175
|
}
|
|
1871
|
-
if (!res.ok) return
|
|
2176
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
1872
2177
|
return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
|
|
1873
2178
|
}
|
|
1874
2179
|
function normalizeOpenAILike(raw) {
|
|
@@ -1878,7 +2183,7 @@ function normalizeOpenAILike(raw) {
|
|
|
1878
2183
|
const toolCalls = (choice?.message?.tool_calls ?? []).filter((tc) => tc.function?.name).map((tc, i) => ({
|
|
1879
2184
|
id: tc.id ?? `tc-${i}`,
|
|
1880
2185
|
name: tc.function.name,
|
|
1881
|
-
args:
|
|
2186
|
+
args: tryParseJson2(tc.function?.arguments) ?? {}
|
|
1882
2187
|
}));
|
|
1883
2188
|
const u = r.usage ?? {};
|
|
1884
2189
|
const tokens = {
|
|
@@ -1895,8 +2200,8 @@ function applyOverrides(request, overrides) {
|
|
|
1895
2200
|
if (!layer) return request;
|
|
1896
2201
|
return { ...request, ...layer };
|
|
1897
2202
|
}
|
|
1898
|
-
function
|
|
1899
|
-
const message =
|
|
2203
|
+
function classifyHttpError2(status, body) {
|
|
2204
|
+
const message = extractErrorMessage2(body) ?? `HTTP ${status}`;
|
|
1900
2205
|
if (status === 429) {
|
|
1901
2206
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
1902
2207
|
}
|
|
@@ -1917,7 +2222,7 @@ function classifyHttpError(status, body) {
|
|
|
1917
2222
|
}
|
|
1918
2223
|
return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
|
|
1919
2224
|
}
|
|
1920
|
-
function
|
|
2225
|
+
function extractErrorMessage2(body) {
|
|
1921
2226
|
if (!body || typeof body !== "object") return void 0;
|
|
1922
2227
|
const b = body;
|
|
1923
2228
|
if (b.error && typeof b.error === "object") {
|
|
@@ -1930,10 +2235,10 @@ function extractErrorMessage(body) {
|
|
|
1930
2235
|
function terminalError(status, code, message) {
|
|
1931
2236
|
return { ok: false, status, errorType: "terminal", errorCode: code, message, raw: null };
|
|
1932
2237
|
}
|
|
1933
|
-
function
|
|
2238
|
+
function retryableError2(status, code, message, raw) {
|
|
1934
2239
|
return { ok: false, status, errorType: "retryable", errorCode: code, message, raw };
|
|
1935
2240
|
}
|
|
1936
|
-
function
|
|
2241
|
+
function tryParseJson2(s) {
|
|
1937
2242
|
if (typeof s !== "string" || s.length === 0) return void 0;
|
|
1938
2243
|
try {
|
|
1939
2244
|
const parsed = JSON.parse(s);
|
|
@@ -2103,10 +2408,13 @@ async function call(ir, opts = {}) {
|
|
|
2103
2408
|
safeEmit(
|
|
2104
2409
|
() => emitExecuteAttempt(traceId, ir.appId, { model: targetModel, attemptIndex: i })
|
|
2105
2410
|
);
|
|
2411
|
+
const targetSupportsStreaming = targetProfile?.streaming === true;
|
|
2412
|
+
const streamingOnChunk = opts.onChunk && !opts.noStream && targetSupportsStreaming ? opts.onChunk : void 0;
|
|
2106
2413
|
const exec = await execute(activeCompile.request, {
|
|
2107
2414
|
apiKeys: opts.apiKeys,
|
|
2108
2415
|
fetchImpl: opts.fetchImpl,
|
|
2109
|
-
providerOverrides: opts.providerOverrides
|
|
2416
|
+
providerOverrides: opts.providerOverrides,
|
|
2417
|
+
onChunk: streamingOnChunk
|
|
2110
2418
|
});
|
|
2111
2419
|
const validated = exec.ok ? validateStructuredContract(exec, ir) : exec;
|
|
2112
2420
|
if (validated.ok) {
|
|
@@ -733,6 +733,43 @@ interface CallOptions {
|
|
|
733
733
|
* for hermetic test runs.
|
|
734
734
|
*/
|
|
735
735
|
noAutoFilter?: boolean;
|
|
736
|
+
/**
|
|
737
|
+
* alpha.34. When provided AND the chosen provider supports streaming
|
|
738
|
+
* (`profile.streaming === true`) AND `noStream` is not set, kgauto
|
|
739
|
+
* enables provider-native SSE streaming at the wire layer and invokes
|
|
740
|
+
* `onChunk(delta)` once per provider stream event. `delta` is the text
|
|
741
|
+
* since the previous `onChunk` call (NOT cumulative) — provider
|
|
742
|
+
* stream-shape headache normalized in kgauto's lowering layer.
|
|
743
|
+
*
|
|
744
|
+
* `CallResult.response.text` is still populated with the full assembled
|
|
745
|
+
* response — kgauto buffers internally regardless of the callback.
|
|
746
|
+
* Consumers can use either the streaming side-effect OR the final
|
|
747
|
+
* `response.text`, doesn't matter.
|
|
748
|
+
*
|
|
749
|
+
* Tool calls + finish reason + usage are collected from stream events
|
|
750
|
+
* and returned in the final `CallResult` exactly as the non-streaming
|
|
751
|
+
* path would shape them. Brain telemetry latency = time-to-stream-end.
|
|
752
|
+
*
|
|
753
|
+
* Chain-walk semantics: if the streaming target fails mid-stream
|
|
754
|
+
* (network error, retryable provider error), kgauto walks to the next
|
|
755
|
+
* fallback target and restarts streaming from its first chunk —
|
|
756
|
+
* `onChunk` fires fresh from the new target. Consumer detects via the
|
|
757
|
+
* post-call `CallResult.fellOverFrom`. To opt out of fallback for
|
|
758
|
+
* streaming, set `noFallback: true` alongside `onChunk`.
|
|
759
|
+
*
|
|
760
|
+
* Filed by playbacksam s42 (2026-05-22) as `streaming-output-callback-
|
|
761
|
+
* on-callresult` for ComposeDrawer SSE; perceived-latency UX win
|
|
762
|
+
* during 10-30s draft assembly.
|
|
763
|
+
*/
|
|
764
|
+
onChunk?: (chunk: string) => void;
|
|
765
|
+
/**
|
|
766
|
+
* alpha.34. Explicit opt-out of streaming even when `onChunk` is
|
|
767
|
+
* provided. Default: false (streaming enabled when `onChunk` is set).
|
|
768
|
+
* Use when the consumer wants to pass `onChunk` conditionally without
|
|
769
|
+
* branching the call site (e.g., capture chunks for instrumentation
|
|
770
|
+
* without engaging streaming wire format).
|
|
771
|
+
*/
|
|
772
|
+
noStream?: boolean;
|
|
736
773
|
}
|
|
737
774
|
interface CallAttempt {
|
|
738
775
|
model: string;
|
|
@@ -733,6 +733,43 @@ interface CallOptions {
|
|
|
733
733
|
* for hermetic test runs.
|
|
734
734
|
*/
|
|
735
735
|
noAutoFilter?: boolean;
|
|
736
|
+
/**
|
|
737
|
+
* alpha.34. When provided AND the chosen provider supports streaming
|
|
738
|
+
* (`profile.streaming === true`) AND `noStream` is not set, kgauto
|
|
739
|
+
* enables provider-native SSE streaming at the wire layer and invokes
|
|
740
|
+
* `onChunk(delta)` once per provider stream event. `delta` is the text
|
|
741
|
+
* since the previous `onChunk` call (NOT cumulative) — provider
|
|
742
|
+
* stream-shape headache normalized in kgauto's lowering layer.
|
|
743
|
+
*
|
|
744
|
+
* `CallResult.response.text` is still populated with the full assembled
|
|
745
|
+
* response — kgauto buffers internally regardless of the callback.
|
|
746
|
+
* Consumers can use either the streaming side-effect OR the final
|
|
747
|
+
* `response.text`, doesn't matter.
|
|
748
|
+
*
|
|
749
|
+
* Tool calls + finish reason + usage are collected from stream events
|
|
750
|
+
* and returned in the final `CallResult` exactly as the non-streaming
|
|
751
|
+
* path would shape them. Brain telemetry latency = time-to-stream-end.
|
|
752
|
+
*
|
|
753
|
+
* Chain-walk semantics: if the streaming target fails mid-stream
|
|
754
|
+
* (network error, retryable provider error), kgauto walks to the next
|
|
755
|
+
* fallback target and restarts streaming from its first chunk —
|
|
756
|
+
* `onChunk` fires fresh from the new target. Consumer detects via the
|
|
757
|
+
* post-call `CallResult.fellOverFrom`. To opt out of fallback for
|
|
758
|
+
* streaming, set `noFallback: true` alongside `onChunk`.
|
|
759
|
+
*
|
|
760
|
+
* Filed by playbacksam s42 (2026-05-22) as `streaming-output-callback-
|
|
761
|
+
* on-callresult` for ComposeDrawer SSE; perceived-latency UX win
|
|
762
|
+
* during 10-30s draft assembly.
|
|
763
|
+
*/
|
|
764
|
+
onChunk?: (chunk: string) => void;
|
|
765
|
+
/**
|
|
766
|
+
* alpha.34. Explicit opt-out of streaming even when `onChunk` is
|
|
767
|
+
* provided. Default: false (streaming enabled when `onChunk` is set).
|
|
768
|
+
* Use when the consumer wants to pass `onChunk` conditionally without
|
|
769
|
+
* branching the call site (e.g., capture chunks for instrumentation
|
|
770
|
+
* without engaging streaming wire format).
|
|
771
|
+
*/
|
|
772
|
+
noStream?: boolean;
|
|
736
773
|
}
|
|
737
774
|
interface CallAttempt {
|
|
738
775
|
model: string;
|
package/dist/profiles.d.mts
CHANGED
package/dist/profiles.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { q as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-
|
|
1
|
+
import { q as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-BAAHLfFL.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Glass-Box observability types (alpha.17).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { q as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-
|
|
1
|
+
import { q as MutationApplied, B as BestPracticeAdvisory, F as FallbackReason, m as CallAttempt } from './ir-B_ygNURd.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Glass-Box observability types (alpha.17).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warmdrift/kgauto-compiler",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.34",
|
|
4
4
|
"description": "Prompt compiler + central learning brain for multi-model AI apps. Swap models without rewriting prompts.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|