@vedmalex/ai-connect 0.9.0 → 0.10.0
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/browser/index.js +353 -134
- package/dist/browser/index.js.map +2 -2
- package/dist/bun/index.js +354 -135
- package/dist/bun/index.js.map +2 -2
- package/dist/bun/local.js +354 -135
- package/dist/bun/local.js.map +2 -2
- package/dist/node/index.js +354 -135
- package/dist/node/index.js.map +2 -2
- package/dist/node/local.js +354 -135
- package/dist/node/local.js.map +2 -2
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/types.d.ts +22 -0
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bun/index.js
CHANGED
|
@@ -2629,6 +2629,73 @@ function normalizeToolResult(result) {
|
|
|
2629
2629
|
...result.isError !== void 0 ? { isError: result.isError } : {}
|
|
2630
2630
|
};
|
|
2631
2631
|
}
|
|
2632
|
+
var MAX_CLIENT_TOOL_LOOP_DEPTH = 8;
|
|
2633
|
+
async function* executeToolCallsRound(params) {
|
|
2634
|
+
const { toolCalls, registry, route, request, runtime, messages, abort } = params;
|
|
2635
|
+
const assistantMessage = {
|
|
2636
|
+
role: "assistant",
|
|
2637
|
+
content: params.assistantText,
|
|
2638
|
+
toolCalls
|
|
2639
|
+
};
|
|
2640
|
+
const toolMessages = [];
|
|
2641
|
+
for (const toolCall of toolCalls) {
|
|
2642
|
+
const tool = registry.get(toolCall.name);
|
|
2643
|
+
if (!tool) {
|
|
2644
|
+
throw new ClientToolExecutionError(
|
|
2645
|
+
new AiConnectError(
|
|
2646
|
+
"validation_error",
|
|
2647
|
+
`No client tool handler is registered for "${toolCall.name}".`
|
|
2648
|
+
)
|
|
2649
|
+
);
|
|
2650
|
+
}
|
|
2651
|
+
if (abort.signal.aborted) {
|
|
2652
|
+
throw mapAbortError(abort.reason);
|
|
2653
|
+
}
|
|
2654
|
+
const startEvent = params.onToolStart?.(toolCall);
|
|
2655
|
+
if (startEvent) {
|
|
2656
|
+
yield startEvent;
|
|
2657
|
+
}
|
|
2658
|
+
let execution;
|
|
2659
|
+
try {
|
|
2660
|
+
execution = normalizeToolResult(
|
|
2661
|
+
await tool.execute(toolCall.arguments, {
|
|
2662
|
+
route,
|
|
2663
|
+
request,
|
|
2664
|
+
runtime,
|
|
2665
|
+
toolCall,
|
|
2666
|
+
messages,
|
|
2667
|
+
...request.workingDirectory ? { workingDirectory: request.workingDirectory } : {}
|
|
2668
|
+
})
|
|
2669
|
+
);
|
|
2670
|
+
} catch (error) {
|
|
2671
|
+
if (abort.signal.aborted) {
|
|
2672
|
+
throw mapAbortError(abort.reason);
|
|
2673
|
+
}
|
|
2674
|
+
throw new ClientToolExecutionError(
|
|
2675
|
+
new AiConnectError(
|
|
2676
|
+
"validation_error",
|
|
2677
|
+
`Client tool "${tool.function.name}" threw during execution: ${error instanceof Error ? error.message : String(error)}`,
|
|
2678
|
+
{ cause: error }
|
|
2679
|
+
)
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
toolMessages.push({
|
|
2683
|
+
role: "tool",
|
|
2684
|
+
content: execution.content,
|
|
2685
|
+
toolName: tool.function.name,
|
|
2686
|
+
toolCallId: toolCall.id,
|
|
2687
|
+
...execution.data !== void 0 ? { data: execution.data } : {},
|
|
2688
|
+
...execution.isError !== void 0 ? { isError: execution.isError } : {}
|
|
2689
|
+
});
|
|
2690
|
+
const finishEvent = params.onToolFinish?.(toolCall, {
|
|
2691
|
+
...execution.isError !== void 0 ? { isError: execution.isError } : {}
|
|
2692
|
+
});
|
|
2693
|
+
if (finishEvent) {
|
|
2694
|
+
yield finishEvent;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return { assistantMessage, toolMessages };
|
|
2698
|
+
}
|
|
2632
2699
|
function sumOptionalNumbers(left, right) {
|
|
2633
2700
|
if (left === void 0) {
|
|
2634
2701
|
return right;
|
|
@@ -3232,7 +3299,7 @@ function createClient(configOrInput, options = {}) {
|
|
|
3232
3299
|
let output = initialOutput;
|
|
3233
3300
|
let messages = [...request.messages];
|
|
3234
3301
|
let transportSummary2;
|
|
3235
|
-
for (let round = 0;
|
|
3302
|
+
for (let round = 0; ; round += 1) {
|
|
3236
3303
|
const toolCalls = output.toolCalls ?? [];
|
|
3237
3304
|
if (toolCalls.length === 0) {
|
|
3238
3305
|
return {
|
|
@@ -3244,62 +3311,36 @@ function createClient(configOrInput, options = {}) {
|
|
|
3244
3311
|
...transportSummary2 ? { transportSummary: transportSummary2 } : {}
|
|
3245
3312
|
};
|
|
3246
3313
|
}
|
|
3247
|
-
const
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
let execution;
|
|
3267
|
-
try {
|
|
3268
|
-
execution = normalizeToolResult(
|
|
3269
|
-
await tool.execute(toolCall.arguments, {
|
|
3270
|
-
route,
|
|
3271
|
-
request,
|
|
3272
|
-
runtime,
|
|
3273
|
-
toolCall,
|
|
3274
|
-
messages,
|
|
3275
|
-
...request.workingDirectory ? { workingDirectory: request.workingDirectory } : {}
|
|
3276
|
-
})
|
|
3277
|
-
);
|
|
3278
|
-
} catch (error) {
|
|
3279
|
-
if (abort.signal.aborted) {
|
|
3280
|
-
throw mapAbortError(abort.reason);
|
|
3281
|
-
}
|
|
3282
|
-
throw new ClientToolExecutionError(
|
|
3283
|
-
new AiConnectError(
|
|
3284
|
-
"validation_error",
|
|
3285
|
-
`Client tool "${tool.function.name}" threw during execution: ${error instanceof Error ? error.message : String(error)}`,
|
|
3286
|
-
{ cause: error }
|
|
3287
|
-
)
|
|
3288
|
-
);
|
|
3289
|
-
}
|
|
3290
|
-
toolMessages.push({
|
|
3291
|
-
role: "tool",
|
|
3292
|
-
content: execution.content,
|
|
3293
|
-
toolName: tool.function.name,
|
|
3294
|
-
toolCallId: toolCall.id,
|
|
3295
|
-
...execution.data !== void 0 ? { data: execution.data } : {},
|
|
3296
|
-
...execution.isError !== void 0 ? { isError: execution.isError } : {}
|
|
3297
|
-
});
|
|
3298
|
-
}
|
|
3299
|
-
messages = [...messages, assistantMessage, ...toolMessages];
|
|
3314
|
+
const roundIterator = executeToolCallsRound({
|
|
3315
|
+
toolCalls,
|
|
3316
|
+
assistantText: output.text ?? "",
|
|
3317
|
+
registry,
|
|
3318
|
+
route,
|
|
3319
|
+
request,
|
|
3320
|
+
runtime,
|
|
3321
|
+
messages,
|
|
3322
|
+
abort
|
|
3323
|
+
});
|
|
3324
|
+
let roundStep = await roundIterator.next();
|
|
3325
|
+
while (!roundStep.done) {
|
|
3326
|
+
roundStep = await roundIterator.next();
|
|
3327
|
+
}
|
|
3328
|
+
messages = [
|
|
3329
|
+
...messages,
|
|
3330
|
+
roundStep.value.assistantMessage,
|
|
3331
|
+
...roundStep.value.toolMessages
|
|
3332
|
+
];
|
|
3300
3333
|
if (abort.signal.aborted) {
|
|
3301
3334
|
throw mapAbortError(abort.reason);
|
|
3302
3335
|
}
|
|
3336
|
+
if (round + 1 >= MAX_CLIENT_TOOL_LOOP_DEPTH) {
|
|
3337
|
+
throw new ClientToolExecutionError(
|
|
3338
|
+
new AiConnectError(
|
|
3339
|
+
"validation_error",
|
|
3340
|
+
`clientTools exceeded the maximum tool-call loop depth of ${MAX_CLIENT_TOOL_LOOP_DEPTH}.`
|
|
3341
|
+
)
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3303
3344
|
output = await runWithAbortRace(
|
|
3304
3345
|
handler({
|
|
3305
3346
|
route,
|
|
@@ -3322,12 +3363,6 @@ function createClient(configOrInput, options = {}) {
|
|
|
3322
3363
|
warnings.push(...output.warnings ?? []);
|
|
3323
3364
|
usage = mergeUsage(usage, output.usage);
|
|
3324
3365
|
}
|
|
3325
|
-
throw new ClientToolExecutionError(
|
|
3326
|
-
new AiConnectError(
|
|
3327
|
-
"validation_error",
|
|
3328
|
-
"clientTools exceeded the maximum tool-call loop depth of 8."
|
|
3329
|
-
)
|
|
3330
|
-
);
|
|
3331
3366
|
}
|
|
3332
3367
|
async function emitWideEvent(event) {
|
|
3333
3368
|
if (!logging?.logger) {
|
|
@@ -4387,12 +4422,6 @@ function createClient(configOrInput, options = {}) {
|
|
|
4387
4422
|
},
|
|
4388
4423
|
async *stream(request, opts) {
|
|
4389
4424
|
const preparedRequest = prepareRequest(request);
|
|
4390
|
-
if (preparedRequest.clientTools?.length) {
|
|
4391
|
-
throw new AiConnectError(
|
|
4392
|
-
"not_supported",
|
|
4393
|
-
"clientTools are currently supported only by generate(), not stream()."
|
|
4394
|
-
);
|
|
4395
|
-
}
|
|
4396
4425
|
const { abort, dispose: disposeAbort } = deriveAbort(
|
|
4397
4426
|
opts,
|
|
4398
4427
|
resolveOpTimeout(opts, "generateMs", options.timeouts),
|
|
@@ -4412,7 +4441,12 @@ function createClient(configOrInput, options = {}) {
|
|
|
4412
4441
|
release = await limiter.acquire(abort.signal);
|
|
4413
4442
|
const selectedModel = await resolveSelectedModel(preparedRequest);
|
|
4414
4443
|
const normalizedRequest = withSelectedModel(preparedRequest, selectedModel);
|
|
4415
|
-
const {
|
|
4444
|
+
const {
|
|
4445
|
+
requiresImage,
|
|
4446
|
+
requiresFile,
|
|
4447
|
+
requiresClientTools,
|
|
4448
|
+
request: capabilityScopedRequest
|
|
4449
|
+
} = resolveRequiredCapabilities(normalizedRequest);
|
|
4416
4450
|
const candidates = router.resolveCandidates(capabilityScopedRequest);
|
|
4417
4451
|
const startedAt = nowMs(runtime);
|
|
4418
4452
|
const requestId = resolveEventRequestId(normalizedRequest.logContext);
|
|
@@ -4420,10 +4454,12 @@ function createClient(configOrInput, options = {}) {
|
|
|
4420
4454
|
const requestSummary = summarizeRequest(normalizedRequest, candidateSummaries);
|
|
4421
4455
|
const attempts = [];
|
|
4422
4456
|
const wideAttempts = [];
|
|
4457
|
+
let hasYieldedOutward = false;
|
|
4458
|
+
let sawStreamHandler = false;
|
|
4423
4459
|
if (candidates.length === 0) {
|
|
4424
|
-
const error = requiresImage || requiresFile ? new AiConnectError(
|
|
4460
|
+
const error = requiresImage || requiresFile || requiresClientTools ? new AiConnectError(
|
|
4425
4461
|
"unsupported_capability",
|
|
4426
|
-
`No eligible route supports the required ${requiresFile ? "document/file" : "image"} input for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`
|
|
4462
|
+
requiresImage || requiresFile ? `No eligible route supports the required ${requiresFile ? "document/file" : "image"} input for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".` : `No eligible route supports client-side tool execution for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`
|
|
4427
4463
|
) : new AiConnectError("not_supported", `No eligible routes are available for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`);
|
|
4428
4464
|
await emitWideEvent({
|
|
4429
4465
|
timestamp: isoTimestamp(startedAt),
|
|
@@ -4451,84 +4487,202 @@ function createClient(configOrInput, options = {}) {
|
|
|
4451
4487
|
continue;
|
|
4452
4488
|
}
|
|
4453
4489
|
if (handler.stream) {
|
|
4490
|
+
sawStreamHandler = true;
|
|
4454
4491
|
const routeSummary = summarizeRoute(route);
|
|
4455
4492
|
const attemptStartedAt = nowMs(runtime);
|
|
4456
4493
|
let lastResult;
|
|
4457
4494
|
let accumulatedText = "";
|
|
4458
4495
|
const routeRequest = applyRouteDefaults(route, normalizedRequest);
|
|
4459
|
-
const
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
let iteratorReturned = false;
|
|
4468
|
-
const returnIterator = async () => {
|
|
4469
|
-
if (iteratorReturned) {
|
|
4470
|
-
return;
|
|
4471
|
-
}
|
|
4472
|
-
iteratorReturned = true;
|
|
4473
|
-
try {
|
|
4474
|
-
await iterator.return?.();
|
|
4475
|
-
} catch {
|
|
4476
|
-
}
|
|
4477
|
-
};
|
|
4496
|
+
const roundTools = routeRequest.clientTools ?? [];
|
|
4497
|
+
const registry = new Map(
|
|
4498
|
+
roundTools.map((tool) => [tool.function.name, tool])
|
|
4499
|
+
);
|
|
4500
|
+
let loopMessages = routeRequest.messages;
|
|
4501
|
+
let aggregatedUsage;
|
|
4502
|
+
const aggregatedWarnings = [];
|
|
4503
|
+
let toolRoundsExecuted = 0;
|
|
4478
4504
|
try {
|
|
4479
|
-
|
|
4505
|
+
roundsLoop: for (let round = 0; ; round += 1) {
|
|
4506
|
+
if (round >= MAX_CLIENT_TOOL_LOOP_DEPTH) {
|
|
4507
|
+
throw new ClientToolExecutionError(
|
|
4508
|
+
new AiConnectError(
|
|
4509
|
+
"validation_error",
|
|
4510
|
+
`clientTools exceeded the maximum tool-call loop depth of ${MAX_CLIENT_TOOL_LOOP_DEPTH}.`
|
|
4511
|
+
)
|
|
4512
|
+
);
|
|
4513
|
+
}
|
|
4480
4514
|
if (abort.signal.aborted) {
|
|
4481
|
-
await returnIterator();
|
|
4482
4515
|
throw mapAbortError(abort.reason);
|
|
4483
4516
|
}
|
|
4484
|
-
|
|
4517
|
+
const roundRequest = round === 0 ? routeRequest : { ...routeRequest, messages: loopMessages, attachments: [] };
|
|
4518
|
+
const iterable = handler.stream({
|
|
4519
|
+
route,
|
|
4520
|
+
request: roundRequest,
|
|
4521
|
+
runtime,
|
|
4522
|
+
attempt: 1,
|
|
4523
|
+
abort
|
|
4524
|
+
});
|
|
4525
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
4526
|
+
let iteratorReturned = false;
|
|
4527
|
+
const returnIterator = async () => {
|
|
4528
|
+
if (iteratorReturned) {
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
iteratorReturned = true;
|
|
4532
|
+
try {
|
|
4533
|
+
await iterator.return?.();
|
|
4534
|
+
} catch {
|
|
4535
|
+
}
|
|
4536
|
+
};
|
|
4537
|
+
let roundResult;
|
|
4538
|
+
try {
|
|
4539
|
+
while (true) {
|
|
4540
|
+
if (abort.signal.aborted) {
|
|
4541
|
+
await returnIterator();
|
|
4542
|
+
throw mapAbortError(abort.reason);
|
|
4543
|
+
}
|
|
4544
|
+
if (pauseSignal?.aborted) {
|
|
4545
|
+
await returnIterator();
|
|
4546
|
+
const pausedResult = lastResult ?? {
|
|
4547
|
+
route,
|
|
4548
|
+
text: accumulatedText,
|
|
4549
|
+
attachments: [],
|
|
4550
|
+
warnings: [],
|
|
4551
|
+
attempts
|
|
4552
|
+
};
|
|
4553
|
+
wideAttempts.push({
|
|
4554
|
+
index: wideAttempts.length + 1,
|
|
4555
|
+
route: routeSummary,
|
|
4556
|
+
durationMs: nowMs(runtime) - attemptStartedAt,
|
|
4557
|
+
outcome: "success",
|
|
4558
|
+
retryCount: 0
|
|
4559
|
+
});
|
|
4560
|
+
await emitWideEvent({
|
|
4561
|
+
timestamp: isoTimestamp(startedAt),
|
|
4562
|
+
event: "ai_connect.operation",
|
|
4563
|
+
requestId,
|
|
4564
|
+
operationName: "stream",
|
|
4565
|
+
operation: normalizedRequest.operation,
|
|
4566
|
+
runtime: runtime.kind,
|
|
4567
|
+
outcome: "success",
|
|
4568
|
+
durationMs: nowMs(runtime) - startedAt,
|
|
4569
|
+
candidates: candidateSummaries,
|
|
4570
|
+
attempts: wideAttempts,
|
|
4571
|
+
selectedRoute: routeSummary,
|
|
4572
|
+
request: requestSummary,
|
|
4573
|
+
result: summarizeResult(pausedResult),
|
|
4574
|
+
...normalizedRequest.logContext ? { context: normalizedRequest.logContext } : {}
|
|
4575
|
+
});
|
|
4576
|
+
yield { type: "paused", result: pausedResult };
|
|
4577
|
+
return;
|
|
4578
|
+
}
|
|
4579
|
+
const next = await iterator.next();
|
|
4580
|
+
if (next.done) {
|
|
4581
|
+
break;
|
|
4582
|
+
}
|
|
4583
|
+
const event = next.value;
|
|
4584
|
+
if (event.type === "delta") {
|
|
4585
|
+
accumulatedText += event.text;
|
|
4586
|
+
hasYieldedOutward = true;
|
|
4587
|
+
yield event;
|
|
4588
|
+
continue;
|
|
4589
|
+
}
|
|
4590
|
+
if (event.type === "paused") {
|
|
4591
|
+
lastResult = event.result;
|
|
4592
|
+
wideAttempts.push({
|
|
4593
|
+
index: wideAttempts.length + 1,
|
|
4594
|
+
route: routeSummary,
|
|
4595
|
+
durationMs: nowMs(runtime) - attemptStartedAt,
|
|
4596
|
+
outcome: "success",
|
|
4597
|
+
retryCount: 0
|
|
4598
|
+
});
|
|
4599
|
+
router.recordSuccess(route);
|
|
4600
|
+
await emitWideEvent({
|
|
4601
|
+
timestamp: isoTimestamp(startedAt),
|
|
4602
|
+
event: "ai_connect.operation",
|
|
4603
|
+
requestId,
|
|
4604
|
+
operationName: "stream",
|
|
4605
|
+
operation: normalizedRequest.operation,
|
|
4606
|
+
runtime: runtime.kind,
|
|
4607
|
+
outcome: "success",
|
|
4608
|
+
durationMs: nowMs(runtime) - startedAt,
|
|
4609
|
+
candidates: candidateSummaries,
|
|
4610
|
+
attempts: wideAttempts,
|
|
4611
|
+
selectedRoute: routeSummary,
|
|
4612
|
+
request: requestSummary,
|
|
4613
|
+
result: summarizeResult(event.result),
|
|
4614
|
+
...normalizedRequest.logContext ? { context: normalizedRequest.logContext } : {}
|
|
4615
|
+
});
|
|
4616
|
+
yield event;
|
|
4617
|
+
return;
|
|
4618
|
+
}
|
|
4619
|
+
if (event.type === "result") {
|
|
4620
|
+
roundResult = event.result;
|
|
4621
|
+
break;
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
} finally {
|
|
4485
4625
|
await returnIterator();
|
|
4486
|
-
const pausedResult = lastResult ?? {
|
|
4487
|
-
route,
|
|
4488
|
-
text: accumulatedText,
|
|
4489
|
-
attachments: [],
|
|
4490
|
-
warnings: [],
|
|
4491
|
-
attempts
|
|
4492
|
-
};
|
|
4493
|
-
wideAttempts.push({
|
|
4494
|
-
index: wideAttempts.length + 1,
|
|
4495
|
-
route: routeSummary,
|
|
4496
|
-
durationMs: nowMs(runtime) - attemptStartedAt,
|
|
4497
|
-
outcome: "success",
|
|
4498
|
-
retryCount: 0
|
|
4499
|
-
});
|
|
4500
|
-
await emitWideEvent({
|
|
4501
|
-
timestamp: isoTimestamp(startedAt),
|
|
4502
|
-
event: "ai_connect.operation",
|
|
4503
|
-
requestId,
|
|
4504
|
-
operationName: "stream",
|
|
4505
|
-
operation: normalizedRequest.operation,
|
|
4506
|
-
runtime: runtime.kind,
|
|
4507
|
-
outcome: "success",
|
|
4508
|
-
durationMs: nowMs(runtime) - startedAt,
|
|
4509
|
-
candidates: candidateSummaries,
|
|
4510
|
-
attempts: wideAttempts,
|
|
4511
|
-
selectedRoute: routeSummary,
|
|
4512
|
-
request: requestSummary,
|
|
4513
|
-
result: summarizeResult(pausedResult),
|
|
4514
|
-
...normalizedRequest.logContext ? { context: normalizedRequest.logContext } : {}
|
|
4515
|
-
});
|
|
4516
|
-
yield { type: "paused", result: pausedResult };
|
|
4517
|
-
return;
|
|
4518
4626
|
}
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4627
|
+
if (!roundResult) {
|
|
4628
|
+
break roundsLoop;
|
|
4629
|
+
}
|
|
4630
|
+
if (registry.size === 0 || !roundResult.toolCalls?.length) {
|
|
4631
|
+
if (toolRoundsExecuted === 0) {
|
|
4632
|
+
lastResult = roundResult;
|
|
4633
|
+
} else {
|
|
4634
|
+
const mergedUsage = mergeUsage(aggregatedUsage, roundResult.usage);
|
|
4635
|
+
lastResult = {
|
|
4636
|
+
...roundResult,
|
|
4637
|
+
warnings: [
|
|
4638
|
+
...aggregatedWarnings,
|
|
4639
|
+
...roundResult.warnings ?? []
|
|
4640
|
+
],
|
|
4641
|
+
...mergedUsage ? { usage: mergedUsage } : {}
|
|
4642
|
+
};
|
|
4643
|
+
}
|
|
4644
|
+
yield { type: "result", result: lastResult };
|
|
4645
|
+
break roundsLoop;
|
|
4522
4646
|
}
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4647
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, roundResult.usage);
|
|
4648
|
+
aggregatedWarnings.push(...roundResult.warnings ?? []);
|
|
4649
|
+
const roundGen = executeToolCallsRound({
|
|
4650
|
+
toolCalls: roundResult.toolCalls,
|
|
4651
|
+
assistantText: roundResult.text ?? "",
|
|
4652
|
+
registry,
|
|
4653
|
+
route,
|
|
4654
|
+
request: routeRequest,
|
|
4655
|
+
runtime,
|
|
4656
|
+
messages: loopMessages,
|
|
4657
|
+
abort,
|
|
4658
|
+
// D3/UR-007: emit a `tool-call` BEFORE execute, `tool-result` AFTER.
|
|
4659
|
+
onToolStart: (toolCall) => ({
|
|
4660
|
+
type: "tool-call",
|
|
4661
|
+
toolCall: {
|
|
4662
|
+
id: toolCall.id,
|
|
4663
|
+
name: toolCall.name,
|
|
4664
|
+
arguments: toolCall.arguments
|
|
4665
|
+
}
|
|
4666
|
+
}),
|
|
4667
|
+
onToolFinish: (toolCall, execution) => ({
|
|
4668
|
+
type: "tool-result",
|
|
4669
|
+
toolCallId: toolCall.id,
|
|
4670
|
+
name: toolCall.name,
|
|
4671
|
+
...execution.isError !== void 0 ? { isError: execution.isError } : {}
|
|
4672
|
+
})
|
|
4673
|
+
});
|
|
4674
|
+
let toolStep = await roundGen.next();
|
|
4675
|
+
while (!toolStep.done) {
|
|
4676
|
+
hasYieldedOutward = true;
|
|
4677
|
+
yield toolStep.value;
|
|
4678
|
+
toolStep = await roundGen.next();
|
|
4528
4679
|
}
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4680
|
+
loopMessages = [
|
|
4681
|
+
...loopMessages,
|
|
4682
|
+
toolStep.value.assistantMessage,
|
|
4683
|
+
...toolStep.value.toolMessages
|
|
4684
|
+
];
|
|
4685
|
+
toolRoundsExecuted += 1;
|
|
4532
4686
|
}
|
|
4533
4687
|
wideAttempts.push({
|
|
4534
4688
|
index: wideAttempts.length + 1,
|
|
@@ -4556,7 +4710,44 @@ function createClient(configOrInput, options = {}) {
|
|
|
4556
4710
|
});
|
|
4557
4711
|
return;
|
|
4558
4712
|
} catch (error) {
|
|
4559
|
-
|
|
4713
|
+
if (error instanceof ClientToolExecutionError && !abort.signal.aborted) {
|
|
4714
|
+
const toolError = error.aiError;
|
|
4715
|
+
attempts.push({
|
|
4716
|
+
routeId: route.id,
|
|
4717
|
+
handlerKey: route.handlerKey,
|
|
4718
|
+
errorCode: toolError.code,
|
|
4719
|
+
message: toolError.message
|
|
4720
|
+
});
|
|
4721
|
+
wideAttempts.push({
|
|
4722
|
+
index: wideAttempts.length + 1,
|
|
4723
|
+
route: routeSummary,
|
|
4724
|
+
durationMs: nowMs(runtime) - attemptStartedAt,
|
|
4725
|
+
outcome: "error",
|
|
4726
|
+
retryCount: 0,
|
|
4727
|
+
errorCode: toolError.code,
|
|
4728
|
+
message: toolError.message
|
|
4729
|
+
});
|
|
4730
|
+
await emitWideEvent({
|
|
4731
|
+
timestamp: isoTimestamp(startedAt),
|
|
4732
|
+
event: "ai_connect.operation",
|
|
4733
|
+
requestId,
|
|
4734
|
+
operationName: "stream",
|
|
4735
|
+
operation: normalizedRequest.operation,
|
|
4736
|
+
runtime: runtime.kind,
|
|
4737
|
+
outcome: "error",
|
|
4738
|
+
durationMs: nowMs(runtime) - startedAt,
|
|
4739
|
+
candidates: candidateSummaries,
|
|
4740
|
+
attempts: wideAttempts,
|
|
4741
|
+
request: requestSummary,
|
|
4742
|
+
error: {
|
|
4743
|
+
code: toolError.code,
|
|
4744
|
+
message: toolError.message,
|
|
4745
|
+
routeId: route.id
|
|
4746
|
+
},
|
|
4747
|
+
...normalizedRequest.logContext ? { context: normalizedRequest.logContext } : {}
|
|
4748
|
+
});
|
|
4749
|
+
throw toolError;
|
|
4750
|
+
}
|
|
4560
4751
|
const normalizedError = abort.signal.aborted ? mapAbortError(abort.reason) : toAiConnectError(error);
|
|
4561
4752
|
attempts.push({
|
|
4562
4753
|
routeId: route.id,
|
|
@@ -4595,6 +4786,28 @@ function createClient(configOrInput, options = {}) {
|
|
|
4595
4786
|
});
|
|
4596
4787
|
throw normalizedError;
|
|
4597
4788
|
}
|
|
4789
|
+
if (hasYieldedOutward) {
|
|
4790
|
+
await emitWideEvent({
|
|
4791
|
+
timestamp: isoTimestamp(startedAt),
|
|
4792
|
+
event: "ai_connect.operation",
|
|
4793
|
+
requestId,
|
|
4794
|
+
operationName: "stream",
|
|
4795
|
+
operation: normalizedRequest.operation,
|
|
4796
|
+
runtime: runtime.kind,
|
|
4797
|
+
outcome: "error",
|
|
4798
|
+
durationMs: nowMs(runtime) - startedAt,
|
|
4799
|
+
candidates: candidateSummaries,
|
|
4800
|
+
attempts: wideAttempts,
|
|
4801
|
+
request: requestSummary,
|
|
4802
|
+
error: {
|
|
4803
|
+
code: normalizedError.code,
|
|
4804
|
+
message: normalizedError.message,
|
|
4805
|
+
routeId: route.id
|
|
4806
|
+
},
|
|
4807
|
+
...normalizedRequest.logContext ? { context: normalizedRequest.logContext } : {}
|
|
4808
|
+
});
|
|
4809
|
+
throw normalizedError;
|
|
4810
|
+
}
|
|
4598
4811
|
router.recordFailure(route, normalizedError.code);
|
|
4599
4812
|
continue;
|
|
4600
4813
|
}
|
|
@@ -4610,6 +4823,12 @@ function createClient(configOrInput, options = {}) {
|
|
|
4610
4823
|
attempts,
|
|
4611
4824
|
wideAttempts
|
|
4612
4825
|
});
|
|
4826
|
+
if (requiresClientTools && !sawStreamHandler) {
|
|
4827
|
+
result.warnings = [
|
|
4828
|
+
...result.warnings,
|
|
4829
|
+
"Streaming tool-loop is not supported by the available route(s); returned a non-streamed result via the generate tool-loop."
|
|
4830
|
+
];
|
|
4831
|
+
}
|
|
4613
4832
|
yield {
|
|
4614
4833
|
type: "result",
|
|
4615
4834
|
result
|