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