ai 7.0.160 → 7.0.162
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 +23 -0
- package/dist/index.d.ts +46 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -61
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +44 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +45 -16
- package/dist/internal/index.js.map +1 -1
- package/docs/03-agents/06-tool-approvals.mdx +2 -0
- package/docs/03-ai-sdk-core/60-telemetry.mdx +48 -0
- package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +55 -0
- package/package.json +11 -11
- package/src/generate-text/generated-file.ts +7 -4
- package/src/generate-text/validate-tool-approvals.ts +21 -3
- package/src/prompt/clone-model-message.ts +53 -17
- package/src/prompt/content-part.ts +4 -2
- package/src/prompt/data-content.ts +10 -8
- package/src/prompt/file-part-data.ts +9 -5
- package/src/telemetry/index.ts +6 -1
- package/src/telemetry/telemetry.ts +62 -0
|
@@ -296,6 +296,8 @@ In the standard `useChat` pattern, the server rebuilds the conversation from the
|
|
|
296
296
|
|
|
297
297
|
Tool approvals reconstructed from this history are re-validated before execution: the tool input is checked against the tool's schema, and the approval policy is re-evaluated. However, without additional protection, a client that crafts a valid-looking approval for a schema-conforming input can bypass the human-in-the-loop step.
|
|
298
298
|
|
|
299
|
+
During re-validation, `toolCall.toolMetadata` and `toolCall.dynamic` come from the current server-side tool definition. The deprecated `toolCall.title` field is also restored from that definition. A server-side policy denial prevents execution even when the client supplies an approved response with a valid signature.
|
|
300
|
+
|
|
299
301
|
If your tools perform sensitive operations (modifying data, spending money, calling external APIs, accessing private resources), use `experimental_toolApprovalSecret` to cryptographically bind approvals to the server that issued them.
|
|
300
302
|
|
|
301
303
|
### Signing approvals with `experimental_toolApprovalSecret`
|
|
@@ -253,6 +253,42 @@ registerTelemetry(
|
|
|
253
253
|
);
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
## Durable WorkflowAgent runs
|
|
257
|
+
|
|
258
|
+
The steps of a durable `WorkflowAgent` run (`@ai-sdk/workflow`) can execute in
|
|
259
|
+
different processes. To report the run as one `invoke_agent` span tree, use the
|
|
260
|
+
same `OpenTelemetryIdGenerator` instance as your tracer provider's ID generator
|
|
261
|
+
and sampler, and pass it to the globally registered `OpenTelemetry` integration:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { OpenTelemetry, OpenTelemetryIdGenerator } from '@ai-sdk/otel';
|
|
265
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
266
|
+
import { registerTelemetry } from 'ai';
|
|
267
|
+
|
|
268
|
+
const durable = new OpenTelemetryIdGenerator();
|
|
269
|
+
new NodeSDK({ idGenerator: durable, sampler: durable }).start();
|
|
270
|
+
registerTelemetry(new OpenTelemetry({ durable }));
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Model calls are reported live from their durable steps, so spans started by the
|
|
274
|
+
provider call (for example auto-instrumented HTTP spans) nest under their `chat`
|
|
275
|
+
span. The root, step, and tool spans appear when the run ends or suspends (for
|
|
276
|
+
example on a tool approval), using the original event timestamps. The root
|
|
277
|
+
includes the run's prompt and final output, subject to `recordInputs` and
|
|
278
|
+
`recordOutputs`.
|
|
279
|
+
|
|
280
|
+
A preparation step captures the active parent trace and makes the run's sampling
|
|
281
|
+
decision once. That context is carried to subsequent steps, preserving the trace
|
|
282
|
+
ID, sampling decision, and trace state. By default, the bridge uses parent-based
|
|
283
|
+
sampling with roots enabled. To use another sampler, pass it through
|
|
284
|
+
`new OpenTelemetryIdGenerator({ sampler: yourSampler })` and keep the bridge as
|
|
285
|
+
the provider's sampler. Unrelated spans use random IDs and the configured sampler.
|
|
286
|
+
|
|
287
|
+
Install this setup in every process that executes workflow steps, with integrations
|
|
288
|
+
registered in the same order. Without the bridge, `OpenTelemetry` preserves live
|
|
289
|
+
per-model operation traces. Other globally registered telemetry integrations receive
|
|
290
|
+
the recorded run lifecycle when it ends or suspends.
|
|
291
|
+
|
|
256
292
|
## Telemetry Integrations
|
|
257
293
|
|
|
258
294
|
Telemetry integrations let you hook into the generation lifecycle to build custom observability — logging, analytics, DevTools, or any other monitoring system. Instead of wiring up individual callbacks on every call, you implement a `Telemetry` once and register it globally or pass it via `telemetry.integrations`.
|
|
@@ -395,6 +431,18 @@ export function myIntegration(): Telemetry {
|
|
|
395
431
|
}
|
|
396
432
|
```
|
|
397
433
|
|
|
434
|
+
For durable workflows, integrations normally receive recorded events after the
|
|
435
|
+
run ends. Events can include `timestamp` (Unix milliseconds); use it for span
|
|
436
|
+
start and end times instead of the current time. Integrations that can reconstruct
|
|
437
|
+
traces across processes can implement the experimental
|
|
438
|
+
`experimental_durable.captureContext(event)` hook. It runs in a preparation step
|
|
439
|
+
and returns a serializable `DurableTelemetryContext`. Live model-start events
|
|
440
|
+
then carry that context in `event.durableContext` with `phase: 'live'`; the
|
|
441
|
+
recorded operation-start event carries it with `phase: 'replay'`. Such integrations
|
|
442
|
+
should skip replayed model events to avoid duplicate spans. Returning `undefined`
|
|
443
|
+
selects live per-model operations instead, with no deferred replay to that
|
|
444
|
+
integration.
|
|
445
|
+
|
|
398
446
|
### Available lifecycle methods
|
|
399
447
|
|
|
400
448
|
<PropertiesTable
|
|
@@ -579,6 +579,61 @@ to `'.'` to use the sandbox's default working directory itself. When omitted,
|
|
|
579
579
|
regular sessions use the default `<harnessId>-<sessionId>` directory, while
|
|
580
580
|
`onBootstrap` receives the sandbox's default working directory.
|
|
581
581
|
|
|
582
|
+
When the harness declares a sandbox bootstrap recipe, as Claude Code and Codex
|
|
583
|
+
do, `createSession()` runs the session's sandbox setup before the harness
|
|
584
|
+
starts: it runs `onBootstrap` when its marker is missing, creates the session
|
|
585
|
+
working directory, and calls `onSession`. For any other harness, such as Pi,
|
|
586
|
+
the setup runs on the first file, exec, or spawn operation that the harness or
|
|
587
|
+
a host tool performs on the sandbox. A session whose turns never touch the
|
|
588
|
+
sandbox then makes no setup calls. If the setup fails, the operation that
|
|
589
|
+
triggered it rejects, and the next operation runs the setup again. A sandbox
|
|
590
|
+
session without `defaultWorkingDirectory` still answers one `pwd` in
|
|
591
|
+
`createSession()`, because the session working directory is derived from it.
|
|
592
|
+
When the sandbox comes from the `sandbox` provider setting instead of
|
|
593
|
+
`sandboxSession`, the provider runs `onBootstrap` when it creates a new
|
|
594
|
+
sandbox, which happens in `createSession()`.
|
|
595
|
+
|
|
596
|
+
## Start the Sandbox on First Use
|
|
597
|
+
|
|
598
|
+
Deferring the setup does not defer the sandbox itself: a session you pass to
|
|
599
|
+
`createSession()` already exists. To defer the sandbox as well, pass
|
|
600
|
+
`defaultWorkingDirectory` and a `createSandboxSession` function instead. With
|
|
601
|
+
a harness that has no sandbox bootstrap recipe, such as Pi, `createSession()`
|
|
602
|
+
returns without calling `createSandboxSession`, and the first file, exec, or
|
|
603
|
+
spawn operation calls it, so a turn that only chats never starts the sandbox.
|
|
604
|
+
For any other harness, `createSession()` calls it right away.
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
import { HarnessAgent } from '@ai-sdk/harness/agent';
|
|
608
|
+
import { pi } from '@ai-sdk/harness-pi';
|
|
609
|
+
import { createVercelNetworkSandboxSessionFromNativeSandbox } from '@ai-sdk/sandbox-vercel';
|
|
610
|
+
import { Sandbox } from '@vercel/sandbox';
|
|
611
|
+
|
|
612
|
+
const agent = new HarnessAgent({ harness: pi });
|
|
613
|
+
|
|
614
|
+
const session = await agent.createSession({
|
|
615
|
+
sessionId: chatId,
|
|
616
|
+
resumeFrom: resumeState,
|
|
617
|
+
sandboxSession: {
|
|
618
|
+
defaultWorkingDirectory: '/vercel/sandbox',
|
|
619
|
+
createSandboxSession: async () =>
|
|
620
|
+
createVercelNetworkSandboxSessionFromNativeSandbox(
|
|
621
|
+
await Sandbox.getOrCreate({
|
|
622
|
+
name: `chat-${chatId}`,
|
|
623
|
+
runtime: 'node24',
|
|
624
|
+
}),
|
|
625
|
+
),
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
The harness needs the working directory before the sandbox exists, so
|
|
631
|
+
`defaultWorkingDirectory` must match the one the sandbox reports. Because the
|
|
632
|
+
sandbox is looked up by name, the same call works for the first turn and for
|
|
633
|
+
every later one. A failed `createSandboxSession` or setup is retried on the
|
|
634
|
+
next operation. You own the sandbox lifecycle, as with any session you pass to
|
|
635
|
+
`createSession()`.
|
|
636
|
+
|
|
582
637
|
## Prepare Reusable Sandboxes
|
|
583
638
|
|
|
584
639
|
Use `agent.getSandboxTemplate()` to resolve one harness's bootstrap recipe and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.162",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.135",
|
|
46
46
|
"@ai-sdk/provider": "4.0.35",
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.74"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
51
|
-
"@ai-sdk/deepseek": "3.0.
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
53
|
-
"@ai-sdk/groq": "4.0.
|
|
54
|
-
"@ai-sdk/huggingface": "2.0.
|
|
55
|
-
"@ai-sdk/moonshotai": "3.0.
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.132",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.79",
|
|
52
|
+
"@ai-sdk/google": "4.0.111",
|
|
53
|
+
"@ai-sdk/groq": "4.0.76",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.84",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.84",
|
|
56
|
+
"@ai-sdk/openai": "4.0.108",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.4",
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
58
|
+
"@ai-sdk/xai": "5.0.38",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { convertDataContentToUint8Array } from '../prompt/data-content';
|
|
1
2
|
import {
|
|
2
3
|
convertBase64ToUint8Array,
|
|
3
4
|
convertUint8ArrayToBase64,
|
|
@@ -48,13 +49,15 @@ export class DefaultGeneratedFile implements GeneratedFile {
|
|
|
48
49
|
mediaType,
|
|
49
50
|
providerMetadata,
|
|
50
51
|
}: {
|
|
51
|
-
data: string | Uint8Array;
|
|
52
|
+
data: string | Uint8Array | ArrayBuffer;
|
|
52
53
|
mediaType: string;
|
|
53
54
|
providerMetadata?: Record<string, JSONObject>;
|
|
54
55
|
}) {
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
|
|
56
|
+
this.base64Data = typeof data === 'string' ? data : undefined;
|
|
57
|
+
this.uint8ArrayData =
|
|
58
|
+
typeof data === 'string'
|
|
59
|
+
? undefined
|
|
60
|
+
: convertDataContentToUint8Array(data);
|
|
58
61
|
this.mediaType = mediaType;
|
|
59
62
|
this.providerMetadata = providerMetadata;
|
|
60
63
|
}
|
|
@@ -57,13 +57,31 @@ export async function validateApprovedToolApprovals<
|
|
|
57
57
|
CollectedToolApprovals<TOOLS> & { error: InvalidToolInputError }
|
|
58
58
|
> = [];
|
|
59
59
|
|
|
60
|
-
for (const
|
|
61
|
-
const { approvalRequest, toolCall } = approval;
|
|
60
|
+
for (const collectedApproval of approvedToolApprovals) {
|
|
62
61
|
// Look up the tool by own property only: `toolName` comes from
|
|
63
62
|
// client-supplied history, so a name matching an inherited object property
|
|
64
63
|
// (e.g. `constructor`, `toString`) must resolve to "no such tool" rather
|
|
65
64
|
// than a prototype value that would silently skip input validation below.
|
|
66
|
-
const tool = getOwn(tools, toolCall.toolName);
|
|
65
|
+
const tool = getOwn(tools, collectedApproval.toolCall.toolName);
|
|
66
|
+
|
|
67
|
+
// History can omit or alter these fields. Rebuild them from the current
|
|
68
|
+
// server-side definition before input refinement and approval policy checks,
|
|
69
|
+
// just as parsing a new model tool call does.
|
|
70
|
+
const {
|
|
71
|
+
toolMetadata: _toolMetadata,
|
|
72
|
+
dynamic: _dynamic,
|
|
73
|
+
...toolCallFromHistory
|
|
74
|
+
} = collectedApproval.toolCall;
|
|
75
|
+
const approval: CollectedToolApprovals<TOOLS> = {
|
|
76
|
+
...collectedApproval,
|
|
77
|
+
toolCall: {
|
|
78
|
+
...toolCallFromHistory,
|
|
79
|
+
...(tool?.metadata != null ? { toolMetadata: tool.metadata } : {}),
|
|
80
|
+
...(tool?.type === 'dynamic' ? { dynamic: true } : {}),
|
|
81
|
+
title: tool?.title,
|
|
82
|
+
} as typeof collectedApproval.toolCall,
|
|
83
|
+
};
|
|
84
|
+
const { approvalRequest, toolCall } = approval;
|
|
67
85
|
|
|
68
86
|
if (toolApprovalSecret != null) {
|
|
69
87
|
if (approvalRequest.signature == null) {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
isArrayBuffer,
|
|
3
|
+
isUint8Array,
|
|
4
|
+
type ModelMessage,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Clone model messages while preserving URL instances. Node's structuredClone
|
|
@@ -7,35 +11,67 @@ import type { ModelMessage } from '@ai-sdk/provider-utils';
|
|
|
7
11
|
export function cloneModelMessages<T extends ModelMessage>(
|
|
8
12
|
messages: Array<T>,
|
|
9
13
|
): Array<T> {
|
|
10
|
-
return messages
|
|
14
|
+
return cloneValue(messages, new Map()) as Array<T>;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
function cloneValue
|
|
17
|
+
function cloneValue(value: unknown, seen: Map<object, unknown>): unknown {
|
|
18
|
+
if (value == null || typeof value !== 'object') {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (seen.has(value)) {
|
|
23
|
+
return seen.get(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
if (value instanceof URL) {
|
|
15
|
-
|
|
27
|
+
const copy = new URL(value.href);
|
|
28
|
+
seen.set(value, copy);
|
|
29
|
+
return copy;
|
|
16
30
|
}
|
|
17
31
|
|
|
18
32
|
if (Array.isArray(value)) {
|
|
19
|
-
|
|
33
|
+
const copy: unknown[] = new Array(value.length);
|
|
34
|
+
seen.set(value, copy);
|
|
35
|
+
value.forEach((item, index) => {
|
|
36
|
+
copy[index] = cloneValue(item, seen);
|
|
37
|
+
});
|
|
38
|
+
return copy;
|
|
20
39
|
}
|
|
21
40
|
|
|
22
|
-
if (value
|
|
23
|
-
|
|
41
|
+
if (isUint8Array(value)) {
|
|
42
|
+
const copy = new Uint8Array(value);
|
|
43
|
+
seen.set(value, copy);
|
|
44
|
+
return copy;
|
|
24
45
|
}
|
|
25
46
|
|
|
26
|
-
if (value
|
|
27
|
-
|
|
47
|
+
if (isArrayBuffer(value)) {
|
|
48
|
+
const copy = new Uint8Array(value).slice().buffer;
|
|
49
|
+
seen.set(value, copy);
|
|
50
|
+
return copy;
|
|
28
51
|
}
|
|
29
52
|
|
|
30
|
-
if (
|
|
31
|
-
|
|
53
|
+
if (
|
|
54
|
+
value instanceof Date ||
|
|
55
|
+
Object.prototype.toString.call(value) === '[object Date]'
|
|
56
|
+
) {
|
|
57
|
+
try {
|
|
58
|
+
const copy = new Date(Date.prototype.getTime.call(value));
|
|
59
|
+
seen.set(value, copy);
|
|
60
|
+
return copy;
|
|
61
|
+
} catch {
|
|
62
|
+
// A string tag alone does not make a plain object a Date.
|
|
63
|
+
}
|
|
32
64
|
}
|
|
33
65
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
66
|
+
const copy: Record<string, unknown> = {};
|
|
67
|
+
seen.set(value, copy);
|
|
68
|
+
for (const [key, val] of Object.entries(value)) {
|
|
69
|
+
Object.defineProperty(copy, key, {
|
|
70
|
+
value: cloneValue(val, seen),
|
|
71
|
+
writable: true,
|
|
72
|
+
enumerable: true,
|
|
73
|
+
configurable: true,
|
|
74
|
+
});
|
|
38
75
|
}
|
|
39
|
-
|
|
40
|
-
return value;
|
|
76
|
+
return copy;
|
|
41
77
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isBuffer,
|
|
3
|
+
isUint8Array,
|
|
4
|
+
isArrayBuffer,
|
|
3
5
|
type CustomPart,
|
|
4
6
|
type DataContent,
|
|
5
7
|
type FilePart,
|
|
@@ -19,8 +21,8 @@ import { z, type ZodType } from '../util/zod';
|
|
|
19
21
|
|
|
20
22
|
const fileInlineDataSchema: ZodType<DataContent> = z.union([
|
|
21
23
|
z.string(),
|
|
22
|
-
z.
|
|
23
|
-
z.
|
|
24
|
+
z.custom<Uint8Array>(isUint8Array, { message: 'Must be a Uint8Array' }),
|
|
25
|
+
z.custom<ArrayBuffer>(isArrayBuffer, { message: 'Must be an ArrayBuffer' }),
|
|
24
26
|
z.custom<Buffer>(isBuffer, { message: 'Must be a Buffer' }),
|
|
25
27
|
]);
|
|
26
28
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isUint8Array,
|
|
3
|
+
isArrayBuffer,
|
|
2
4
|
convertBase64ToUint8Array,
|
|
3
5
|
convertUint8ArrayToBase64,
|
|
4
6
|
type DataContent,
|
|
@@ -16,11 +18,7 @@ export function convertDataContentToBase64String(content: DataContent): string {
|
|
|
16
18
|
return content;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
return convertUint8ArrayToBase64(new Uint8Array(content));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return convertUint8ArrayToBase64(content);
|
|
21
|
+
return convertUint8ArrayToBase64(convertDataContentToUint8Array(content));
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
/**
|
|
@@ -32,8 +30,12 @@ export function convertDataContentToBase64String(content: DataContent): string {
|
|
|
32
30
|
export function convertDataContentToUint8Array(
|
|
33
31
|
content: DataContent,
|
|
34
32
|
): Uint8Array {
|
|
35
|
-
if (content
|
|
36
|
-
|
|
33
|
+
if (isUint8Array(content)) {
|
|
34
|
+
if (content instanceof Uint8Array) {
|
|
35
|
+
return content;
|
|
36
|
+
}
|
|
37
|
+
const { buffer, byteOffset, byteLength } = content;
|
|
38
|
+
return new Uint8Array(buffer, byteOffset, byteLength);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
if (typeof content === 'string') {
|
|
@@ -49,7 +51,7 @@ export function convertDataContentToUint8Array(
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
if (content
|
|
54
|
+
if (isArrayBuffer(content)) {
|
|
53
55
|
return new Uint8Array(content);
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { convertDataContentToUint8Array } from './data-content';
|
|
1
2
|
import type { LanguageModelV4FilePart } from '@ai-sdk/provider';
|
|
2
3
|
import {
|
|
4
|
+
isUint8Array,
|
|
5
|
+
isArrayBuffer,
|
|
3
6
|
isBuffer,
|
|
4
7
|
isProviderReference,
|
|
5
8
|
type DataContent,
|
|
@@ -58,12 +61,9 @@ function convertUrlStringToFilePartData(content: string): ConvertResult {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
function convertInlineDataToFilePartData(content: DataContent): ConvertResult {
|
|
61
|
-
if (content
|
|
62
|
-
return { data: { type: 'data', data: content }, mediaType: undefined };
|
|
63
|
-
}
|
|
64
|
-
if (content instanceof ArrayBuffer) {
|
|
64
|
+
if (isUint8Array(content) || isArrayBuffer(content)) {
|
|
65
65
|
return {
|
|
66
|
-
data: { type: 'data', data:
|
|
66
|
+
data: { type: 'data', data: convertDataContentToUint8Array(content) },
|
|
67
67
|
mediaType: undefined,
|
|
68
68
|
};
|
|
69
69
|
}
|
|
@@ -130,6 +130,10 @@ export function convertToLanguageModelV4FilePart(
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
if (isUint8Array(content) || isArrayBuffer(content)) {
|
|
134
|
+
return convertInlineDataToFilePartData(content);
|
|
135
|
+
}
|
|
136
|
+
|
|
133
137
|
if (isProviderReference(content)) {
|
|
134
138
|
return {
|
|
135
139
|
data: { type: 'reference', reference: content as ProviderReference },
|
package/src/telemetry/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export type { TelemetryOptions } from './telemetry-options';
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
DurableTelemetryContext,
|
|
4
|
+
DurableTelemetryEventContext,
|
|
5
|
+
InferTelemetryEvent,
|
|
6
|
+
Telemetry,
|
|
7
|
+
} from './telemetry';
|
|
3
8
|
export { registerTelemetry } from './telemetry-registry';
|
|
4
9
|
export {
|
|
5
10
|
AI_SDK_TELEMETRY_TRACING_CHANNEL,
|
|
@@ -56,6 +56,43 @@ import type { TelemetryOptions } from '../telemetry/telemetry-options';
|
|
|
56
56
|
import type { TelemetryTracingEventType } from './tracing-channel';
|
|
57
57
|
import type { TracingChannelContext } from './tracing-channel-publisher';
|
|
58
58
|
|
|
59
|
+
/** Serializable tracing identity shared by the processes of a durable run. */
|
|
60
|
+
export type DurableTelemetryContext = {
|
|
61
|
+
traceId: string;
|
|
62
|
+
rootSpanId: string;
|
|
63
|
+
/** The integration's root-span data, reused when the run is reported. */
|
|
64
|
+
rootSpanName?: string;
|
|
65
|
+
rootAttributes?: Record<
|
|
66
|
+
string,
|
|
67
|
+
| string
|
|
68
|
+
| number
|
|
69
|
+
| boolean
|
|
70
|
+
| Array<string | null | undefined>
|
|
71
|
+
| Array<number | null | undefined>
|
|
72
|
+
| Array<boolean | null | undefined>
|
|
73
|
+
| undefined
|
|
74
|
+
>;
|
|
75
|
+
traceFlags: number;
|
|
76
|
+
traceState?: string;
|
|
77
|
+
samplingDecision: 0 | 1 | 2;
|
|
78
|
+
parentSpanContext?: {
|
|
79
|
+
traceId: string;
|
|
80
|
+
spanId: string;
|
|
81
|
+
traceFlags: number;
|
|
82
|
+
traceState?: string;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** Identifies live model events and the run events reported after completion. */
|
|
87
|
+
export type DurableTelemetryEventContext = {
|
|
88
|
+
run: DurableTelemetryContext;
|
|
89
|
+
stepNumber?: number;
|
|
90
|
+
phase: 'live' | 'replay';
|
|
91
|
+
/** Context has already been filtered before crossing the step boundary. */
|
|
92
|
+
runtimeContext?: Record<string, unknown>;
|
|
93
|
+
headers?: Record<string, string | undefined>;
|
|
94
|
+
};
|
|
95
|
+
|
|
59
96
|
export type InferTelemetryEvent<EVENT> = EVENT &
|
|
60
97
|
Omit<
|
|
61
98
|
TelemetryOptions,
|
|
@@ -66,6 +103,16 @@ export type InferTelemetryEvent<EVENT> = EVENT &
|
|
|
66
103
|
* Absent in older releases.
|
|
67
104
|
*/
|
|
68
105
|
readonly aiVersion?: string;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Time at which the event occurred, in milliseconds since the Unix epoch.
|
|
109
|
+
*
|
|
110
|
+
* Set when events are reported after they happened, for example when a
|
|
111
|
+
* durable workflow reports a run that spanned several processes.
|
|
112
|
+
* Integrations should use it instead of the current time when present.
|
|
113
|
+
*/
|
|
114
|
+
readonly timestamp?: number;
|
|
115
|
+
readonly durableContext?: DurableTelemetryEventContext;
|
|
69
116
|
};
|
|
70
117
|
|
|
71
118
|
type BivariantCallback<EVENT> = {
|
|
@@ -142,6 +189,21 @@ export interface TelemetryDispatcher {
|
|
|
142
189
|
* Methods can be sync or return a PromiseLike.
|
|
143
190
|
*/
|
|
144
191
|
export interface Telemetry {
|
|
192
|
+
/**
|
|
193
|
+
* Capture a serializable tracing identity before a durable run's model calls.
|
|
194
|
+
* Return undefined to preserve live per-model operations when the integration
|
|
195
|
+
* cannot reconstruct a run span across processes. Integrations without this
|
|
196
|
+
* hook receive the recorded lifecycle after completion.
|
|
197
|
+
*/
|
|
198
|
+
experimental_durable?: {
|
|
199
|
+
captureContext: (
|
|
200
|
+
event: InferTelemetryEvent<GenerateTextStartEvent>,
|
|
201
|
+
) =>
|
|
202
|
+
| DurableTelemetryContext
|
|
203
|
+
| undefined
|
|
204
|
+
| PromiseLike<DurableTelemetryContext | undefined>;
|
|
205
|
+
};
|
|
206
|
+
|
|
145
207
|
/**
|
|
146
208
|
* Called when an operation begins. Fired for text generation
|
|
147
209
|
* (generateText/streamText), object generation (generateObject/streamObject),
|