@vedmalex/ai-connect 0.10.0 → 0.11.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/README.md +7 -7
- package/dist/browser/index.js +483 -242
- package/dist/browser/index.js.map +3 -3
- package/dist/bun/index.js +1114 -390
- package/dist/bun/index.js.map +4 -4
- package/dist/bun/local.js +1110 -390
- package/dist/bun/local.js.map +4 -4
- package/dist/node/index.js +1114 -390
- package/dist/node/index.js.map +4 -4
- package/dist/node/local.js +1110 -390
- package/dist/node/local.js.map +4 -4
- package/dist/types/acp.d.ts.map +1 -1
- package/dist/types/cli.d.ts +8 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/client-tools-mcp.d.ts +82 -0
- package/dist/types/client-tools-mcp.d.ts.map +1 -0
- package/dist/types/client-tools-text-protocol.d.ts +70 -0
- package/dist/types/client-tools-text-protocol.d.ts.map +1 -0
- package/dist/types/client.d.ts +20 -0
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/config.d.ts +8 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.browser.d.ts +2 -2
- package/dist/types/index.browser.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/model-reference.d.ts +25 -10
- package/dist/types/model-reference.d.ts.map +1 -1
- package/dist/types/router.d.ts.map +1 -1
- package/dist/types/server.d.ts.map +1 -1
- package/dist/types/types.d.ts +36 -2
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -197,6 +197,373 @@ function toAiConnectError(error, fallbackCode = "temporary_unavailable") {
|
|
|
197
197
|
return new AiConnectError(fallbackCode, "Unknown route failure.");
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
// src/model-reference.ts
|
|
201
|
+
var DEFAULT_CONTEXT_WINDOW = 8192;
|
|
202
|
+
var CHAT_CAPS = { vision: true, tools: true };
|
|
203
|
+
var MODEL_REFERENCE = {
|
|
204
|
+
// --- OpenAI (gpt / codex families; catalog: gpt-4.1, gpt-5.4/xhigh) ---
|
|
205
|
+
"gpt-4.1": { key: "gpt-4.1", contextLength: 1047576, capabilities: CHAT_CAPS },
|
|
206
|
+
"gpt-4.1-mini": {
|
|
207
|
+
key: "gpt-4.1-mini",
|
|
208
|
+
contextLength: 1047576,
|
|
209
|
+
capabilities: CHAT_CAPS
|
|
210
|
+
},
|
|
211
|
+
"gpt-4.1-nano": {
|
|
212
|
+
key: "gpt-4.1-nano",
|
|
213
|
+
contextLength: 1047576,
|
|
214
|
+
capabilities: CHAT_CAPS
|
|
215
|
+
},
|
|
216
|
+
"gpt-4o": { key: "gpt-4o", contextLength: 128e3, capabilities: CHAT_CAPS },
|
|
217
|
+
"gpt-4o-mini": {
|
|
218
|
+
key: "gpt-4o-mini",
|
|
219
|
+
contextLength: 128e3,
|
|
220
|
+
capabilities: CHAT_CAPS
|
|
221
|
+
},
|
|
222
|
+
"gpt-4-turbo": {
|
|
223
|
+
key: "gpt-4-turbo",
|
|
224
|
+
contextLength: 128e3,
|
|
225
|
+
capabilities: CHAT_CAPS
|
|
226
|
+
},
|
|
227
|
+
// Exception (F-D4-2-1): gpt-4 is not vision-capable.
|
|
228
|
+
"gpt-4": {
|
|
229
|
+
key: "gpt-4",
|
|
230
|
+
contextLength: 8192,
|
|
231
|
+
capabilities: { vision: false, tools: true }
|
|
232
|
+
},
|
|
233
|
+
// Exception (F-D4-2-1): gpt-3.5-turbo is not vision-capable.
|
|
234
|
+
"gpt-3.5-turbo": {
|
|
235
|
+
key: "gpt-3.5-turbo",
|
|
236
|
+
contextLength: 16385,
|
|
237
|
+
capabilities: { vision: false, tools: true }
|
|
238
|
+
},
|
|
239
|
+
"gpt-5": { key: "gpt-5", contextLength: 4e5, capabilities: CHAT_CAPS },
|
|
240
|
+
"gpt-5.4": { key: "gpt-5.4", contextLength: 4e5, capabilities: CHAT_CAPS },
|
|
241
|
+
o1: { key: "o1", contextLength: 2e5, capabilities: CHAT_CAPS },
|
|
242
|
+
// F-D4-2-1: o1-mini is neither vision- nor tool-capable.
|
|
243
|
+
"o1-mini": {
|
|
244
|
+
key: "o1-mini",
|
|
245
|
+
contextLength: 128e3,
|
|
246
|
+
capabilities: { vision: false, tools: false }
|
|
247
|
+
},
|
|
248
|
+
// Exception (F-D4-2-1): o3-mini is not vision-capable (tools remain supported).
|
|
249
|
+
"o3-mini": {
|
|
250
|
+
key: "o3-mini",
|
|
251
|
+
contextLength: 2e5,
|
|
252
|
+
capabilities: { vision: false, tools: true }
|
|
253
|
+
},
|
|
254
|
+
// --- Anthropic (claude families; catalog: claude-sonnet-4-6, claude-sonnet-4) ---
|
|
255
|
+
"claude-sonnet-4-6": {
|
|
256
|
+
key: "claude-sonnet-4-6",
|
|
257
|
+
contextLength: 2e5,
|
|
258
|
+
capabilities: CHAT_CAPS
|
|
259
|
+
},
|
|
260
|
+
"claude-sonnet-4": {
|
|
261
|
+
key: "claude-sonnet-4",
|
|
262
|
+
contextLength: 2e5,
|
|
263
|
+
capabilities: CHAT_CAPS
|
|
264
|
+
},
|
|
265
|
+
"claude-opus-4": {
|
|
266
|
+
key: "claude-opus-4",
|
|
267
|
+
contextLength: 2e5,
|
|
268
|
+
capabilities: CHAT_CAPS
|
|
269
|
+
},
|
|
270
|
+
"claude-haiku-4": {
|
|
271
|
+
key: "claude-haiku-4",
|
|
272
|
+
contextLength: 2e5,
|
|
273
|
+
capabilities: CHAT_CAPS
|
|
274
|
+
},
|
|
275
|
+
"claude-3-5-sonnet": {
|
|
276
|
+
key: "claude-3-5-sonnet",
|
|
277
|
+
contextLength: 2e5,
|
|
278
|
+
capabilities: CHAT_CAPS
|
|
279
|
+
},
|
|
280
|
+
"claude-3-5-haiku": {
|
|
281
|
+
key: "claude-3-5-haiku",
|
|
282
|
+
contextLength: 2e5,
|
|
283
|
+
capabilities: CHAT_CAPS
|
|
284
|
+
},
|
|
285
|
+
"claude-3-opus": {
|
|
286
|
+
key: "claude-3-opus",
|
|
287
|
+
contextLength: 2e5,
|
|
288
|
+
capabilities: CHAT_CAPS
|
|
289
|
+
},
|
|
290
|
+
"claude-3-sonnet": {
|
|
291
|
+
key: "claude-3-sonnet",
|
|
292
|
+
contextLength: 2e5,
|
|
293
|
+
capabilities: CHAT_CAPS
|
|
294
|
+
},
|
|
295
|
+
"claude-3-haiku": {
|
|
296
|
+
key: "claude-3-haiku",
|
|
297
|
+
contextLength: 2e5,
|
|
298
|
+
capabilities: CHAT_CAPS
|
|
299
|
+
},
|
|
300
|
+
// --- Gemini (catalog: gemini-3.1-flash-lite, gemini-2.5-flash, auto-gemini-3) ---
|
|
301
|
+
"gemini-3.1-flash-lite": {
|
|
302
|
+
key: "gemini-3.1-flash-lite",
|
|
303
|
+
contextLength: 1048576,
|
|
304
|
+
capabilities: CHAT_CAPS
|
|
305
|
+
},
|
|
306
|
+
"gemini-3.1-pro": {
|
|
307
|
+
key: "gemini-3.1-pro",
|
|
308
|
+
contextLength: 1048576,
|
|
309
|
+
capabilities: CHAT_CAPS
|
|
310
|
+
},
|
|
311
|
+
"gemini-3-pro": {
|
|
312
|
+
key: "gemini-3-pro",
|
|
313
|
+
contextLength: 2097152,
|
|
314
|
+
capabilities: CHAT_CAPS
|
|
315
|
+
},
|
|
316
|
+
"auto-gemini-3": {
|
|
317
|
+
key: "auto-gemini-3",
|
|
318
|
+
contextLength: 1048576,
|
|
319
|
+
capabilities: CHAT_CAPS
|
|
320
|
+
},
|
|
321
|
+
"gemini-2.5-flash": {
|
|
322
|
+
key: "gemini-2.5-flash",
|
|
323
|
+
contextLength: 1048576,
|
|
324
|
+
capabilities: CHAT_CAPS
|
|
325
|
+
},
|
|
326
|
+
"gemini-2.5-pro": {
|
|
327
|
+
key: "gemini-2.5-pro",
|
|
328
|
+
contextLength: 2097152,
|
|
329
|
+
capabilities: CHAT_CAPS
|
|
330
|
+
},
|
|
331
|
+
"gemini-2.0-flash": {
|
|
332
|
+
key: "gemini-2.0-flash",
|
|
333
|
+
contextLength: 1048576,
|
|
334
|
+
capabilities: CHAT_CAPS
|
|
335
|
+
},
|
|
336
|
+
"gemini-1.5-flash": {
|
|
337
|
+
key: "gemini-1.5-flash",
|
|
338
|
+
contextLength: 1048576,
|
|
339
|
+
capabilities: CHAT_CAPS
|
|
340
|
+
},
|
|
341
|
+
"gemini-1.5-pro": {
|
|
342
|
+
key: "gemini-1.5-pro",
|
|
343
|
+
contextLength: 2097152,
|
|
344
|
+
capabilities: CHAT_CAPS
|
|
345
|
+
},
|
|
346
|
+
// --- Capability-only entries (F-D4-2-1, TASK-036 WP2): no documented context window — do NOT
|
|
347
|
+
// invent one. None of these are vision- or tool-capable chat models. ---
|
|
348
|
+
"text-embedding-3-small": {
|
|
349
|
+
key: "text-embedding-3-small",
|
|
350
|
+
capabilities: { vision: false, tools: false }
|
|
351
|
+
},
|
|
352
|
+
"text-embedding-3-large": {
|
|
353
|
+
key: "text-embedding-3-large",
|
|
354
|
+
capabilities: { vision: false, tools: false }
|
|
355
|
+
},
|
|
356
|
+
"text-embedding-ada-002": {
|
|
357
|
+
key: "text-embedding-ada-002",
|
|
358
|
+
capabilities: { vision: false, tools: false }
|
|
359
|
+
},
|
|
360
|
+
"whisper-1": {
|
|
361
|
+
key: "whisper-1",
|
|
362
|
+
capabilities: { vision: false, tools: false }
|
|
363
|
+
},
|
|
364
|
+
"tts-1": {
|
|
365
|
+
key: "tts-1",
|
|
366
|
+
capabilities: { vision: false, tools: false }
|
|
367
|
+
},
|
|
368
|
+
"tts-1-hd": {
|
|
369
|
+
key: "tts-1-hd",
|
|
370
|
+
capabilities: { vision: false, tools: false }
|
|
371
|
+
},
|
|
372
|
+
"dall-e-3": {
|
|
373
|
+
key: "dall-e-3",
|
|
374
|
+
capabilities: { vision: false, tools: false }
|
|
375
|
+
},
|
|
376
|
+
"gpt-image-1": {
|
|
377
|
+
key: "gpt-image-1",
|
|
378
|
+
capabilities: { vision: false, tools: false }
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
var REASONING_EFFORT_SUFFIXES = /* @__PURE__ */ new Set([
|
|
382
|
+
"minimal",
|
|
383
|
+
"low",
|
|
384
|
+
"medium",
|
|
385
|
+
"high",
|
|
386
|
+
"xhigh"
|
|
387
|
+
]);
|
|
388
|
+
function normalizeModelKey(model) {
|
|
389
|
+
let key = model.trim().toLowerCase();
|
|
390
|
+
const lastSlashIndex = key.lastIndexOf("/");
|
|
391
|
+
if (lastSlashIndex > 0 && lastSlashIndex < key.length - 1) {
|
|
392
|
+
const tail = key.slice(lastSlashIndex + 1);
|
|
393
|
+
if (REASONING_EFFORT_SUFFIXES.has(tail)) {
|
|
394
|
+
key = key.slice(0, lastSlashIndex);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const slashIndex = key.indexOf("/");
|
|
398
|
+
if (slashIndex > 0) {
|
|
399
|
+
key = key.slice(slashIndex + 1);
|
|
400
|
+
}
|
|
401
|
+
key = key.replace(/:(?:free|beta)$/i, "");
|
|
402
|
+
return key.trim();
|
|
403
|
+
}
|
|
404
|
+
var MIN_SUBSTRING_MATCH_KEY_LENGTH = 4;
|
|
405
|
+
function lookupModelRef(model) {
|
|
406
|
+
const normalized = normalizeModelKey(model);
|
|
407
|
+
if (!normalized) {
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
const exact = MODEL_REFERENCE[normalized];
|
|
411
|
+
if (exact) {
|
|
412
|
+
return exact;
|
|
413
|
+
}
|
|
414
|
+
let prefixWinner;
|
|
415
|
+
for (const [key, entry] of Object.entries(MODEL_REFERENCE)) {
|
|
416
|
+
if (normalized.startsWith(key)) {
|
|
417
|
+
if (!prefixWinner || key.length > prefixWinner.key.length) {
|
|
418
|
+
prefixWinner = entry;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (prefixWinner) {
|
|
423
|
+
return prefixWinner;
|
|
424
|
+
}
|
|
425
|
+
let substringWinner;
|
|
426
|
+
for (const [key, entry] of Object.entries(MODEL_REFERENCE)) {
|
|
427
|
+
if (key.length >= MIN_SUBSTRING_MATCH_KEY_LENGTH && normalized.includes(key)) {
|
|
428
|
+
if (!substringWinner || key.length > substringWinner.key.length) {
|
|
429
|
+
substringWinner = entry;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return substringWinner;
|
|
434
|
+
}
|
|
435
|
+
function lookupModelCapabilities(model) {
|
|
436
|
+
const normalized = normalizeModelKey(model);
|
|
437
|
+
if (!normalized) {
|
|
438
|
+
return void 0;
|
|
439
|
+
}
|
|
440
|
+
return MODEL_REFERENCE[normalized]?.capabilities;
|
|
441
|
+
}
|
|
442
|
+
function downgradeCapability(routeValue, hint) {
|
|
443
|
+
return hint === false ? false : routeValue;
|
|
444
|
+
}
|
|
445
|
+
function isUsableContextLength(value) {
|
|
446
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
447
|
+
}
|
|
448
|
+
function extractModelContextLength(raw) {
|
|
449
|
+
if (!raw || typeof raw !== "object") {
|
|
450
|
+
return void 0;
|
|
451
|
+
}
|
|
452
|
+
const record = raw;
|
|
453
|
+
if (isUsableContextLength(record.context_length)) {
|
|
454
|
+
return record.context_length;
|
|
455
|
+
}
|
|
456
|
+
if (isUsableContextLength(record.max_model_len)) {
|
|
457
|
+
return record.max_model_len;
|
|
458
|
+
}
|
|
459
|
+
if (isUsableContextLength(record.context_window)) {
|
|
460
|
+
return record.context_window;
|
|
461
|
+
}
|
|
462
|
+
const topProvider = record.top_provider;
|
|
463
|
+
if (topProvider && typeof topProvider === "object") {
|
|
464
|
+
const nested = topProvider.context_length;
|
|
465
|
+
if (isUsableContextLength(nested)) {
|
|
466
|
+
return nested;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return void 0;
|
|
470
|
+
}
|
|
471
|
+
function asPricingNumber(value) {
|
|
472
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
473
|
+
return value;
|
|
474
|
+
}
|
|
475
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
476
|
+
const parsed = Number(value);
|
|
477
|
+
if (Number.isFinite(parsed)) {
|
|
478
|
+
return parsed;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return void 0;
|
|
482
|
+
}
|
|
483
|
+
function parseModelPricing(raw) {
|
|
484
|
+
if (!raw || typeof raw !== "object") {
|
|
485
|
+
return void 0;
|
|
486
|
+
}
|
|
487
|
+
const pricing = raw.pricing;
|
|
488
|
+
if (!pricing || typeof pricing !== "object") {
|
|
489
|
+
return void 0;
|
|
490
|
+
}
|
|
491
|
+
const record = pricing;
|
|
492
|
+
const prompt = asPricingNumber(record.prompt);
|
|
493
|
+
const completion = asPricingNumber(record.completion);
|
|
494
|
+
const request = asPricingNumber(record.request);
|
|
495
|
+
const image = asPricingNumber(record.image);
|
|
496
|
+
const currency = typeof record.currency === "string" ? record.currency : void 0;
|
|
497
|
+
const result = {
|
|
498
|
+
...prompt !== void 0 ? { prompt } : {},
|
|
499
|
+
...completion !== void 0 ? { completion } : {},
|
|
500
|
+
...request !== void 0 ? { request } : {},
|
|
501
|
+
...image !== void 0 ? { image } : {},
|
|
502
|
+
...currency !== void 0 ? { currency } : {}
|
|
503
|
+
};
|
|
504
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
505
|
+
}
|
|
506
|
+
function detectModelFree(modelId, pricing, raw) {
|
|
507
|
+
if (/:free$/i.test(modelId.trim())) {
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
const effectivePricing = pricing ?? parseModelPricing(raw);
|
|
511
|
+
if (!effectivePricing) {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
const components = [
|
|
515
|
+
effectivePricing.prompt,
|
|
516
|
+
effectivePricing.completion,
|
|
517
|
+
effectivePricing.request,
|
|
518
|
+
effectivePricing.image
|
|
519
|
+
].filter((value) => typeof value === "number");
|
|
520
|
+
if (components.length === 0) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
return components.every((value) => value === 0);
|
|
524
|
+
}
|
|
525
|
+
function resolveModelContextWindow(input) {
|
|
526
|
+
if (isUsableContextLength(input.discovered)) {
|
|
527
|
+
return { contextWindow: input.discovered, source: "discovered" };
|
|
528
|
+
}
|
|
529
|
+
if (isUsableContextLength(input.reference)) {
|
|
530
|
+
return { contextWindow: input.reference, source: "reference" };
|
|
531
|
+
}
|
|
532
|
+
if (isUsableContextLength(input.configured)) {
|
|
533
|
+
return { contextWindow: input.configured, source: "configured" };
|
|
534
|
+
}
|
|
535
|
+
const fallback = isUsableContextLength(input.defaultContextWindow) ? input.defaultContextWindow : DEFAULT_CONTEXT_WINDOW;
|
|
536
|
+
return { contextWindow: fallback, source: "default" };
|
|
537
|
+
}
|
|
538
|
+
function fillConfiguredContextLength(route, models) {
|
|
539
|
+
if (!isUsableContextLength(route.contextWindow)) {
|
|
540
|
+
return models;
|
|
541
|
+
}
|
|
542
|
+
const configured = route.contextWindow;
|
|
543
|
+
return models.map((entry) => {
|
|
544
|
+
if (isUsableContextLength(entry.contextLength)) {
|
|
545
|
+
return entry;
|
|
546
|
+
}
|
|
547
|
+
if (route.model !== void 0 && entry.modelId !== route.model) {
|
|
548
|
+
return entry;
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
...entry,
|
|
552
|
+
contextLength: configured,
|
|
553
|
+
// FT-002: typed provenance (canonical) + metadata alias (back-compat).
|
|
554
|
+
contextWindowSource: "configured",
|
|
555
|
+
metadata: { ...entry.metadata ?? {}, contextWindowSource: "configured" }
|
|
556
|
+
};
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
function modelContextCacheKey(input) {
|
|
560
|
+
if (typeof input === "string") {
|
|
561
|
+
return { key: `::${input}`, model: input };
|
|
562
|
+
}
|
|
563
|
+
const scope = input.baseUrl ?? input.transportId ?? "";
|
|
564
|
+
return { key: `${scope}::${input.model}`, model: input.model };
|
|
565
|
+
}
|
|
566
|
+
|
|
200
567
|
// src/config.ts
|
|
201
568
|
var DEFAULT_OPERATIONS = ["text", "image", "file"];
|
|
202
569
|
var DEFAULT_STRATEGY = "priority";
|
|
@@ -303,16 +670,26 @@ var DEFAULT_RETRY_DELAY_MS = {
|
|
|
303
670
|
function slugify(value) {
|
|
304
671
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
305
672
|
}
|
|
306
|
-
function toPublicCapabilities(capabilities) {
|
|
673
|
+
function toPublicCapabilities(capabilities, model) {
|
|
674
|
+
const hints = model ? lookupModelCapabilities(model) : void 0;
|
|
307
675
|
return {
|
|
308
676
|
browserSafe: capabilities.browserSafe,
|
|
309
677
|
supportsStreaming: capabilities.supportsStreaming,
|
|
310
|
-
supportsToolSchema:
|
|
678
|
+
supportsToolSchema: downgradeCapability(
|
|
679
|
+
capabilities.supportsToolSchema,
|
|
680
|
+
hints?.tools
|
|
681
|
+
),
|
|
311
682
|
supportsToolExecution: capabilities.supportsToolExecution,
|
|
312
|
-
supportsClientToolExecution:
|
|
683
|
+
supportsClientToolExecution: downgradeCapability(
|
|
684
|
+
capabilities.supportsClientToolExecution,
|
|
685
|
+
hints?.tools
|
|
686
|
+
),
|
|
313
687
|
supportsFileUpload: capabilities.supportsFileUpload,
|
|
314
688
|
supportsFileOutput: capabilities.supportsFileOutput,
|
|
315
|
-
supportsImageInput:
|
|
689
|
+
supportsImageInput: downgradeCapability(
|
|
690
|
+
capabilities.supportsImageInput,
|
|
691
|
+
hints?.vision
|
|
692
|
+
),
|
|
316
693
|
supportsImageOutput: capabilities.supportsImageOutput
|
|
317
694
|
};
|
|
318
695
|
}
|
|
@@ -713,7 +1090,13 @@ function defaultCapabilities(transport, runtime) {
|
|
|
713
1090
|
supportsStreaming: transport.kind === "api" || transport.kind === "acp",
|
|
714
1091
|
supportsToolSchema: transport.kind === "api",
|
|
715
1092
|
supportsToolExecution: transport.kind === "acp",
|
|
716
|
-
|
|
1093
|
+
// TASK-036 D5/WP3+WP4: acp runs client tools through the in-process MCP
|
|
1094
|
+
// bridge (session/new.mcpServers → ai-connect-client-tools); cli/server run
|
|
1095
|
+
// them through the shared text-protocol round (client-tools-text-protocol.ts:
|
|
1096
|
+
// preamble + non-live echo history + trailing-fence extraction) landed in
|
|
1097
|
+
// the same diff as this flag. The per-account `capabilities` override
|
|
1098
|
+
// remains the escape hatch in both directions.
|
|
1099
|
+
supportsClientToolExecution: transport.kind === "api" || transport.kind === "acp" || transport.kind === "cli" || transport.kind === "server",
|
|
717
1100
|
// C6 / F-P-4: api routes now advertise document + image input parity with acp.
|
|
718
1101
|
// The per-account `capabilities` override remains the escape hatch for
|
|
719
1102
|
// text-only api proxies that must opt out. REQ-024: a cli route advertises
|
|
@@ -1836,7 +2219,7 @@ function runtimeEligible(route, runtime) {
|
|
|
1836
2219
|
}
|
|
1837
2220
|
return route.runtime === runtime.kind;
|
|
1838
2221
|
}
|
|
1839
|
-
function capabilityMatch(
|
|
2222
|
+
function capabilityMatch(capabilities, hints) {
|
|
1840
2223
|
if (!hints?.requiredCapabilities) {
|
|
1841
2224
|
return true;
|
|
1842
2225
|
}
|
|
@@ -1844,9 +2227,33 @@ function capabilityMatch(route, hints) {
|
|
|
1844
2227
|
if (value === void 0) {
|
|
1845
2228
|
return true;
|
|
1846
2229
|
}
|
|
1847
|
-
return
|
|
2230
|
+
return capabilities[key] === value;
|
|
1848
2231
|
});
|
|
1849
2232
|
}
|
|
2233
|
+
function effectiveCapabilitiesForModel(route, requestedModel) {
|
|
2234
|
+
if (!requestedModel) {
|
|
2235
|
+
return route.capabilities;
|
|
2236
|
+
}
|
|
2237
|
+
const hints = lookupModelCapabilities(requestedModel);
|
|
2238
|
+
if (!hints) {
|
|
2239
|
+
return route.capabilities;
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
...route.capabilities,
|
|
2243
|
+
supportsToolSchema: downgradeCapability(
|
|
2244
|
+
route.capabilities.supportsToolSchema,
|
|
2245
|
+
hints.tools
|
|
2246
|
+
),
|
|
2247
|
+
supportsClientToolExecution: downgradeCapability(
|
|
2248
|
+
route.capabilities.supportsClientToolExecution,
|
|
2249
|
+
hints.tools
|
|
2250
|
+
),
|
|
2251
|
+
supportsImageInput: downgradeCapability(
|
|
2252
|
+
route.capabilities.supportsImageInput,
|
|
2253
|
+
hints.vision
|
|
2254
|
+
)
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
1850
2257
|
function preferredProviderScore(route, preferredProviders) {
|
|
1851
2258
|
if (!preferredProviders || preferredProviders.length === 0) {
|
|
1852
2259
|
return Number.MAX_SAFE_INTEGER;
|
|
@@ -1954,16 +2361,29 @@ var RouteRegistry = class {
|
|
|
1954
2361
|
).filter((route) => runtimeEligible(route, this.runtime)).filter((route) => {
|
|
1955
2362
|
const state = this.getHealth(route).state;
|
|
1956
2363
|
return state !== "cooling_down" && state !== "unavailable";
|
|
1957
|
-
}).
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2364
|
+
}).flatMap((route) => {
|
|
2365
|
+
if (route.advertisedModels.length === 0) {
|
|
2366
|
+
return [
|
|
2367
|
+
{
|
|
2368
|
+
model: route.model,
|
|
2369
|
+
provider: route.provider,
|
|
2370
|
+
routeId: route.id,
|
|
2371
|
+
transportKind: route.transport.kind,
|
|
2372
|
+
// INV-PROJ-1: project the secret-free subset (never the full
|
|
2373
|
+
// RouteCapabilities) so internal routing flags do not leak to the
|
|
2374
|
+
// ModelSelector / listCandidateModels surface (#12).
|
|
2375
|
+
capabilities: toPublicCapabilities(route.capabilities)
|
|
2376
|
+
}
|
|
2377
|
+
];
|
|
2378
|
+
}
|
|
2379
|
+
return route.advertisedModels.map((model) => ({
|
|
2380
|
+
model,
|
|
2381
|
+
provider: route.provider,
|
|
2382
|
+
routeId: route.id,
|
|
2383
|
+
transportKind: route.transport.kind,
|
|
2384
|
+
capabilities: toPublicCapabilities(route.capabilities, model)
|
|
2385
|
+
}));
|
|
2386
|
+
});
|
|
1967
2387
|
}
|
|
1968
2388
|
resolveCandidates(request) {
|
|
1969
2389
|
const operation = request.operation;
|
|
@@ -1971,7 +2391,12 @@ var RouteRegistry = class {
|
|
|
1971
2391
|
const routeIds = request.routeHints?.pool?.length ? resolveRouteSelectors(request.routeHints.pool, this.config.routes) : operationRouting.pool;
|
|
1972
2392
|
const excludedIds = new Set(request.routeHints?.excludeRouteIds ?? []);
|
|
1973
2393
|
const requestedModel = request.routeHints?.model;
|
|
1974
|
-
const eligible = routeIds.map((routeId) => this.#routeMap.get(routeId)).filter((route) => Boolean(route)).filter((route) => !excludedIds.has(route.id)).filter((route) => runtimeEligible(route, this.runtime)).filter(
|
|
2394
|
+
const eligible = routeIds.map((routeId) => this.#routeMap.get(routeId)).filter((route) => Boolean(route)).filter((route) => !excludedIds.has(route.id)).filter((route) => runtimeEligible(route, this.runtime)).filter(
|
|
2395
|
+
(route) => capabilityMatch(
|
|
2396
|
+
effectiveCapabilitiesForModel(route, requestedModel),
|
|
2397
|
+
request.routeHints
|
|
2398
|
+
)
|
|
2399
|
+
).map((route) => applyRequestedModel(route, requestedModel)).filter((route) => Boolean(route));
|
|
1975
2400
|
const strategy = request.routeHints?.strategy ?? operationRouting.strategy;
|
|
1976
2401
|
const cursorKey = `${operation}:${strategy}:${eligible.map((route) => route.id).join("|")}`;
|
|
1977
2402
|
const healthyIds = new Set(
|
|
@@ -2086,223 +2511,6 @@ function applyRequestedModel(route, requestedModel) {
|
|
|
2086
2511
|
return { ...route, model: requestedModel };
|
|
2087
2512
|
}
|
|
2088
2513
|
|
|
2089
|
-
// src/model-reference.ts
|
|
2090
|
-
var DEFAULT_CONTEXT_WINDOW = 8192;
|
|
2091
|
-
var MODEL_REFERENCE = {
|
|
2092
|
-
// --- OpenAI (gpt / codex families; catalog: gpt-4.1, gpt-5.4/xhigh) ---
|
|
2093
|
-
"gpt-4.1": { key: "gpt-4.1", contextLength: 1047576 },
|
|
2094
|
-
"gpt-4.1-mini": { key: "gpt-4.1-mini", contextLength: 1047576 },
|
|
2095
|
-
"gpt-4.1-nano": { key: "gpt-4.1-nano", contextLength: 1047576 },
|
|
2096
|
-
"gpt-4o": { key: "gpt-4o", contextLength: 128e3 },
|
|
2097
|
-
"gpt-4o-mini": { key: "gpt-4o-mini", contextLength: 128e3 },
|
|
2098
|
-
"gpt-4-turbo": { key: "gpt-4-turbo", contextLength: 128e3 },
|
|
2099
|
-
"gpt-4": { key: "gpt-4", contextLength: 8192 },
|
|
2100
|
-
"gpt-3.5-turbo": { key: "gpt-3.5-turbo", contextLength: 16385 },
|
|
2101
|
-
"gpt-5": { key: "gpt-5", contextLength: 4e5 },
|
|
2102
|
-
"gpt-5.4": { key: "gpt-5.4", contextLength: 4e5 },
|
|
2103
|
-
o1: { key: "o1", contextLength: 2e5 },
|
|
2104
|
-
"o1-mini": { key: "o1-mini", contextLength: 128e3 },
|
|
2105
|
-
"o3-mini": { key: "o3-mini", contextLength: 2e5 },
|
|
2106
|
-
// --- Anthropic (claude families; catalog: claude-sonnet-4-6, claude-sonnet-4) ---
|
|
2107
|
-
"claude-sonnet-4-6": { key: "claude-sonnet-4-6", contextLength: 2e5 },
|
|
2108
|
-
"claude-sonnet-4": { key: "claude-sonnet-4", contextLength: 2e5 },
|
|
2109
|
-
"claude-opus-4": { key: "claude-opus-4", contextLength: 2e5 },
|
|
2110
|
-
"claude-haiku-4": { key: "claude-haiku-4", contextLength: 2e5 },
|
|
2111
|
-
"claude-3-5-sonnet": { key: "claude-3-5-sonnet", contextLength: 2e5 },
|
|
2112
|
-
"claude-3-5-haiku": { key: "claude-3-5-haiku", contextLength: 2e5 },
|
|
2113
|
-
"claude-3-opus": { key: "claude-3-opus", contextLength: 2e5 },
|
|
2114
|
-
"claude-3-sonnet": { key: "claude-3-sonnet", contextLength: 2e5 },
|
|
2115
|
-
"claude-3-haiku": { key: "claude-3-haiku", contextLength: 2e5 },
|
|
2116
|
-
// --- Gemini (catalog: gemini-3.1-flash-lite, gemini-2.5-flash, auto-gemini-3) ---
|
|
2117
|
-
"gemini-3.1-flash-lite": {
|
|
2118
|
-
key: "gemini-3.1-flash-lite",
|
|
2119
|
-
contextLength: 1048576
|
|
2120
|
-
},
|
|
2121
|
-
"gemini-3.1-pro": { key: "gemini-3.1-pro", contextLength: 1048576 },
|
|
2122
|
-
"gemini-3-pro": { key: "gemini-3-pro", contextLength: 2097152 },
|
|
2123
|
-
"auto-gemini-3": { key: "auto-gemini-3", contextLength: 1048576 },
|
|
2124
|
-
"gemini-2.5-flash": { key: "gemini-2.5-flash", contextLength: 1048576 },
|
|
2125
|
-
"gemini-2.5-pro": { key: "gemini-2.5-pro", contextLength: 2097152 },
|
|
2126
|
-
"gemini-2.0-flash": { key: "gemini-2.0-flash", contextLength: 1048576 },
|
|
2127
|
-
"gemini-1.5-flash": { key: "gemini-1.5-flash", contextLength: 1048576 },
|
|
2128
|
-
"gemini-1.5-pro": { key: "gemini-1.5-pro", contextLength: 2097152 }
|
|
2129
|
-
};
|
|
2130
|
-
var REASONING_EFFORT_SUFFIXES = /* @__PURE__ */ new Set([
|
|
2131
|
-
"minimal",
|
|
2132
|
-
"low",
|
|
2133
|
-
"medium",
|
|
2134
|
-
"high",
|
|
2135
|
-
"xhigh"
|
|
2136
|
-
]);
|
|
2137
|
-
function normalizeModelKey(model) {
|
|
2138
|
-
let key = model.trim().toLowerCase();
|
|
2139
|
-
const lastSlashIndex = key.lastIndexOf("/");
|
|
2140
|
-
if (lastSlashIndex > 0 && lastSlashIndex < key.length - 1) {
|
|
2141
|
-
const tail = key.slice(lastSlashIndex + 1);
|
|
2142
|
-
if (REASONING_EFFORT_SUFFIXES.has(tail)) {
|
|
2143
|
-
key = key.slice(0, lastSlashIndex);
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
const slashIndex = key.indexOf("/");
|
|
2147
|
-
if (slashIndex > 0) {
|
|
2148
|
-
key = key.slice(slashIndex + 1);
|
|
2149
|
-
}
|
|
2150
|
-
key = key.replace(/:(?:free|beta)$/i, "");
|
|
2151
|
-
return key.trim();
|
|
2152
|
-
}
|
|
2153
|
-
var MIN_SUBSTRING_MATCH_KEY_LENGTH = 4;
|
|
2154
|
-
function lookupModelRef(model) {
|
|
2155
|
-
const normalized = normalizeModelKey(model);
|
|
2156
|
-
if (!normalized) {
|
|
2157
|
-
return void 0;
|
|
2158
|
-
}
|
|
2159
|
-
const exact = MODEL_REFERENCE[normalized];
|
|
2160
|
-
if (exact) {
|
|
2161
|
-
return exact;
|
|
2162
|
-
}
|
|
2163
|
-
let prefixWinner;
|
|
2164
|
-
for (const [key, entry] of Object.entries(MODEL_REFERENCE)) {
|
|
2165
|
-
if (normalized.startsWith(key)) {
|
|
2166
|
-
if (!prefixWinner || key.length > prefixWinner.key.length) {
|
|
2167
|
-
prefixWinner = entry;
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
if (prefixWinner) {
|
|
2172
|
-
return prefixWinner;
|
|
2173
|
-
}
|
|
2174
|
-
let substringWinner;
|
|
2175
|
-
for (const [key, entry] of Object.entries(MODEL_REFERENCE)) {
|
|
2176
|
-
if (key.length >= MIN_SUBSTRING_MATCH_KEY_LENGTH && normalized.includes(key)) {
|
|
2177
|
-
if (!substringWinner || key.length > substringWinner.key.length) {
|
|
2178
|
-
substringWinner = entry;
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
return substringWinner;
|
|
2183
|
-
}
|
|
2184
|
-
function isUsableContextLength(value) {
|
|
2185
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
2186
|
-
}
|
|
2187
|
-
function extractModelContextLength(raw) {
|
|
2188
|
-
if (!raw || typeof raw !== "object") {
|
|
2189
|
-
return void 0;
|
|
2190
|
-
}
|
|
2191
|
-
const record = raw;
|
|
2192
|
-
if (isUsableContextLength(record.context_length)) {
|
|
2193
|
-
return record.context_length;
|
|
2194
|
-
}
|
|
2195
|
-
if (isUsableContextLength(record.max_model_len)) {
|
|
2196
|
-
return record.max_model_len;
|
|
2197
|
-
}
|
|
2198
|
-
if (isUsableContextLength(record.context_window)) {
|
|
2199
|
-
return record.context_window;
|
|
2200
|
-
}
|
|
2201
|
-
const topProvider = record.top_provider;
|
|
2202
|
-
if (topProvider && typeof topProvider === "object") {
|
|
2203
|
-
const nested = topProvider.context_length;
|
|
2204
|
-
if (isUsableContextLength(nested)) {
|
|
2205
|
-
return nested;
|
|
2206
|
-
}
|
|
2207
|
-
}
|
|
2208
|
-
return void 0;
|
|
2209
|
-
}
|
|
2210
|
-
function asPricingNumber(value) {
|
|
2211
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2212
|
-
return value;
|
|
2213
|
-
}
|
|
2214
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
2215
|
-
const parsed = Number(value);
|
|
2216
|
-
if (Number.isFinite(parsed)) {
|
|
2217
|
-
return parsed;
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
return void 0;
|
|
2221
|
-
}
|
|
2222
|
-
function parseModelPricing(raw) {
|
|
2223
|
-
if (!raw || typeof raw !== "object") {
|
|
2224
|
-
return void 0;
|
|
2225
|
-
}
|
|
2226
|
-
const pricing = raw.pricing;
|
|
2227
|
-
if (!pricing || typeof pricing !== "object") {
|
|
2228
|
-
return void 0;
|
|
2229
|
-
}
|
|
2230
|
-
const record = pricing;
|
|
2231
|
-
const prompt = asPricingNumber(record.prompt);
|
|
2232
|
-
const completion = asPricingNumber(record.completion);
|
|
2233
|
-
const request = asPricingNumber(record.request);
|
|
2234
|
-
const image = asPricingNumber(record.image);
|
|
2235
|
-
const currency = typeof record.currency === "string" ? record.currency : void 0;
|
|
2236
|
-
const result = {
|
|
2237
|
-
...prompt !== void 0 ? { prompt } : {},
|
|
2238
|
-
...completion !== void 0 ? { completion } : {},
|
|
2239
|
-
...request !== void 0 ? { request } : {},
|
|
2240
|
-
...image !== void 0 ? { image } : {},
|
|
2241
|
-
...currency !== void 0 ? { currency } : {}
|
|
2242
|
-
};
|
|
2243
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
2244
|
-
}
|
|
2245
|
-
function detectModelFree(modelId, pricing, raw) {
|
|
2246
|
-
if (/:free$/i.test(modelId.trim())) {
|
|
2247
|
-
return true;
|
|
2248
|
-
}
|
|
2249
|
-
const effectivePricing = pricing ?? parseModelPricing(raw);
|
|
2250
|
-
if (!effectivePricing) {
|
|
2251
|
-
return false;
|
|
2252
|
-
}
|
|
2253
|
-
const components = [
|
|
2254
|
-
effectivePricing.prompt,
|
|
2255
|
-
effectivePricing.completion,
|
|
2256
|
-
effectivePricing.request,
|
|
2257
|
-
effectivePricing.image
|
|
2258
|
-
].filter((value) => typeof value === "number");
|
|
2259
|
-
if (components.length === 0) {
|
|
2260
|
-
return false;
|
|
2261
|
-
}
|
|
2262
|
-
return components.every((value) => value === 0);
|
|
2263
|
-
}
|
|
2264
|
-
function resolveModelContextWindow(input) {
|
|
2265
|
-
if (isUsableContextLength(input.discovered)) {
|
|
2266
|
-
return { contextWindow: input.discovered, source: "discovered" };
|
|
2267
|
-
}
|
|
2268
|
-
if (isUsableContextLength(input.reference)) {
|
|
2269
|
-
return { contextWindow: input.reference, source: "reference" };
|
|
2270
|
-
}
|
|
2271
|
-
if (isUsableContextLength(input.configured)) {
|
|
2272
|
-
return { contextWindow: input.configured, source: "configured" };
|
|
2273
|
-
}
|
|
2274
|
-
const fallback = isUsableContextLength(input.defaultContextWindow) ? input.defaultContextWindow : DEFAULT_CONTEXT_WINDOW;
|
|
2275
|
-
return { contextWindow: fallback, source: "default" };
|
|
2276
|
-
}
|
|
2277
|
-
function fillConfiguredContextLength(route, models) {
|
|
2278
|
-
if (!isUsableContextLength(route.contextWindow)) {
|
|
2279
|
-
return models;
|
|
2280
|
-
}
|
|
2281
|
-
const configured = route.contextWindow;
|
|
2282
|
-
return models.map((entry) => {
|
|
2283
|
-
if (isUsableContextLength(entry.contextLength)) {
|
|
2284
|
-
return entry;
|
|
2285
|
-
}
|
|
2286
|
-
if (route.model !== void 0 && entry.modelId !== route.model) {
|
|
2287
|
-
return entry;
|
|
2288
|
-
}
|
|
2289
|
-
return {
|
|
2290
|
-
...entry,
|
|
2291
|
-
contextLength: configured,
|
|
2292
|
-
// FT-002: typed provenance (canonical) + metadata alias (back-compat).
|
|
2293
|
-
contextWindowSource: "configured",
|
|
2294
|
-
metadata: { ...entry.metadata ?? {}, contextWindowSource: "configured" }
|
|
2295
|
-
};
|
|
2296
|
-
});
|
|
2297
|
-
}
|
|
2298
|
-
function modelContextCacheKey(input) {
|
|
2299
|
-
if (typeof input === "string") {
|
|
2300
|
-
return { key: `::${input}`, model: input };
|
|
2301
|
-
}
|
|
2302
|
-
const scope = input.baseUrl ?? input.transportId ?? "";
|
|
2303
|
-
return { key: `${scope}::${input.model}`, model: input.model };
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
2514
|
// src/probe.ts
|
|
2307
2515
|
var PROBE_DEFAULT_TIMEOUT_MS = 8e3;
|
|
2308
2516
|
var PROBE_DEFAULT_TTL_MS = 3e5;
|
|
@@ -2744,8 +2952,14 @@ function mergeUsage(current, next) {
|
|
|
2744
2952
|
return merged;
|
|
2745
2953
|
}
|
|
2746
2954
|
function requiresToolSchema(request) {
|
|
2955
|
+
const clientToolNames = new Set(
|
|
2956
|
+
(request.clientTools ?? []).map((tool) => tool.function.name)
|
|
2957
|
+
);
|
|
2958
|
+
const hasNativeTool = (request.parameters?.tools ?? []).some(
|
|
2959
|
+
(tool) => !clientToolNames.has(tool.function.name)
|
|
2960
|
+
);
|
|
2747
2961
|
return Boolean(
|
|
2748
|
-
|
|
2962
|
+
hasNativeTool || request.parameters?.toolChoice !== void 0 || request.parameters?.parallelToolCalls !== void 0
|
|
2749
2963
|
);
|
|
2750
2964
|
}
|
|
2751
2965
|
function requiresImageInput(request) {
|
|
@@ -2849,6 +3063,14 @@ function applyRouteDefaults(route, request) {
|
|
|
2849
3063
|
}
|
|
2850
3064
|
function toPublicRoute(route, health) {
|
|
2851
3065
|
const capabilities = toPublicCapabilities(route.capabilities);
|
|
3066
|
+
const perModelCapabilities = {};
|
|
3067
|
+
let hasModelCapabilityHints = false;
|
|
3068
|
+
for (const model of route.advertisedModels) {
|
|
3069
|
+
if (lookupModelCapabilities(model)) {
|
|
3070
|
+
hasModelCapabilityHints = true;
|
|
3071
|
+
}
|
|
3072
|
+
perModelCapabilities[model] = toPublicCapabilities(route.capabilities, model);
|
|
3073
|
+
}
|
|
2852
3074
|
return {
|
|
2853
3075
|
id: route.id,
|
|
2854
3076
|
label: route.label ?? route.id,
|
|
@@ -2861,6 +3083,7 @@ function toPublicRoute(route, health) {
|
|
|
2861
3083
|
...route.defaultResponseFormat !== void 0 ? { defaultResponseFormat: route.defaultResponseFormat } : {},
|
|
2862
3084
|
...route.systemPrompt !== void 0 ? { systemPrompt: route.systemPrompt } : {},
|
|
2863
3085
|
capabilities,
|
|
3086
|
+
...hasModelCapabilityHints ? { perModelCapabilities } : {},
|
|
2864
3087
|
health
|
|
2865
3088
|
};
|
|
2866
3089
|
}
|
|
@@ -3262,10 +3485,10 @@ function createClient(configOrInput, options = {}) {
|
|
|
3262
3485
|
request.clientTools,
|
|
3263
3486
|
clientToolRegistry
|
|
3264
3487
|
);
|
|
3265
|
-
if (clientTools.length > 0 &&
|
|
3488
|
+
if (clientTools.length > 0 && normalized.operation !== "text") {
|
|
3266
3489
|
throw new AiConnectError(
|
|
3267
|
-
"
|
|
3268
|
-
|
|
3490
|
+
"unsupported_capability",
|
|
3491
|
+
`clientTools are not supported for operation "${normalized.operation}" \u2014 tool-loop semantics exist only for text requests.`
|
|
3269
3492
|
);
|
|
3270
3493
|
}
|
|
3271
3494
|
const mergedTools = clientTools.length ? mergeToolDefinitions(normalized.parameters?.tools, clientTools) : void 0;
|
|
@@ -3385,12 +3608,15 @@ function createClient(configOrInput, options = {}) {
|
|
|
3385
3608
|
async function executeGenerateOperation(normalizedRequest, operationName, abort, seed = {}) {
|
|
3386
3609
|
const startedAt = seed.startMs ?? nowMs(runtime);
|
|
3387
3610
|
const requestId = seed.requestId ?? resolveEventRequestId(normalizedRequest.logContext);
|
|
3388
|
-
const { requiresImage, requiresFile, request: capabilityScopedRequest } = resolveRequiredCapabilities(normalizedRequest);
|
|
3611
|
+
const { requiresImage, requiresFile, requiresClientTools, request: capabilityScopedRequest } = resolveRequiredCapabilities(normalizedRequest);
|
|
3389
3612
|
const resolvedCandidates = router.resolveCandidates(capabilityScopedRequest);
|
|
3390
3613
|
const candidates = seed.candidates ?? resolvedCandidates.map(summarizeRoute);
|
|
3391
3614
|
const requestSummary = summarizeRequest(normalizedRequest, candidates);
|
|
3392
3615
|
if (resolvedCandidates.length === 0) {
|
|
3393
|
-
const error =
|
|
3616
|
+
const error = requiresClientTools ? new AiConnectError(
|
|
3617
|
+
"unsupported_capability",
|
|
3618
|
+
`No route supports the requested capabilities: client tool execution${requiresImage ? ", image input" : ""}${requiresFile ? ", file upload" : ""}.`
|
|
3619
|
+
) : requiresImage || requiresFile ? new AiConnectError(
|
|
3394
3620
|
"unsupported_capability",
|
|
3395
3621
|
`No eligible route supports the required ${requiresFile ? "document/file" : "image"} input for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`
|
|
3396
3622
|
) : new AiConnectError("not_supported", `No eligible routes are available for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`);
|
|
@@ -4452,7 +4678,7 @@ function createClient(configOrInput, options = {}) {
|
|
|
4452
4678
|
if (candidates.length === 0) {
|
|
4453
4679
|
const error = requiresImage || requiresFile || requiresClientTools ? new AiConnectError(
|
|
4454
4680
|
"unsupported_capability",
|
|
4455
|
-
|
|
4681
|
+
requiresClientTools ? `No route supports the requested capabilities: client tool execution${requiresImage ? ", image input" : ""}${requiresFile ? ", file upload" : ""}.` : `No eligible route supports the required ${requiresFile ? "document/file" : "image"} input for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`
|
|
4456
4682
|
) : new AiConnectError("not_supported", `No eligible routes are available for operation "${normalizedRequest.operation}" in runtime "${runtime.kind}".`);
|
|
4457
4683
|
await emitWideEvent({
|
|
4458
4684
|
timestamp: isoTimestamp(startedAt),
|
|
@@ -4618,6 +4844,17 @@ function createClient(configOrInput, options = {}) {
|
|
|
4618
4844
|
await returnIterator();
|
|
4619
4845
|
}
|
|
4620
4846
|
if (!roundResult) {
|
|
4847
|
+
if (toolRoundsExecuted > 0) {
|
|
4848
|
+
lastResult = {
|
|
4849
|
+
route,
|
|
4850
|
+
text: accumulatedText,
|
|
4851
|
+
attachments: [],
|
|
4852
|
+
warnings: [...aggregatedWarnings],
|
|
4853
|
+
attempts,
|
|
4854
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {}
|
|
4855
|
+
};
|
|
4856
|
+
yield { type: "result", result: lastResult };
|
|
4857
|
+
}
|
|
4621
4858
|
break roundsLoop;
|
|
4622
4859
|
}
|
|
4623
4860
|
if (registry.size === 0 || !roundResult.toolCalls?.length) {
|
|
@@ -7404,10 +7641,12 @@ export {
|
|
|
7404
7641
|
AI_CONNECT_DEFAULT_SERVER_COMMANDS,
|
|
7405
7642
|
AI_CONNECT_DEFAULT_SERVER_PRESETS,
|
|
7406
7643
|
AiConnectError,
|
|
7644
|
+
ClientToolExecutionError,
|
|
7407
7645
|
DEFAULT_CONTEXT_WINDOW,
|
|
7408
7646
|
DEFAULT_IMAGE_SIZE,
|
|
7409
7647
|
IMAGE_PROMPT_PROTOCOL,
|
|
7410
7648
|
IMAGE_SIZE_PRESETS,
|
|
7649
|
+
MAX_CLIENT_TOOL_LOOP_DEPTH,
|
|
7411
7650
|
MODEL_REFERENCE,
|
|
7412
7651
|
PROBE_DEFAULT_CONCURRENCY,
|
|
7413
7652
|
PROBE_DEFAULT_TIMEOUT_MS,
|
|
@@ -7426,6 +7665,7 @@ export {
|
|
|
7426
7665
|
createRuntimeEnvironment,
|
|
7427
7666
|
defineConfig,
|
|
7428
7667
|
detectModelFree,
|
|
7668
|
+
downgradeCapability,
|
|
7429
7669
|
extractImagePayloads,
|
|
7430
7670
|
extractModelContextLength,
|
|
7431
7671
|
filterStructuralMarkers,
|
|
@@ -7439,6 +7679,7 @@ export {
|
|
|
7439
7679
|
isNormalizedConfig,
|
|
7440
7680
|
isPortableDocumentFile,
|
|
7441
7681
|
listTextProviderCatalog,
|
|
7682
|
+
lookupModelCapabilities,
|
|
7442
7683
|
lookupModelRef,
|
|
7443
7684
|
mapAbortError,
|
|
7444
7685
|
materializePortableFile,
|