ai 6.0.207 → 6.0.209

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # ai
2
2
 
3
+ ## 6.0.209
4
+
5
+ ### Patch Changes
6
+
7
+ - d102dff: Sanitize OpenTelemetry array attributes so spans no longer emit invalid OTLP values (arrays containing `undefined`/`null`/objects, or arrays mixing primitive types). For example, `gen_ai.response.finish_reasons` could be emitted as `[undefined]` when a finish reason was missing. Such values previously failed telemetry ingestion with `deserializing message invalid value: map, expected map with a single key` and flooded function logs with errors.
8
+ - Updated dependencies [709c0fa]
9
+ - @ai-sdk/gateway@3.0.134
10
+
11
+ ## 6.0.208
12
+
13
+ ### Patch Changes
14
+
15
+ - 8261640: fix(ai): handle partial unicode escapes in fixJson
16
+ - f994df3: Serialize `undefined` tool output to `null` in UI message chunks
17
+
3
18
  ## 6.0.207
4
19
 
5
20
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1281,7 +1281,7 @@ function detectMediaType({
1281
1281
  var import_provider_utils3 = require("@ai-sdk/provider-utils");
1282
1282
 
1283
1283
  // src/version.ts
1284
- var VERSION = true ? "6.0.207" : "0.0.0-test";
1284
+ var VERSION = true ? "6.0.209" : "0.0.0-test";
1285
1285
 
1286
1286
  // src/util/download/download.ts
1287
1287
  var download = async ({
@@ -2505,6 +2505,30 @@ function recordErrorOnSpan(span, error) {
2505
2505
  }
2506
2506
  }
2507
2507
 
2508
+ // src/telemetry/sanitize-attribute-value.ts
2509
+ function isPrimitiveAttributeValue(value) {
2510
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
2511
+ }
2512
+ function sanitizeAttributeValue(value) {
2513
+ if (!Array.isArray(value)) {
2514
+ return value;
2515
+ }
2516
+ const primitiveTypes = new Set(
2517
+ value.filter(isPrimitiveAttributeValue).map((item) => typeof item)
2518
+ );
2519
+ if (primitiveTypes.size !== 1) {
2520
+ return void 0;
2521
+ }
2522
+ const [primitiveType] = primitiveTypes;
2523
+ if (primitiveType === "string") {
2524
+ return value.filter((item) => typeof item === "string");
2525
+ }
2526
+ if (primitiveType === "number") {
2527
+ return value.filter((item) => typeof item === "number");
2528
+ }
2529
+ return value.filter((item) => typeof item === "boolean");
2530
+ }
2531
+
2508
2532
  // src/telemetry/select-telemetry-attributes.ts
2509
2533
  async function selectTelemetryAttributes({
2510
2534
  telemetry,
@@ -2524,7 +2548,9 @@ async function selectTelemetryAttributes({
2524
2548
  }
2525
2549
  const result = await value.input();
2526
2550
  if (result != null) {
2527
- resultAttributes[key] = result;
2551
+ const sanitized2 = sanitizeAttributeValue(result);
2552
+ if (sanitized2 != null)
2553
+ resultAttributes[key] = sanitized2;
2528
2554
  }
2529
2555
  continue;
2530
2556
  }
@@ -2534,11 +2560,15 @@ async function selectTelemetryAttributes({
2534
2560
  }
2535
2561
  const result = await value.output();
2536
2562
  if (result != null) {
2537
- resultAttributes[key] = result;
2563
+ const sanitized2 = sanitizeAttributeValue(result);
2564
+ if (sanitized2 != null)
2565
+ resultAttributes[key] = sanitized2;
2538
2566
  }
2539
2567
  continue;
2540
2568
  }
2541
- resultAttributes[key] = value;
2569
+ const sanitized = sanitizeAttributeValue(value);
2570
+ if (sanitized != null)
2571
+ resultAttributes[key] = sanitized;
2542
2572
  }
2543
2573
  return resultAttributes;
2544
2574
  }
@@ -3349,6 +3379,10 @@ function fixJson(input) {
3349
3379
  const stack = ["ROOT"];
3350
3380
  let lastValidIndex = -1;
3351
3381
  let literalStart = null;
3382
+ let unicodeEscapeDigits = 0;
3383
+ function isHexDigit(char) {
3384
+ return char >= "0" && char <= "9" || char >= "A" && char <= "F" || char >= "a" && char <= "f";
3385
+ }
3352
3386
  function processValueStart(char, i, swapState) {
3353
3387
  {
3354
3388
  switch (char) {
@@ -3553,7 +3587,22 @@ function fixJson(input) {
3553
3587
  }
3554
3588
  case "INSIDE_STRING_ESCAPE": {
3555
3589
  stack.pop();
3556
- lastValidIndex = i;
3590
+ if (char === "u") {
3591
+ unicodeEscapeDigits = 0;
3592
+ stack.push("INSIDE_STRING_UNICODE_ESCAPE");
3593
+ } else {
3594
+ lastValidIndex = i;
3595
+ }
3596
+ break;
3597
+ }
3598
+ case "INSIDE_STRING_UNICODE_ESCAPE": {
3599
+ if (isHexDigit(char)) {
3600
+ unicodeEscapeDigits++;
3601
+ if (unicodeEscapeDigits === 4) {
3602
+ stack.pop();
3603
+ lastValidIndex = i;
3604
+ }
3605
+ }
3557
3606
  break;
3558
3607
  }
3559
3608
  case "INSIDE_NUMBER": {
@@ -8436,7 +8485,9 @@ var DefaultStreamTextResult = class {
8436
8485
  controller.enqueue({
8437
8486
  type: "tool-output-available",
8438
8487
  toolCallId: part.toolCallId,
8439
- output: part.output,
8488
+ // UI stream chunks are serialized as JSON, which drops undefined
8489
+ // properties. Use null so tool outputs always keep the output field.
8490
+ output: part.output === void 0 ? null : part.output,
8440
8491
  ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
8441
8492
  ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
8442
8493
  ...part.toolMetadata != null ? { toolMetadata: part.toolMetadata } : {},