langsmith 0.7.0 → 0.7.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.
Files changed (39) hide show
  1. package/dist/client.cjs +1 -1
  2. package/dist/client.js +1 -1
  3. package/dist/experimental/vercel/index.cjs +18 -637
  4. package/dist/experimental/vercel/index.d.ts +3 -257
  5. package/dist/experimental/vercel/index.js +2 -635
  6. package/dist/experimental/vercel/middleware.cjs +2 -78
  7. package/dist/experimental/vercel/middleware.js +1 -77
  8. package/dist/experimental/vercel/telemetry.cjs +462 -0
  9. package/dist/experimental/vercel/telemetry.d.ts +89 -0
  10. package/dist/experimental/vercel/telemetry.js +459 -0
  11. package/dist/experimental/vercel/utils.cjs +142 -35
  12. package/dist/experimental/vercel/utils.d.ts +28 -3
  13. package/dist/experimental/vercel/utils.js +140 -34
  14. package/dist/experimental/vercel/wrap.cjs +639 -0
  15. package/dist/experimental/vercel/wrap.d.ts +257 -0
  16. package/dist/experimental/vercel/wrap.js +635 -0
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/sandbox/client.cjs +21 -0
  21. package/dist/sandbox/client.d.ts +1 -0
  22. package/dist/sandbox/client.js +21 -0
  23. package/dist/sandbox/sandbox.cjs +12 -1
  24. package/dist/sandbox/sandbox.js +12 -1
  25. package/dist/sandbox/types.d.ts +12 -0
  26. package/dist/sandbox/ws_execute.cjs +11 -7
  27. package/dist/sandbox/ws_execute.d.ts +2 -1
  28. package/dist/sandbox/ws_execute.js +11 -7
  29. package/dist/utils/types.cjs +13 -0
  30. package/dist/utils/types.d.ts +2 -0
  31. package/dist/utils/types.js +8 -0
  32. package/dist/utils/vercel.cjs +68 -10
  33. package/dist/utils/vercel.d.ts +17 -2
  34. package/dist/utils/vercel.js +68 -10
  35. package/experimental/sandbox.cjs +1 -0
  36. package/experimental/sandbox.d.cts +1 -0
  37. package/experimental/sandbox.d.ts +1 -0
  38. package/experimental/sandbox.js +1 -0
  39. package/package.json +22 -7
@@ -89,6 +89,12 @@ class SandboxClient {
89
89
  writable: true,
90
90
  value: void 0
91
91
  });
92
+ Object.defineProperty(this, "_defaultHeaders", {
93
+ enumerable: true,
94
+ configurable: true,
95
+ writable: true,
96
+ value: void 0
97
+ });
92
98
  Object.defineProperty(this, "_fetchImpl", {
93
99
  enumerable: true,
94
100
  configurable: true,
@@ -103,6 +109,7 @@ class SandboxClient {
103
109
  });
104
110
  this._baseUrl = (config.apiEndpoint ?? getDefaultApiEndpoint()).replace(/\/$/, "");
105
111
  this._apiKey = config.apiKey ?? getDefaultApiKey();
112
+ this._defaultHeaders = { ...(config.headers ?? {}) };
106
113
  this._fetchImpl = (0, fetch_js_1._getFetchImplementation)();
107
114
  this._caller = new async_caller_js_1.AsyncCaller({
108
115
  maxRetries: config.maxRetries ?? 3,
@@ -122,6 +129,11 @@ class SandboxClient {
122
129
  if (this._apiKey) {
123
130
  headers.set("X-Api-Key", this._apiKey);
124
131
  }
132
+ for (const [name, value] of Object.entries(this._defaultHeaders)) {
133
+ if (!headers.has(name)) {
134
+ headers.set(name, value);
135
+ }
136
+ }
125
137
  return this._caller.call(() => this._fetchImpl(url, {
126
138
  ...init,
127
139
  headers,
@@ -134,6 +146,15 @@ class SandboxClient {
134
146
  getApiKey() {
135
147
  return this._apiKey;
136
148
  }
149
+ /**
150
+ * Get the constructor-supplied default headers. Used by the WebSocket exec
151
+ * path so headers like `X-Service-Key` set on the client are attached to
152
+ * the WS upgrade request.
153
+ * @internal
154
+ */
155
+ getDefaultHeaders() {
156
+ return { ...this._defaultHeaders };
157
+ }
137
158
  /**
138
159
  * JSON POST helper. Sends JSON body, checks response status,
139
160
  * and returns the Response for further processing.
@@ -34,6 +34,7 @@ import { Sandbox } from "./sandbox.js";
34
34
  export declare class SandboxClient {
35
35
  private _baseUrl;
36
36
  private _apiKey?;
37
+ private _defaultHeaders;
37
38
  private _fetchImpl;
38
39
  private _caller;
39
40
  constructor(config?: SandboxClientConfig);
@@ -86,6 +86,12 @@ export class SandboxClient {
86
86
  writable: true,
87
87
  value: void 0
88
88
  });
89
+ Object.defineProperty(this, "_defaultHeaders", {
90
+ enumerable: true,
91
+ configurable: true,
92
+ writable: true,
93
+ value: void 0
94
+ });
89
95
  Object.defineProperty(this, "_fetchImpl", {
90
96
  enumerable: true,
91
97
  configurable: true,
@@ -100,6 +106,7 @@ export class SandboxClient {
100
106
  });
101
107
  this._baseUrl = (config.apiEndpoint ?? getDefaultApiEndpoint()).replace(/\/$/, "");
102
108
  this._apiKey = config.apiKey ?? getDefaultApiKey();
109
+ this._defaultHeaders = { ...(config.headers ?? {}) };
103
110
  this._fetchImpl = _getFetchImplementation();
104
111
  this._caller = new AsyncCaller({
105
112
  maxRetries: config.maxRetries ?? 3,
@@ -119,6 +126,11 @@ export class SandboxClient {
119
126
  if (this._apiKey) {
120
127
  headers.set("X-Api-Key", this._apiKey);
121
128
  }
129
+ for (const [name, value] of Object.entries(this._defaultHeaders)) {
130
+ if (!headers.has(name)) {
131
+ headers.set(name, value);
132
+ }
133
+ }
122
134
  return this._caller.call(() => this._fetchImpl(url, {
123
135
  ...init,
124
136
  headers,
@@ -131,6 +143,15 @@ export class SandboxClient {
131
143
  getApiKey() {
132
144
  return this._apiKey;
133
145
  }
146
+ /**
147
+ * Get the constructor-supplied default headers. Used by the WebSocket exec
148
+ * path so headers like `X-Service-Key` set on the client are attached to
149
+ * the WS upgrade request.
150
+ * @internal
151
+ */
152
+ getDefaultHeaders() {
153
+ return { ...this._defaultHeaders };
154
+ }
134
155
  /**
135
156
  * JSON POST helper. Sends JSON body, checks response status,
136
157
  * and returns the Response for further processing.
@@ -227,6 +227,7 @@ class Sandbox {
227
227
  async _runWs(command, options = {}) {
228
228
  const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, idleTimeout, killOnDisconnect, ttlSeconds, pty, } = options;
229
229
  const dataplaneUrl = this.requireDataplaneUrl();
230
+ const clientHeaders = this._client.getDefaultHeaders();
230
231
  const [stream, control] = await (0, ws_execute_js_1.runWsStream)(dataplaneUrl, this._client.getApiKey(), command, {
231
232
  timeout,
232
233
  env,
@@ -238,6 +239,9 @@ class Sandbox {
238
239
  killOnDisconnect,
239
240
  ttlSeconds,
240
241
  pty,
242
+ ...(Object.keys(clientHeaders).length > 0
243
+ ? { headers: clientHeaders }
244
+ : {}),
241
245
  });
242
246
  const handle = new command_handle_js_1.CommandHandle(stream, control, this);
243
247
  await handle._ensureStarted();
@@ -292,7 +296,14 @@ class Sandbox {
292
296
  async reconnect(commandId, options = {}) {
293
297
  const { stdoutOffset = 0, stderrOffset = 0 } = options;
294
298
  const dataplaneUrl = this.requireDataplaneUrl();
295
- const [stream, control] = await (0, ws_execute_js_1.reconnectWsStream)(dataplaneUrl, this._client.getApiKey(), commandId, { stdoutOffset, stderrOffset });
299
+ const clientHeaders = this._client.getDefaultHeaders();
300
+ const [stream, control] = await (0, ws_execute_js_1.reconnectWsStream)(dataplaneUrl, this._client.getApiKey(), commandId, {
301
+ stdoutOffset,
302
+ stderrOffset,
303
+ ...(Object.keys(clientHeaders).length > 0
304
+ ? { headers: clientHeaders }
305
+ : {}),
306
+ });
296
307
  return new command_handle_js_1.CommandHandle(stream, control, this, {
297
308
  commandId,
298
309
  stdoutOffset,
@@ -224,6 +224,7 @@ export class Sandbox {
224
224
  async _runWs(command, options = {}) {
225
225
  const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, idleTimeout, killOnDisconnect, ttlSeconds, pty, } = options;
226
226
  const dataplaneUrl = this.requireDataplaneUrl();
227
+ const clientHeaders = this._client.getDefaultHeaders();
227
228
  const [stream, control] = await runWsStream(dataplaneUrl, this._client.getApiKey(), command, {
228
229
  timeout,
229
230
  env,
@@ -235,6 +236,9 @@ export class Sandbox {
235
236
  killOnDisconnect,
236
237
  ttlSeconds,
237
238
  pty,
239
+ ...(Object.keys(clientHeaders).length > 0
240
+ ? { headers: clientHeaders }
241
+ : {}),
238
242
  });
239
243
  const handle = new CommandHandle(stream, control, this);
240
244
  await handle._ensureStarted();
@@ -289,7 +293,14 @@ export class Sandbox {
289
293
  async reconnect(commandId, options = {}) {
290
294
  const { stdoutOffset = 0, stderrOffset = 0 } = options;
291
295
  const dataplaneUrl = this.requireDataplaneUrl();
292
- const [stream, control] = await reconnectWsStream(dataplaneUrl, this._client.getApiKey(), commandId, { stdoutOffset, stderrOffset });
296
+ const clientHeaders = this._client.getDefaultHeaders();
297
+ const [stream, control] = await reconnectWsStream(dataplaneUrl, this._client.getApiKey(), commandId, {
298
+ stdoutOffset,
299
+ stderrOffset,
300
+ ...(Object.keys(clientHeaders).length > 0
301
+ ? { headers: clientHeaders }
302
+ : {}),
303
+ });
293
304
  return new CommandHandle(stream, control, this, {
294
305
  commandId,
295
306
  stdoutOffset,
@@ -108,6 +108,13 @@ export interface SandboxClientConfig {
108
108
  * Defaults to Infinity (no limit).
109
109
  */
110
110
  maxConcurrency?: number;
111
+ /**
112
+ * Optional default headers attached to every request on this client,
113
+ * including the data-plane `/execute` HTTP endpoint and the `/execute/ws`
114
+ * WebSocket upgrade. Use this to pass additional auth headers
115
+ * (e.g. `X-Service-Key`).
116
+ */
117
+ headers?: Record<string, string>;
111
118
  }
112
119
  /**
113
120
  * A single chunk of streaming output from command execution.
@@ -163,6 +170,11 @@ export interface WsRunOptions {
163
170
  ttlSeconds?: number;
164
171
  /** Whether to allocate a PTY. */
165
172
  pty?: boolean;
173
+ /**
174
+ * Additional headers attached to the WebSocket upgrade request. Merged on
175
+ * top of any default headers the SandboxClient was constructed with.
176
+ */
177
+ headers?: Record<string, string>;
166
178
  }
167
179
  /**
168
180
  * Options for running a command in a sandbox.
@@ -38,11 +38,15 @@ function buildWsUrl(dataplaneUrl) {
38
38
  /**
39
39
  * Build auth headers for the WebSocket upgrade request.
40
40
  */
41
- function buildAuthHeaders(apiKey) {
41
+ function buildAuthHeaders(apiKey, extraHeaders) {
42
+ const headers = {};
42
43
  if (apiKey) {
43
- return { "X-Api-Key": apiKey };
44
+ headers["X-Api-Key"] = apiKey;
44
45
  }
45
- return {};
46
+ if (extraHeaders) {
47
+ Object.assign(headers, extraHeaders);
48
+ }
49
+ return headers;
46
50
  }
47
51
  // =============================================================================
48
52
  // Stream Control
@@ -238,9 +242,9 @@ async function* readWsMessages(ws) {
238
242
  * data arrives in addition to yielding the messages.
239
243
  */
240
244
  async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
241
- const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, } = options;
245
+ const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, } = options;
242
246
  const wsUrl = buildWsUrl(dataplaneUrl);
243
- const headers = buildAuthHeaders(apiKey);
247
+ const headers = buildAuthHeaders(apiKey, extraHeaders);
244
248
  const control = new WSStreamControl();
245
249
  async function* stream() {
246
250
  let ws;
@@ -308,9 +312,9 @@ async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
308
312
  * No 'started' message is sent on reconnection.
309
313
  */
310
314
  async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {}) {
311
- const { stdoutOffset = 0, stderrOffset = 0 } = options;
315
+ const { stdoutOffset = 0, stderrOffset = 0, headers: extraHeaders } = options;
312
316
  const wsUrl = buildWsUrl(dataplaneUrl);
313
- const headers = buildAuthHeaders(apiKey);
317
+ const headers = buildAuthHeaders(apiKey, extraHeaders);
314
318
  const control = new WSStreamControl();
315
319
  async function* stream() {
316
320
  let ws;
@@ -13,7 +13,7 @@ export declare function buildWsUrl(dataplaneUrl: string): string;
13
13
  /**
14
14
  * Build auth headers for the WebSocket upgrade request.
15
15
  */
16
- export declare function buildAuthHeaders(apiKey: string | undefined): Record<string, string>;
16
+ export declare function buildAuthHeaders(apiKey: string | undefined, extraHeaders?: Record<string, string>): Record<string, string>;
17
17
  /**
18
18
  * Control interface for an active WebSocket stream.
19
19
  *
@@ -66,5 +66,6 @@ export declare function runWsStream(dataplaneUrl: string, apiKey: string | undef
66
66
  export declare function reconnectWsStream(dataplaneUrl: string, apiKey: string | undefined, commandId: string, options?: {
67
67
  stdoutOffset?: number;
68
68
  stderrOffset?: number;
69
+ headers?: Record<string, string>;
69
70
  }): Promise<[AsyncIterableIterator<WsMessage>, WSStreamControl]>;
70
71
  export {};
@@ -30,11 +30,15 @@ export function buildWsUrl(dataplaneUrl) {
30
30
  /**
31
31
  * Build auth headers for the WebSocket upgrade request.
32
32
  */
33
- export function buildAuthHeaders(apiKey) {
33
+ export function buildAuthHeaders(apiKey, extraHeaders) {
34
+ const headers = {};
34
35
  if (apiKey) {
35
- return { "X-Api-Key": apiKey };
36
+ headers["X-Api-Key"] = apiKey;
36
37
  }
37
- return {};
38
+ if (extraHeaders) {
39
+ Object.assign(headers, extraHeaders);
40
+ }
41
+ return headers;
38
42
  }
39
43
  // =============================================================================
40
44
  // Stream Control
@@ -229,9 +233,9 @@ async function* readWsMessages(ws) {
229
233
  * data arrives in addition to yielding the messages.
230
234
  */
231
235
  export async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
232
- const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, } = options;
236
+ const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, } = options;
233
237
  const wsUrl = buildWsUrl(dataplaneUrl);
234
- const headers = buildAuthHeaders(apiKey);
238
+ const headers = buildAuthHeaders(apiKey, extraHeaders);
235
239
  const control = new WSStreamControl();
236
240
  async function* stream() {
237
241
  let ws;
@@ -299,9 +303,9 @@ export async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
299
303
  * No 'started' message is sent on reconnection.
300
304
  */
301
305
  export async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {}) {
302
- const { stdoutOffset = 0, stderrOffset = 0 } = options;
306
+ const { stdoutOffset = 0, stderrOffset = 0, headers: extraHeaders } = options;
303
307
  const wsUrl = buildWsUrl(dataplaneUrl);
304
- const headers = buildAuthHeaders(apiKey);
308
+ const headers = buildAuthHeaders(apiKey, extraHeaders);
305
309
  const control = new WSStreamControl();
306
310
  async function* stream() {
307
311
  let ws;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isPrimitive = exports.isRecord = void 0;
4
+ const isRecord = (value) => {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ };
7
+ exports.isRecord = isRecord;
8
+ const isPrimitive = (value) => {
9
+ return (typeof value === "string" ||
10
+ typeof value === "number" ||
11
+ typeof value === "boolean");
12
+ };
13
+ exports.isPrimitive = isPrimitive;
@@ -0,0 +1,2 @@
1
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
2
+ export declare const isPrimitive: (value: unknown) => value is string | number | boolean;
@@ -0,0 +1,8 @@
1
+ export const isRecord = (value) => {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ };
4
+ export const isPrimitive = (value) => {
5
+ return (typeof value === "string" ||
6
+ typeof value === "number" ||
7
+ typeof value === "boolean");
8
+ };
@@ -4,6 +4,7 @@ exports.extractOutputTokenDetails = extractOutputTokenDetails;
4
4
  exports.extractInputTokenDetails = extractInputTokenDetails;
5
5
  exports.extractUsageMetadata = extractUsageMetadata;
6
6
  const usage_js_1 = require("./usage.cjs");
7
+ const types_js_1 = require("./types.cjs");
7
8
  function extractTraceableServiceTier(providerMetadata) {
8
9
  if (providerMetadata?.openai != null &&
9
10
  typeof providerMetadata.openai === "object") {
@@ -19,6 +20,14 @@ function extractTraceableServiceTier(providerMetadata) {
19
20
  function isLanguageModelV3Usage(usage) {
20
21
  return usage.inputTokens != null && typeof usage.inputTokens === "object";
21
22
  }
23
+ function isTelemetryLanguageModelUsage(usage) {
24
+ if (!(0, types_js_1.isRecord)(usage))
25
+ return false;
26
+ return ((usage.inputTokens == null || typeof usage.inputTokens === "number") &&
27
+ (0, types_js_1.isRecord)(usage.inputTokenDetails) &&
28
+ (usage.outputTokens == null || typeof usage.outputTokens === "number") &&
29
+ (0, types_js_1.isRecord)(usage.outputTokenDetails));
30
+ }
22
31
  function extractAISDK6OutputTokenDetails(usage, providerMetadata) {
23
32
  const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
24
33
  const outputTokenDetailsKeyPrefix = openAIServiceTier
@@ -42,32 +51,51 @@ function extractAISDK6OutputTokenDetails(usage, providerMetadata) {
42
51
  }
43
52
  return outputTokenDetails;
44
53
  }
54
+ // TODO: verify service tier token counting
55
+ function extractTelemetryOutputTokenDetails(usage, providerMetadata) {
56
+ const result = {};
57
+ const serviceTier = extractTraceableServiceTier(providerMetadata ?? {});
58
+ const prefix = serviceTier ? `${serviceTier}_` : "";
59
+ const reasoning = usage.outputTokenDetails?.reasoningTokens ?? undefined;
60
+ if (typeof reasoning === "number" && reasoning > 0) {
61
+ const key = `${prefix}reasoning`;
62
+ result[key] = reasoning;
63
+ }
64
+ // Apply service tier logic
65
+ if (serviceTier != null) {
66
+ // Avoid counting reasoning tokens towards the output token count
67
+ // since service tier tokens are already priced differently
68
+ result[serviceTier] = (usage.outputTokens ?? 0) - (reasoning ?? 0);
69
+ }
70
+ return result;
71
+ }
45
72
  function extractOutputTokenDetails(usage, providerMetadata) {
46
73
  if (usage == null) {
47
74
  return {};
48
75
  }
76
+ // AI SDK 7: Use Telemetry Language Model Usage
77
+ if (isTelemetryLanguageModelUsage(usage)) {
78
+ return extractTelemetryOutputTokenDetails(usage, providerMetadata);
79
+ }
49
80
  // AI SDK 6: Check for built-in outputTokens breakdown first
50
81
  if (isLanguageModelV3Usage(usage)) {
51
82
  // Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
52
83
  return extractAISDK6OutputTokenDetails(usage, providerMetadata);
53
84
  }
85
+ // AI SDK <= 5
54
86
  const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
55
- const outputTokenDetailsKeyPrefix = openAIServiceTier
56
- ? `${openAIServiceTier}_`
57
- : "";
58
- const outputTokenDetails = {};
87
+ const prefix = openAIServiceTier ? `${openAIServiceTier}_` : "";
88
+ const result = {};
59
89
  if (typeof usage?.reasoningTokens === "number") {
60
- outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] =
61
- usage.reasoningTokens;
90
+ result[`${prefix}reasoning`] = usage.reasoningTokens;
62
91
  }
63
92
  if (openAIServiceTier && typeof usage?.outputTokens === "number") {
64
93
  // Avoid counting reasoning tokens towards the output token count
65
94
  // since service tier tokens are already priced differently
66
- outputTokenDetails[openAIServiceTier] =
67
- usage.outputTokens -
68
- (outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] ?? 0);
95
+ result[openAIServiceTier] =
96
+ usage.outputTokens - (result[`${prefix}reasoning`] ?? 0);
69
97
  }
70
- return outputTokenDetails;
98
+ return result;
71
99
  }
72
100
  function extractAISDK6InputTokenDetails(usage, providerMetadata) {
73
101
  let inputTokenDetails = {};
@@ -116,10 +144,40 @@ function extractAISDK6InputTokenDetails(usage, providerMetadata) {
116
144
  }
117
145
  return inputTokenDetails;
118
146
  }
147
+ function extractTelemetryInputTokenDetails(usage, providerMetadata) {
148
+ const result = {};
149
+ const cacheRead = usage.inputTokenDetails?.cacheReadTokens ?? undefined;
150
+ const cacheWrite = usage.inputTokenDetails?.cacheWriteTokens ?? undefined;
151
+ if (typeof cacheRead === "number" && cacheRead > 0) {
152
+ result.cache_read = cacheRead;
153
+ }
154
+ if (typeof cacheWrite === "number" && cacheWrite > 0) {
155
+ result.cache_creation = cacheWrite;
156
+ }
157
+ const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
158
+ const prefix = openAIServiceTier
159
+ ? `${openAIServiceTier}_`
160
+ : "";
161
+ if (openAIServiceTier && typeof usage.inputTokens === "number") {
162
+ if (result.cache_read != null) {
163
+ result[`${prefix}cache_read`] = result.cache_read;
164
+ delete result.cache_read;
165
+ }
166
+ if (typeof usage?.inputTokens === "number") {
167
+ result[openAIServiceTier] =
168
+ usage.inputTokens - (result[`${prefix}cache_read`] ?? 0);
169
+ }
170
+ }
171
+ return result;
172
+ }
119
173
  function extractInputTokenDetails(usage, providerMetadata) {
120
174
  if (usage == null) {
121
175
  return {};
122
176
  }
177
+ // AI SDK 7: Use Telemetry Language Model Usage
178
+ if (isTelemetryLanguageModelUsage(usage)) {
179
+ return extractTelemetryInputTokenDetails(usage, providerMetadata);
180
+ }
123
181
  // AI SDK 6: Check for built-in inputTokens breakdown first
124
182
  if (isLanguageModelV3Usage(usage)) {
125
183
  // Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
@@ -1,7 +1,22 @@
1
1
  import type { LanguageModelV2Usage, LanguageModelV3Usage } from "@ai-sdk/provider";
2
2
  import { KVMap } from "../schemas.js";
3
- export declare function extractOutputTokenDetails(usage?: Partial<LanguageModelV2Usage> | Partial<LanguageModelV3Usage>, providerMetadata?: Record<string, unknown>): Record<string, number>;
4
- export declare function extractInputTokenDetails(usage?: Partial<LanguageModelV2Usage> | Partial<LanguageModelV3Usage>, providerMetadata?: Record<string, unknown>): Record<string, number>;
3
+ export declare function extractOutputTokenDetails(usage?: Partial<LanguageModelV2Usage> | Partial<LanguageModelV3Usage>, providerMetadata?: Record<string, unknown>): Record<string, number> | {
4
+ reasoning?: number;
5
+ flex?: number;
6
+ flex_reasoning?: number;
7
+ priority?: number;
8
+ priority_reasoning?: number;
9
+ };
10
+ export declare function extractInputTokenDetails(usage?: Partial<LanguageModelV2Usage> | Partial<LanguageModelV3Usage>, providerMetadata?: Record<string, unknown>): Record<string, number> | {
11
+ cache_read?: number;
12
+ cache_creation?: number;
13
+ flex?: number;
14
+ flex_cache_read?: number;
15
+ flex_cache_creation?: number;
16
+ priority?: number;
17
+ priority_cache_read?: number;
18
+ priority_cache_creation?: number;
19
+ };
5
20
  export declare function extractUsageMetadata(span?: {
6
21
  status?: {
7
22
  code: number;
@@ -1,4 +1,5 @@
1
1
  import { convertAnthropicUsageToInputTokenDetails } from "./usage.js";
2
+ import { isRecord } from "./types.js";
2
3
  function extractTraceableServiceTier(providerMetadata) {
3
4
  if (providerMetadata?.openai != null &&
4
5
  typeof providerMetadata.openai === "object") {
@@ -14,6 +15,14 @@ function extractTraceableServiceTier(providerMetadata) {
14
15
  function isLanguageModelV3Usage(usage) {
15
16
  return usage.inputTokens != null && typeof usage.inputTokens === "object";
16
17
  }
18
+ function isTelemetryLanguageModelUsage(usage) {
19
+ if (!isRecord(usage))
20
+ return false;
21
+ return ((usage.inputTokens == null || typeof usage.inputTokens === "number") &&
22
+ isRecord(usage.inputTokenDetails) &&
23
+ (usage.outputTokens == null || typeof usage.outputTokens === "number") &&
24
+ isRecord(usage.outputTokenDetails));
25
+ }
17
26
  function extractAISDK6OutputTokenDetails(usage, providerMetadata) {
18
27
  const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
19
28
  const outputTokenDetailsKeyPrefix = openAIServiceTier
@@ -37,32 +46,51 @@ function extractAISDK6OutputTokenDetails(usage, providerMetadata) {
37
46
  }
38
47
  return outputTokenDetails;
39
48
  }
49
+ // TODO: verify service tier token counting
50
+ function extractTelemetryOutputTokenDetails(usage, providerMetadata) {
51
+ const result = {};
52
+ const serviceTier = extractTraceableServiceTier(providerMetadata ?? {});
53
+ const prefix = serviceTier ? `${serviceTier}_` : "";
54
+ const reasoning = usage.outputTokenDetails?.reasoningTokens ?? undefined;
55
+ if (typeof reasoning === "number" && reasoning > 0) {
56
+ const key = `${prefix}reasoning`;
57
+ result[key] = reasoning;
58
+ }
59
+ // Apply service tier logic
60
+ if (serviceTier != null) {
61
+ // Avoid counting reasoning tokens towards the output token count
62
+ // since service tier tokens are already priced differently
63
+ result[serviceTier] = (usage.outputTokens ?? 0) - (reasoning ?? 0);
64
+ }
65
+ return result;
66
+ }
40
67
  export function extractOutputTokenDetails(usage, providerMetadata) {
41
68
  if (usage == null) {
42
69
  return {};
43
70
  }
71
+ // AI SDK 7: Use Telemetry Language Model Usage
72
+ if (isTelemetryLanguageModelUsage(usage)) {
73
+ return extractTelemetryOutputTokenDetails(usage, providerMetadata);
74
+ }
44
75
  // AI SDK 6: Check for built-in outputTokens breakdown first
45
76
  if (isLanguageModelV3Usage(usage)) {
46
77
  // Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
47
78
  return extractAISDK6OutputTokenDetails(usage, providerMetadata);
48
79
  }
80
+ // AI SDK <= 5
49
81
  const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
50
- const outputTokenDetailsKeyPrefix = openAIServiceTier
51
- ? `${openAIServiceTier}_`
52
- : "";
53
- const outputTokenDetails = {};
82
+ const prefix = openAIServiceTier ? `${openAIServiceTier}_` : "";
83
+ const result = {};
54
84
  if (typeof usage?.reasoningTokens === "number") {
55
- outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] =
56
- usage.reasoningTokens;
85
+ result[`${prefix}reasoning`] = usage.reasoningTokens;
57
86
  }
58
87
  if (openAIServiceTier && typeof usage?.outputTokens === "number") {
59
88
  // Avoid counting reasoning tokens towards the output token count
60
89
  // since service tier tokens are already priced differently
61
- outputTokenDetails[openAIServiceTier] =
62
- usage.outputTokens -
63
- (outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] ?? 0);
90
+ result[openAIServiceTier] =
91
+ usage.outputTokens - (result[`${prefix}reasoning`] ?? 0);
64
92
  }
65
- return outputTokenDetails;
93
+ return result;
66
94
  }
67
95
  function extractAISDK6InputTokenDetails(usage, providerMetadata) {
68
96
  let inputTokenDetails = {};
@@ -111,10 +139,40 @@ function extractAISDK6InputTokenDetails(usage, providerMetadata) {
111
139
  }
112
140
  return inputTokenDetails;
113
141
  }
142
+ function extractTelemetryInputTokenDetails(usage, providerMetadata) {
143
+ const result = {};
144
+ const cacheRead = usage.inputTokenDetails?.cacheReadTokens ?? undefined;
145
+ const cacheWrite = usage.inputTokenDetails?.cacheWriteTokens ?? undefined;
146
+ if (typeof cacheRead === "number" && cacheRead > 0) {
147
+ result.cache_read = cacheRead;
148
+ }
149
+ if (typeof cacheWrite === "number" && cacheWrite > 0) {
150
+ result.cache_creation = cacheWrite;
151
+ }
152
+ const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
153
+ const prefix = openAIServiceTier
154
+ ? `${openAIServiceTier}_`
155
+ : "";
156
+ if (openAIServiceTier && typeof usage.inputTokens === "number") {
157
+ if (result.cache_read != null) {
158
+ result[`${prefix}cache_read`] = result.cache_read;
159
+ delete result.cache_read;
160
+ }
161
+ if (typeof usage?.inputTokens === "number") {
162
+ result[openAIServiceTier] =
163
+ usage.inputTokens - (result[`${prefix}cache_read`] ?? 0);
164
+ }
165
+ }
166
+ return result;
167
+ }
114
168
  export function extractInputTokenDetails(usage, providerMetadata) {
115
169
  if (usage == null) {
116
170
  return {};
117
171
  }
172
+ // AI SDK 7: Use Telemetry Language Model Usage
173
+ if (isTelemetryLanguageModelUsage(usage)) {
174
+ return extractTelemetryInputTokenDetails(usage, providerMetadata);
175
+ }
118
176
  // AI SDK 6: Check for built-in inputTokens breakdown first
119
177
  if (isLanguageModelV3Usage(usage)) {
120
178
  // Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
@@ -0,0 +1 @@
1
+ module.exports = require('../dist/sandbox/index.cjs');
@@ -0,0 +1 @@
1
+ export * from '../dist/sandbox/index.js'
@@ -0,0 +1 @@
1
+ export * from '../dist/sandbox/index.js'
@@ -0,0 +1 @@
1
+ export * from '../dist/sandbox/index.js'