@rulvar/bridge-ai-sdk 1.83.0 → 1.85.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.
Files changed (2) hide show
  1. package/dist/index.js +51 -28
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -150,27 +150,35 @@ function bridgeAiSdk(model, options = {}) {
150
150
  };
151
151
  return;
152
152
  }
153
- if (!mapper.terminal) yield {
154
- type: "error",
155
- error: {
156
- code: "agent",
157
- message: `bridged model '${model.modelId}' ended its stream without a finish part`,
158
- retryable: true,
159
- data: { kind: "transport" }
160
- }
161
- };
153
+ if (!mapper.terminal) {
154
+ if (signal?.aborted === true) return;
155
+ yield {
156
+ type: "error",
157
+ error: {
158
+ code: "agent",
159
+ message: `bridged model '${model.modelId}' ended its stream without a finish part`,
160
+ retryable: true,
161
+ data: { kind: "transport" }
162
+ }
163
+ };
164
+ }
162
165
  }
163
166
  };
164
167
  }
165
168
  async function* iterateStream(stream) {
166
169
  const reader = stream.getReader();
170
+ let drained = false;
167
171
  try {
168
172
  while (true) {
169
173
  const { done, value } = await reader.read();
170
- if (done) return;
174
+ if (done) {
175
+ drained = true;
176
+ return;
177
+ }
171
178
  yield value;
172
179
  }
173
180
  } finally {
181
+ if (!drained) reader.cancel().catch(() => void 0);
174
182
  reader.releaseLock();
175
183
  }
176
184
  }
@@ -255,7 +263,7 @@ function mapMessages(messages, context) {
255
263
  type: "tool-call",
256
264
  toolCallId: context.ids.wireFor(part.id),
257
265
  toolName: part.name,
258
- input: part.args
266
+ input: unwrapUnparsedArgs(part.args)
259
267
  });
260
268
  else if (part.type === "provider-raw" && part.provider === context.family) {
261
269
  const reinserted = reinsertRetained(part.block);
@@ -333,7 +341,10 @@ function reinsertRetained(block) {
333
341
  type: "tool-result",
334
342
  toolCallId: record.toolCallId,
335
343
  toolName: record.toolName,
336
- output: {
344
+ output: record.isError === true ? {
345
+ type: "error-json",
346
+ value: record.result
347
+ } : {
337
348
  type: "json",
338
349
  value: record.result
339
350
  },
@@ -356,6 +367,22 @@ function stringifyResult(result) {
356
367
  return typeof result === "string" ? result : JSON.stringify(result) ?? "null";
357
368
  }
358
369
  /**
370
+ * Projects tool-call arguments back into the V4 prompt. The adapter
371
+ * parse-failure wrapper ({__unparsed: raw} and nothing else) projects as
372
+ * the ORIGINAL raw text the model wrote, mirroring the openai wire's
373
+ * guard from the v1.74 experiment review: re-encoding the internal
374
+ * wrapper showed the live model {"__unparsed":"..."} as its own past
375
+ * call and taught it to imitate that shape.
376
+ */
377
+ function unwrapUnparsedArgs(args) {
378
+ if (typeof args === "object" && args !== null && !Array.isArray(args)) {
379
+ const keys = Object.keys(args);
380
+ const raw = args.__unparsed;
381
+ if (keys.length === 1 && keys[0] === "__unparsed" && typeof raw === "string") return raw;
382
+ }
383
+ return args;
384
+ }
385
+ /**
359
386
  * Maps the V4 stream-part vocabulary onto ChatEvents, assembling tool
360
387
  * arguments, normalizing usage under the Usage invariant, and collecting
361
388
  * the turn's retention payload (reasoning parts with their signatures,
@@ -367,7 +394,6 @@ var StreamMapper = class {
367
394
  adapterId;
368
395
  ids;
369
396
  effortDownmapped;
370
- toolNames = /* @__PURE__ */ new Map();
371
397
  startedToolWireIds = /* @__PURE__ */ new Set();
372
398
  providerExecutedWireIds = /* @__PURE__ */ new Set();
373
399
  reasoning = /* @__PURE__ */ new Map();
@@ -432,7 +458,6 @@ var StreamMapper = class {
432
458
  this.providerExecutedWireIds.add(part.id);
433
459
  return [];
434
460
  }
435
- this.toolNames.set(part.id, part.toolName);
436
461
  this.startedToolWireIds.add(part.id);
437
462
  return [{
438
463
  type: "tool-call-start",
@@ -469,26 +494,15 @@ var StreamMapper = class {
469
494
  name: part.toolName
470
495
  });
471
496
  const parsed = parseToolArgs(part.input);
472
- if (parsed.ok) events.push({
497
+ events.push({
473
498
  type: "tool-call-end",
474
499
  id: canonical,
475
- args: parsed.value
500
+ args: parsed.ok ? parsed.value : { __unparsed: part.input }
476
501
  });
477
- else {
478
- this.terminal = true;
479
- events.push({
480
- type: "error",
481
- error: {
482
- code: "agent",
483
- message: `bridged tool call '${part.toolName}' carried arguments that are not valid JSON`,
484
- retryable: true,
485
- data: { kind: "transport" }
486
- }
487
- });
488
- }
489
502
  return events;
490
503
  }
491
504
  case "tool-result":
505
+ if (part.preliminary === true) return [];
492
506
  this.retained.push({
493
507
  type: "tool-result",
494
508
  toolCallId: part.toolCallId,
@@ -537,6 +551,9 @@ var StreamMapper = class {
537
551
  }
538
552
  mapFinish(part) {
539
553
  if (part.finishReason.unified === "error") return [{
554
+ type: "usage",
555
+ usage: mapUsage(part.usage)
556
+ }, {
540
557
  type: "error",
541
558
  error: {
542
559
  code: "agent",
@@ -545,6 +562,12 @@ var StreamMapper = class {
545
562
  data: { kind: "transport" }
546
563
  }
547
564
  }];
565
+ for (const acc of this.reasoning.values()) this.retained.push({
566
+ type: "reasoning",
567
+ text: acc.text,
568
+ ...acc.providerMetadata === void 0 ? {} : { providerMetadata: acc.providerMetadata }
569
+ });
570
+ this.reasoning.clear();
548
571
  const finish = mapFinishReason(part.finishReason, this.adapterId);
549
572
  const usage = mapUsage(part.usage);
550
573
  const bag = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/bridge-ai-sdk",
3
- "version": "1.83.0",
3
+ "version": "1.85.0",
4
4
  "description": "Rulvar bridge adapter wrapping any Vercel AI SDK LanguageModelV4 as a ProviderAdapter.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -22,15 +22,15 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@ai-sdk/provider": "^4.0.0",
26
- "@rulvar/core": "1.83.0"
25
+ "@ai-sdk/provider": "^4.0.3",
26
+ "@rulvar/core": "1.85.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@ai-sdk/google": "^4.0.0",
30
- "@types/node": "^22.20.0",
31
- "tsdown": "^0.22.3",
29
+ "@ai-sdk/google": "^4.0.24",
30
+ "@types/node": "^22.20.1",
31
+ "tsdown": "^0.22.14",
32
32
  "typescript": "~6.0.3",
33
- "@rulvar/testing": "1.83.0"
33
+ "@rulvar/testing": "1.85.0"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",