@agentskit/adapters 0.12.0 → 0.12.2

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/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { ConfigError, ErrorCodes, AdapterError } from '@agentskit/core';
2
+
1
3
  // src/createAdapter.ts
2
4
  function createAdapter(config) {
3
5
  return {
@@ -1094,8 +1096,6 @@ function vertex(config) {
1094
1096
  };
1095
1097
  }
1096
1098
  var vertexAdapter = vertex;
1097
-
1098
- // src/bedrock.ts
1099
1099
  var cachedSdk = null;
1100
1100
  async function loadSdk() {
1101
1101
  if (!cachedSdk) {
@@ -1107,9 +1107,11 @@ async function loadSdk() {
1107
1107
  moduleId
1108
1108
  );
1109
1109
  } catch {
1110
- throw new Error(
1111
- "Install @aws-sdk/client-bedrock-runtime to use the bedrock adapter: npm install @aws-sdk/client-bedrock-runtime"
1112
- );
1110
+ throw new AdapterError({
1111
+ code: ErrorCodes.AK_ADAPTER_MISSING,
1112
+ message: "Install @aws-sdk/client-bedrock-runtime to use the bedrock adapter: npm install @aws-sdk/client-bedrock-runtime",
1113
+ hint: "bedrock depends on the optional peer @aws-sdk/client-bedrock-runtime."
1114
+ });
1113
1115
  }
1114
1116
  })();
1115
1117
  }
@@ -1210,9 +1212,11 @@ async function* parseAnthropicBedrockEvents(events) {
1210
1212
  function bedrock(config) {
1211
1213
  const { model, region, maxTokens = 4096 } = config;
1212
1214
  if (!isAnthropicModel(model)) {
1213
- throw new Error(
1214
- `bedrock: model "${model}" is not supported in v1. Only anthropic.* models are wired up; Titan + others are tracked as a follow-up.`
1215
- );
1215
+ throw new ConfigError({
1216
+ code: ErrorCodes.AK_CONFIG_INVALID,
1217
+ message: `bedrock: model "${model}" is not supported in v1. Only anthropic.* models are wired up; Titan + others are tracked as a follow-up.`,
1218
+ hint: 'Pass an anthropic.* model id like "anthropic.claude-3-5-sonnet-20241022-v2:0".'
1219
+ });
1216
1220
  }
1217
1221
  let clientPromise = null;
1218
1222
  const getClient = async () => {
@@ -1271,8 +1275,6 @@ function bedrock(config) {
1271
1275
  };
1272
1276
  }
1273
1277
  var bedrockAdapter = bedrock;
1274
-
1275
- // src/webllm.ts
1276
1278
  var cachedSdk2 = null;
1277
1279
  async function loadSdk2() {
1278
1280
  if (!cachedSdk2) {
@@ -1284,7 +1286,11 @@ async function loadSdk2() {
1284
1286
  moduleId
1285
1287
  );
1286
1288
  } catch {
1287
- throw new Error("Install @mlc-ai/web-llm to use the webllm adapter: npm install @mlc-ai/web-llm");
1289
+ throw new AdapterError({
1290
+ code: ErrorCodes.AK_ADAPTER_MISSING,
1291
+ message: "Install @mlc-ai/web-llm to use the webllm adapter: npm install @mlc-ai/web-llm",
1292
+ hint: "webllm is browser-only and depends on the optional peer @mlc-ai/web-llm."
1293
+ });
1288
1294
  }
1289
1295
  })();
1290
1296
  }
@@ -1334,8 +1340,6 @@ function webllm(config) {
1334
1340
  };
1335
1341
  }
1336
1342
  var webllmAdapter = webllm;
1337
-
1338
- // src/router.ts
1339
1343
  function requireCapabilities(request) {
1340
1344
  const needs = {};
1341
1345
  if (request.context?.tools && request.context.tools.length > 0) needs.tools = true;
@@ -1362,7 +1366,13 @@ function pickSyncPolicy(pool, policy) {
1362
1366
  }
1363
1367
  function createRouter(options) {
1364
1368
  const { candidates } = options;
1365
- if (candidates.length === 0) throw new Error("createRouter requires at least one candidate");
1369
+ if (candidates.length === 0) {
1370
+ throw new ConfigError({
1371
+ code: ErrorCodes.AK_CONFIG_INVALID,
1372
+ message: "createRouter requires at least one candidate",
1373
+ hint: "Pass at least one candidate, e.g. createRouter({ candidates: [{ id, adapter, capabilities }] })."
1374
+ });
1375
+ }
1366
1376
  const policy = options.policy ?? "cheapest";
1367
1377
  return {
1368
1378
  createSource: (request) => {
@@ -1385,7 +1395,13 @@ function createRouter(options) {
1385
1395
  pickedBy = "classify:tags";
1386
1396
  }
1387
1397
  }
1388
- if (pool.length === 0) throw new Error("no candidate satisfies the request");
1398
+ if (pool.length === 0) {
1399
+ throw new AdapterError({
1400
+ code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
1401
+ message: "no candidate satisfies the request",
1402
+ hint: "Ensure at least one candidate declares the required capabilities (tools, json, etc.)."
1403
+ });
1404
+ }
1389
1405
  if (typeof policy === "function") {
1390
1406
  const maybe = policy({ request, candidates: pool });
1391
1407
  if (maybe instanceof Promise) {
@@ -1395,14 +1411,26 @@ function createRouter(options) {
1395
1411
  stream: async function* () {
1396
1412
  const id = await maybe;
1397
1413
  const c3 = pool.find((x) => x.id === id);
1398
- if (!c3) throw new Error(`policy returned unknown id: ${id}`);
1414
+ if (!c3) {
1415
+ throw new ConfigError({
1416
+ code: ErrorCodes.AK_CONFIG_INVALID,
1417
+ message: `policy returned unknown id: ${id}`,
1418
+ hint: "Custom policy functions must return one of the candidate ids."
1419
+ });
1420
+ }
1399
1421
  options.onRoute?.({ id: c3.id, reason: pickedBy ?? "custom policy", request });
1400
1422
  for await (const chunk of c3.adapter.createSource(request).stream()) yield chunk;
1401
1423
  }
1402
1424
  };
1403
1425
  }
1404
1426
  const c2 = pool.find((x) => x.id === maybe);
1405
- if (!c2) throw new Error(`policy returned unknown id: ${maybe}`);
1427
+ if (!c2) {
1428
+ throw new ConfigError({
1429
+ code: ErrorCodes.AK_CONFIG_INVALID,
1430
+ message: `policy returned unknown id: ${maybe}`,
1431
+ hint: "Custom policy functions must return one of the candidate ids."
1432
+ });
1433
+ }
1406
1434
  options.onRoute?.({ id: c2.id, reason: pickedBy ?? "custom policy", request });
1407
1435
  return c2.adapter.createSource(request);
1408
1436
  }
@@ -1412,8 +1440,6 @@ function createRouter(options) {
1412
1440
  }
1413
1441
  };
1414
1442
  }
1415
-
1416
- // src/ensemble.ts
1417
1443
  async function drain(source, timeoutMs) {
1418
1444
  const chunks = [];
1419
1445
  let text = "";
@@ -1458,7 +1484,11 @@ function majorityVote(branches, candidates) {
1458
1484
  }
1459
1485
  function createEnsembleAdapter(options) {
1460
1486
  if (options.candidates.length === 0) {
1461
- throw new Error("createEnsembleAdapter requires at least one candidate");
1487
+ throw new ConfigError({
1488
+ code: ErrorCodes.AK_CONFIG_INVALID,
1489
+ message: "createEnsembleAdapter requires at least one candidate",
1490
+ hint: "Pass at least one candidate, e.g. createEnsembleAdapter({ candidates: [{ id, adapter }] })."
1491
+ });
1462
1492
  }
1463
1493
  const aggregate = options.aggregate ?? "majority-vote";
1464
1494
  return {
@@ -1498,7 +1528,14 @@ function createEnsembleAdapter(options) {
1498
1528
  if (aborted) return;
1499
1529
  const text = await finalText(branches);
1500
1530
  if (!text && branches.every((b) => b.error)) {
1501
- throw new Error("all ensemble branches failed");
1531
+ const summary = branches.map((b) => `${b.id}: ${b.error?.message ?? "unknown"}`).join("; ");
1532
+ const err = new AdapterError({
1533
+ code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
1534
+ message: `all ensemble branches failed (${summary})`,
1535
+ hint: "Inspect each branch error via options.onBranches or the err.branchErrors field."
1536
+ });
1537
+ err.branchErrors = branches.map((b) => ({ id: b.id, error: b.error }));
1538
+ throw err;
1502
1539
  }
1503
1540
  yield { type: "text", content: text, metadata: { ensemble: branches.map((b) => ({ id: b.id, bytes: b.text.length, error: b.error?.message })) } };
1504
1541
  yield { type: "done" };
@@ -1507,11 +1544,13 @@ function createEnsembleAdapter(options) {
1507
1544
  }
1508
1545
  };
1509
1546
  }
1510
-
1511
- // src/fallback.ts
1512
1547
  function createFallbackAdapter(candidates, options = {}) {
1513
1548
  if (candidates.length === 0) {
1514
- throw new Error("createFallbackAdapter requires at least one candidate");
1549
+ throw new ConfigError({
1550
+ code: ErrorCodes.AK_CONFIG_INVALID,
1551
+ message: "createFallbackAdapter requires at least one candidate",
1552
+ hint: 'Pass at least one candidate, e.g. createFallbackAdapter([{ id: "primary", adapter }]).'
1553
+ });
1515
1554
  }
1516
1555
  return {
1517
1556
  createSource: (request) => {
@@ -1566,14 +1605,16 @@ function createFallbackAdapter(candidates, options = {}) {
1566
1605
  }
1567
1606
  }
1568
1607
  const summary = errors.map((e) => `${e.id}: ${e.error.message}`).join("; ");
1569
- throw new Error(`all fallback candidates failed (${summary})`);
1608
+ throw new AdapterError({
1609
+ code: ErrorCodes.AK_ADAPTER_STREAM_FAILED,
1610
+ message: `all fallback candidates failed (${summary})`,
1611
+ hint: "Check each candidate adapter's configuration and provider keys."
1612
+ });
1570
1613
  }
1571
1614
  };
1572
1615
  }
1573
1616
  };
1574
1617
  }
1575
-
1576
- // src/mock.ts
1577
1618
  function mockAdapter(options) {
1578
1619
  const { response, delayMs = 0, history } = options;
1579
1620
  let callIndex = 0;
@@ -1658,7 +1699,11 @@ function inMemorySink() {
1658
1699
  }
1659
1700
  function replayAdapter(fixture) {
1660
1701
  if (fixture.length === 0) {
1661
- throw new Error("replayAdapter: fixture is empty");
1702
+ throw new ConfigError({
1703
+ code: ErrorCodes.AK_CONFIG_INVALID,
1704
+ message: "replayAdapter: fixture is empty",
1705
+ hint: "Pass a non-empty fixture; record one with recordingAdapter() first."
1706
+ });
1662
1707
  }
1663
1708
  return mockAdapter({
1664
1709
  response: fixture.map((turn) => turn.chunks)
@@ -1667,11 +1712,13 @@ function replayAdapter(fixture) {
1667
1712
 
1668
1713
  // src/embedders/openai.ts
1669
1714
  async function fetchAvailableModels(baseUrl, apiKey) {
1670
- const response = await fetch(`${baseUrl}/v1/models`, {
1715
+ const url = `${baseUrl}/v1/models`;
1716
+ const response = await fetch(url, {
1671
1717
  headers: { "Authorization": `Bearer ${apiKey}` }
1672
1718
  });
1673
1719
  if (!response.ok) {
1674
- throw new Error(`HTTP ${response.status}`);
1720
+ const body = await response.text().catch(() => "");
1721
+ throw new Error(`HTTP ${response.status} from ${url}: ${body.slice(0, 200)}`);
1675
1722
  }
1676
1723
  const data = await response.json();
1677
1724
  return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
@@ -1712,9 +1759,11 @@ function openaiEmbedder(config) {
1712
1759
 
1713
1760
  // src/embedders/gemini.ts
1714
1761
  async function fetchAvailableModels2(baseUrl, apiKey) {
1715
- const response = await fetch(`${baseUrl}/v1beta/models?key=${apiKey}`);
1762
+ const url = `${baseUrl}/v1beta/models?key=${apiKey}`;
1763
+ const response = await fetch(url);
1716
1764
  if (!response.ok) {
1717
- throw new Error(`HTTP ${response.status}`);
1765
+ const body = await response.text().catch(() => "");
1766
+ throw new Error(`HTTP ${response.status} from gemini ${baseUrl}/v1beta/models: ${body.slice(0, 200)}`);
1718
1767
  }
1719
1768
  const data = await response.json();
1720
1769
  return data.models.filter((m) => m.supportedGenerationMethods.includes("embedContent")).map((m) => m.name.replace("models/", "")).sort();
@@ -1762,9 +1811,11 @@ function geminiEmbedder(config) {
1762
1811
 
1763
1812
  // src/embedders/ollama.ts
1764
1813
  async function fetchAvailableModels3(baseUrl) {
1765
- const response = await fetch(`${baseUrl}/api/tags`);
1814
+ const url = `${baseUrl}/api/tags`;
1815
+ const response = await fetch(url);
1766
1816
  if (!response.ok) {
1767
- throw new Error(`HTTP ${response.status}`);
1817
+ const body = await response.text().catch(() => "");
1818
+ throw new Error(`HTTP ${response.status} from ollama ${url}: ${body.slice(0, 200)}`);
1768
1819
  }
1769
1820
  const data = await response.json();
1770
1821
  return data.models.map((m) => m.name).filter((name) => name.includes("embed")).sort();
@@ -1799,14 +1850,14 @@ function ollamaEmbedder(config) {
1799
1850
  return data.embeddings[0];
1800
1851
  };
1801
1852
  }
1802
-
1803
- // src/embedders/openai-compatible.ts
1804
1853
  async function fetchAvailableModels4(baseUrl, apiKey) {
1805
- const response = await fetch(`${baseUrl}/v1/models`, {
1854
+ const url = `${baseUrl}/v1/models`;
1855
+ const response = await fetch(url, {
1806
1856
  headers: { "Authorization": `Bearer ${apiKey}` }
1807
1857
  });
1808
1858
  if (!response.ok) {
1809
- throw new Error(`HTTP ${response.status}`);
1859
+ const body = await response.text().catch(() => "");
1860
+ throw new Error(`HTTP ${response.status} from ${url}: ${body.slice(0, 200)}`);
1810
1861
  }
1811
1862
  const data = await response.json();
1812
1863
  return data.data.map((m) => m.id).filter((id) => id.includes("embed")).sort();
@@ -1827,7 +1878,11 @@ async function buildModelError4(provider, baseUrl, apiKey, originalError) {
1827
1878
  function createOpenAICompatibleEmbedder(provider, defaultBaseUrl) {
1828
1879
  return function embedder(config) {
1829
1880
  if (!config.model) {
1830
- throw new Error(`Model is required for ${provider}. Pass { model: "<model-name>" }.`);
1881
+ throw new ConfigError({
1882
+ code: ErrorCodes.AK_CONFIG_INVALID,
1883
+ message: `Model is required for ${provider}. Pass { model: "<model-name>" }.`,
1884
+ hint: `${provider} does not infer a default; pick a recent embedding model and pass it explicitly.`
1885
+ });
1831
1886
  }
1832
1887
  const { apiKey, model, baseUrl = defaultBaseUrl } = config;
1833
1888
  return async (text) => {