@ai-sdk/workflow 1.0.0-beta.8 → 1.0.0-beta.94

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.
@@ -1,12 +1,12 @@
1
1
  import {
2
+ parseJsonEventStream,
3
+ uiMessageChunkSchema,
2
4
  type ChatRequestOptions,
3
5
  type ChatTransport,
4
6
  type PrepareReconnectToStreamRequest,
5
7
  type PrepareSendMessagesRequest,
6
- parseJsonEventStream,
7
8
  type UIMessage,
8
9
  type UIMessageChunk,
9
- uiMessageChunkSchema,
10
10
  } from 'ai';
11
11
  import {
12
12
  convertAsyncIteratorToReadableStream,
@@ -216,7 +216,7 @@ export class WorkflowChatTransport<
216
216
  : undefined;
217
217
 
218
218
  const url = requestConfig?.api ?? this.api;
219
- const res = await this.fetch(url, {
219
+ const response = await this.fetch(url, {
220
220
  method: 'POST',
221
221
  body: JSON.stringify(
222
222
  requestConfig?.body ?? { messages, ...options.body },
@@ -226,13 +226,13 @@ export class WorkflowChatTransport<
226
226
  signal: abortSignal,
227
227
  });
228
228
 
229
- if (!res.ok || !res.body) {
229
+ if (!response.ok || !response.body) {
230
230
  throw new Error(
231
- `Failed to fetch chat: ${res.status} ${await res.text()}`,
231
+ `Failed to fetch chat: ${response.status} ${await response.text()}`,
232
232
  );
233
233
  }
234
234
 
235
- const workflowRunId = res.headers.get('x-workflow-run-id');
235
+ const workflowRunId = response.headers.get('x-workflow-run-id');
236
236
  if (!workflowRunId) {
237
237
  throw new Error(
238
238
  'Workflow run ID not found in "x-workflow-run-id" response header',
@@ -242,12 +242,12 @@ export class WorkflowChatTransport<
242
242
  // Notify the caller that the chat POST request was sent.
243
243
  // This is useful for tracking the chat history on the client
244
244
  // side and allows for inspecting response headers.
245
- await this.onChatSendMessage?.(res, options);
245
+ await this.onChatSendMessage?.(response, options);
246
246
 
247
247
  // Flush the initial stream until the end or an error occurs
248
248
  try {
249
249
  const chunkStream = parseJsonEventStream({
250
- stream: res.body,
250
+ stream: response.body,
251
251
  schema: uiMessageChunkSchema,
252
252
  });
253
253
  for await (const chunk of createAsyncIterableStream(chunkStream)) {
@@ -292,8 +292,8 @@ export class WorkflowChatTransport<
292
292
  async reconnectToStream(
293
293
  options: ReconnectToStreamOptions & ChatRequestOptions,
294
294
  ): Promise<ReadableStream<UIMessageChunk> | null> {
295
- const it = this.reconnectToStreamIterator(options);
296
- return convertAsyncIteratorToReadableStream(it);
295
+ const reconnectIterator = this.reconnectToStreamIterator(options);
296
+ return convertAsyncIteratorToReadableStream(reconnectIterator);
297
297
  }
298
298
 
299
299
  private async *reconnectToStreamIterator(
@@ -344,15 +344,15 @@ export class WorkflowChatTransport<
344
344
  : chunkIndex;
345
345
 
346
346
  const url = `${baseUrl}?startIndex=${startIndex}`;
347
- const res = await this.fetch(url, {
347
+ const response = await this.fetch(url, {
348
348
  headers: requestConfig?.headers,
349
349
  credentials: requestConfig?.credentials,
350
350
  signal: options.abortSignal,
351
351
  });
352
352
 
353
- if (!res.ok || !res.body) {
353
+ if (!response.ok || !response.body) {
354
354
  throw new Error(
355
- `Failed to fetch chat: ${res.status} ${await res.text()}`,
355
+ `Failed to fetch chat: ${response.status} ${await response.text()}`,
356
356
  );
357
357
  }
358
358
 
@@ -365,7 +365,9 @@ export class WorkflowChatTransport<
365
365
  // resume from (explicitStartIndex + chunks received).
366
366
  chunkIndex = explicitStartIndex;
367
367
  } else if (useExplicitStartIndex && explicitStartIndex < 0) {
368
- const tailIndexHeader = res.headers.get('x-workflow-stream-tail-index');
368
+ const tailIndexHeader = response.headers.get(
369
+ 'x-workflow-stream-tail-index',
370
+ );
369
371
  const tailIndex =
370
372
  tailIndexHeader !== null ? parseInt(tailIndexHeader, 10) : NaN;
371
373
 
@@ -389,7 +391,7 @@ export class WorkflowChatTransport<
389
391
 
390
392
  try {
391
393
  const chunkStream = parseJsonEventStream({
392
- stream: res.body,
394
+ stream: response.body,
393
395
  schema: uiMessageChunkSchema,
394
396
  });
395
397
  for await (const chunk of createAsyncIterableStream(chunkStream)) {
package/src/telemetry.ts DELETED
@@ -1,199 +0,0 @@
1
- import type { TelemetrySettings } from './workflow-agent.js';
2
-
3
- // Minimal OTel type shims so we don't depend on @opentelemetry/api at compile time.
4
- type Attributes = Record<string, unknown>;
5
-
6
- type Span = {
7
- setAttributes(attributes: Attributes): void;
8
- setStatus(status: { code: number; message?: string }): void;
9
- recordException(exception: {
10
- name: string;
11
- message: string;
12
- stack?: string;
13
- }): void;
14
- end(): void;
15
- };
16
-
17
- type Context = unknown;
18
-
19
- type Tracer = {
20
- startActiveSpan<T>(
21
- name: string,
22
- options: Attributes,
23
- fn: (span: Span) => T,
24
- ): T;
25
- };
26
-
27
- // Full OTel API surface we use
28
- interface OtelApi {
29
- trace: {
30
- getTracer(name: string): Tracer;
31
- setSpan(context: Context, span: Span): Context;
32
- };
33
- context: {
34
- active(): Context;
35
- with<T>(ctx: Context, fn: () => T): T;
36
- };
37
- SpanStatusCode: { ERROR: number };
38
- }
39
-
40
- // Lazy-loaded OTel API — self-initializes on first use (item 5)
41
- let otelApi: OtelApi | null = null;
42
- let otelLoadAttempted = false;
43
-
44
- async function ensureOtelApi(): Promise<OtelApi | null> {
45
- if (otelLoadAttempted) return otelApi;
46
- otelLoadAttempted = true;
47
- try {
48
- // Dynamic import — @opentelemetry/api is an optional peer dependency.
49
- // Use Function() to hide the import from bundlers that would fail at
50
- // compile time when the package is absent.
51
- otelApi = await (Function(
52
- 'return import("@opentelemetry/api")',
53
- )() as Promise<OtelApi>);
54
- } catch {
55
- otelApi = null;
56
- }
57
- return otelApi;
58
- }
59
-
60
- /**
61
- * Stateless tracer accessor matching AI SDK's `getTracer` pattern (item 5).
62
- * Returns a no-op–equivalent `null` when telemetry is disabled, so callers
63
- * don't need a separate init step.
64
- */
65
- function getTracer(telemetry?: TelemetrySettings): Tracer | null {
66
- if (!telemetry?.isEnabled || !otelApi) return null;
67
- if (telemetry.tracer) return telemetry.tracer as Tracer;
68
- return otelApi.trace.getTracer('ai');
69
- }
70
-
71
- // ── Attribute helpers ──────────────────────────────────────────────────
72
-
73
- /**
74
- * Assemble `operation.name` / `resource.name` following the AI SDK convention
75
- * (items 1 + 2): separator is a **space**, not a dot.
76
- */
77
- function assembleOperationName(
78
- operationId: string,
79
- telemetry?: TelemetrySettings,
80
- ): Attributes {
81
- return {
82
- 'operation.name': `${operationId}${
83
- telemetry?.functionId != null ? ` ${telemetry.functionId}` : ''
84
- }`,
85
- 'resource.name': telemetry?.functionId,
86
- 'ai.operationId': operationId,
87
- 'ai.telemetry.functionId': telemetry?.functionId,
88
- };
89
- }
90
-
91
- /**
92
- * Build the full attribute bag for a span, merging operation name,
93
- * caller-supplied attributes, and user-defined telemetry metadata.
94
- */
95
- function buildAttributes(
96
- operationId: string,
97
- telemetry: TelemetrySettings | undefined,
98
- extra?: Attributes,
99
- ): Attributes {
100
- if (!telemetry?.isEnabled) return {};
101
-
102
- const attrs: Attributes = {
103
- ...assembleOperationName(operationId, telemetry),
104
- ...extra,
105
- };
106
-
107
- if (telemetry.metadata) {
108
- for (const [key, value] of Object.entries(telemetry.metadata)) {
109
- if (value != null) {
110
- attrs[`ai.telemetry.metadata.${key}`] = value;
111
- }
112
- }
113
- }
114
-
115
- return attrs;
116
- }
117
-
118
- // ── Error recording (item 3) ───────────────────────────────────────────
119
-
120
- /**
121
- * Record an error on a span following the AI SDK pattern:
122
- * `recordException` (with name / message / stack) + `setStatus`.
123
- */
124
- function recordErrorOnSpan(span: Span, error: unknown): void {
125
- if (error instanceof Error) {
126
- span.recordException({
127
- name: error.name,
128
- message: error.message,
129
- stack: error.stack,
130
- });
131
- span.setStatus({
132
- code: otelApi?.SpanStatusCode.ERROR ?? 2,
133
- message: error.message,
134
- });
135
- } else {
136
- span.setStatus({ code: otelApi?.SpanStatusCode.ERROR ?? 2 });
137
- }
138
- }
139
-
140
- // ── Public API ─────────────────────────────────────────────────────────
141
-
142
- /**
143
- * Record a span around an async function.
144
- *
145
- * Self-initialising: the first call lazily loads `@opentelemetry/api`.
146
- * If telemetry is disabled or OTel is unavailable the `fn` runs without
147
- * instrumentation (no-op fast path).
148
- *
149
- * Matches the AI SDK's `recordSpan`:
150
- * - Uses `context.with()` for proper context propagation (item 4)
151
- * - Calls `recordException` + `setStatus` on errors (item 3)
152
- * - Uses space separator in `operation.name` (item 1)
153
- * - Sets `resource.name` (item 2)
154
- */
155
- export async function recordSpan<T>(options: {
156
- name: string;
157
- telemetry?: TelemetrySettings;
158
- attributes?: Attributes;
159
- fn: (span?: Span) => PromiseLike<T> | T;
160
- }): Promise<T> {
161
- // Self-initialise on first call (item 5)
162
- if (!otelLoadAttempted) {
163
- await ensureOtelApi();
164
- }
165
-
166
- const tracer = getTracer(options.telemetry);
167
- if (!tracer || !otelApi) {
168
- return options.fn(undefined);
169
- }
170
-
171
- const attrs = buildAttributes(
172
- options.name,
173
- options.telemetry,
174
- options.attributes,
175
- );
176
-
177
- return tracer.startActiveSpan(
178
- options.name,
179
- { attributes: attrs },
180
- async span => {
181
- // Capture current context so nested spans parent correctly (item 4).
182
- // otelApi is guaranteed non-null here (checked before startActiveSpan).
183
- const ctx = otelApi!.context.active();
184
-
185
- try {
186
- const result = await otelApi!.context.with(ctx, () => options.fn(span));
187
- span.end();
188
- return result;
189
- } catch (error) {
190
- try {
191
- recordErrorOnSpan(span, error);
192
- } finally {
193
- span.end();
194
- }
195
- throw error;
196
- }
197
- },
198
- );
199
- }