@tanstack/openai-base 0.9.11 → 0.9.13

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.
@@ -217,9 +217,13 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
217
217
  // from strict mode is undone by the engine, not here.
218
218
  const transformed = this.transformStructuredOutput(parsed)
219
219
 
220
+ // Surface usage so non-stream structured paths (and
221
+ // fallbackStructuredOutputStream) can forward tokens to middleware.
222
+ const usage = buildChatCompletionsUsage(response.usage)
220
223
  return {
221
224
  data: transformed,
222
225
  rawText,
226
+ ...(usage && { usage }),
223
227
  }
224
228
  } catch (error: unknown) {
225
229
  // Narrow before logging: raw SDK errors can carry request metadata
@@ -252,12 +256,10 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
252
256
  outputSchema.required,
253
257
  )
254
258
 
255
- const timestamp = Date.now()
256
259
  const aguiState = {
257
260
  runId: generateId(this.name),
258
261
  threadId: chatOptions.threadId ?? generateId(this.name),
259
262
  messageId: generateId(this.name),
260
- timestamp,
261
263
  hasEmittedRunStarted: false,
262
264
  }
263
265
 
@@ -281,13 +283,13 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
281
283
  type: EventType.REASONING_MESSAGE_END,
282
284
  messageId: reasoningMessageId,
283
285
  model: lastModel || chatOptions.model,
284
- timestamp,
286
+ timestamp: Date.now(),
285
287
  }
286
288
  yield {
287
289
  type: EventType.REASONING_END,
288
290
  messageId: reasoningMessageId,
289
291
  model: lastModel || chatOptions.model,
290
- timestamp,
292
+ timestamp: Date.now(),
291
293
  }
292
294
  if (stepId) {
293
295
  yield {
@@ -295,7 +297,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
295
297
  stepName: stepId,
296
298
  stepId,
297
299
  model: lastModel || chatOptions.model,
298
- timestamp,
300
+ timestamp: Date.now(),
299
301
  content: accumulatedReasoning,
300
302
  }
301
303
  }
@@ -360,7 +362,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
360
362
  runId: aguiState.runId,
361
363
  threadId: aguiState.threadId,
362
364
  model: chunk.model || chatOptions.model,
363
- timestamp,
365
+ timestamp: Date.now(),
364
366
  parentRunId: chatOptions.parentRunId,
365
367
  }
366
368
  }
@@ -375,21 +377,21 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
375
377
  type: EventType.REASONING_START,
376
378
  messageId: reasoningMessageId,
377
379
  model: chunk.model || chatOptions.model,
378
- timestamp,
380
+ timestamp: Date.now(),
379
381
  }
380
382
  yield {
381
383
  type: EventType.REASONING_MESSAGE_START,
382
384
  messageId: reasoningMessageId,
383
385
  role: 'reasoning' as const,
384
386
  model: chunk.model || chatOptions.model,
385
- timestamp,
387
+ timestamp: Date.now(),
386
388
  }
387
389
  yield {
388
390
  type: EventType.STEP_STARTED,
389
391
  stepName: stepId,
390
392
  stepId,
391
393
  model: chunk.model || chatOptions.model,
392
- timestamp,
394
+ timestamp: Date.now(),
393
395
  stepType: 'thinking',
394
396
  }
395
397
  }
@@ -399,7 +401,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
399
401
  messageId: reasoningMessageId,
400
402
  delta: reasoning.text,
401
403
  model: chunk.model || chatOptions.model,
402
- timestamp,
404
+ timestamp: Date.now(),
403
405
  }
404
406
  }
405
407
 
@@ -416,7 +418,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
416
418
  type: EventType.TEXT_MESSAGE_START,
417
419
  messageId: aguiState.messageId,
418
420
  model: chunk.model || chatOptions.model,
419
- timestamp,
421
+ timestamp: Date.now(),
420
422
  role: 'assistant',
421
423
  }
422
424
  }
@@ -427,7 +429,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
427
429
  type: EventType.TEXT_MESSAGE_CONTENT,
428
430
  messageId: aguiState.messageId,
429
431
  model: chunk.model || chatOptions.model,
430
- timestamp,
432
+ timestamp: Date.now(),
431
433
  delta: deltaContent,
432
434
  content: accumulatedContent,
433
435
  }
@@ -444,7 +446,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
444
446
  type: EventType.TEXT_MESSAGE_END,
445
447
  messageId: aguiState.messageId,
446
448
  model: lastModel || chatOptions.model,
447
- timestamp,
449
+ timestamp: Date.now(),
448
450
  }
449
451
  }
450
452
 
@@ -453,7 +455,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
453
455
  type: EventType.RUN_ERROR,
454
456
  runId: aguiState.runId,
455
457
  model: lastModel || chatOptions.model,
456
- timestamp,
458
+ timestamp: Date.now(),
457
459
  message: `${this.name}.structuredOutputStream: response contained no content`,
458
460
  code: 'empty-response',
459
461
  error: {
@@ -472,7 +474,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
472
474
  type: EventType.RUN_ERROR,
473
475
  runId: aguiState.runId,
474
476
  model: lastModel || chatOptions.model,
475
- timestamp,
477
+ timestamp: Date.now(),
476
478
  message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? '...' : ''}`,
477
479
  code: 'parse-error',
478
480
  error: {
@@ -494,7 +496,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
494
496
  ...(accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}),
495
497
  },
496
498
  model: lastModel || chatOptions.model,
497
- timestamp,
499
+ timestamp: Date.now(),
498
500
  }
499
501
 
500
502
  yield {
@@ -502,7 +504,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
502
504
  runId: aguiState.runId,
503
505
  threadId: aguiState.threadId,
504
506
  model: lastModel || chatOptions.model,
505
- timestamp,
507
+ timestamp: Date.now(),
506
508
  finishReason: 'stop',
507
509
  ...(lastUsage && {
508
510
  usage: buildChatCompletionsUsage(lastUsage),
@@ -516,7 +518,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
516
518
  runId: aguiState.runId,
517
519
  threadId: aguiState.threadId,
518
520
  model: chatOptions.model,
519
- timestamp,
521
+ timestamp: Date.now(),
520
522
  parentRunId: chatOptions.parentRunId,
521
523
  }
522
524
  }
@@ -536,7 +538,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
536
538
  type: EventType.RUN_ERROR,
537
539
  runId: aguiState.runId,
538
540
  model: lastModel || chatOptions.model,
539
- timestamp,
541
+ timestamp: Date.now(),
540
542
  message: errorPayload.message,
541
543
  ...(resolvedCode !== undefined && { code: resolvedCode }),
542
544
  ...(rawEvent !== undefined && { rawEvent }),
@@ -260,9 +260,13 @@ export abstract class OpenAIBaseResponsesTextAdapter<
260
260
  // from strict mode is undone by the engine, not here.
261
261
  const transformed = this.transformStructuredOutput(parsed)
262
262
 
263
+ // Surface usage so non-stream structured paths (and
264
+ // fallbackStructuredOutputStream) can forward tokens to middleware.
265
+ const usage = buildResponsesUsage(response.usage)
263
266
  return {
264
267
  data: transformed,
265
268
  rawText,
269
+ ...(usage && { usage }),
266
270
  }
267
271
  } catch (error: unknown) {
268
272
  // Narrow before logging: raw SDK errors can carry request metadata
@@ -299,12 +303,10 @@ export abstract class OpenAIBaseResponsesTextAdapter<
299
303
  outputSchema.required,
300
304
  )
301
305
 
302
- const timestamp = Date.now()
303
306
  const aguiState = {
304
307
  runId: generateId(this.name),
305
308
  threadId: chatOptions.threadId ?? generateId(this.name),
306
309
  messageId: generateId(this.name),
307
- timestamp,
308
310
  hasEmittedRunStarted: false,
309
311
  }
310
312
 
@@ -326,13 +328,13 @@ export abstract class OpenAIBaseResponsesTextAdapter<
326
328
  type: EventType.REASONING_MESSAGE_END,
327
329
  messageId: reasoningMessageId,
328
330
  model,
329
- timestamp,
331
+ timestamp: Date.now(),
330
332
  }
331
333
  yield {
332
334
  type: EventType.REASONING_END,
333
335
  messageId: reasoningMessageId,
334
336
  model,
335
- timestamp,
337
+ timestamp: Date.now(),
336
338
  }
337
339
  if (stepId) {
338
340
  yield {
@@ -340,7 +342,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
340
342
  stepName: stepId,
341
343
  stepId,
342
344
  model,
343
- timestamp,
345
+ timestamp: Date.now(),
344
346
  content: accumulatedReasoning,
345
347
  }
346
348
  }
@@ -357,21 +359,21 @@ export abstract class OpenAIBaseResponsesTextAdapter<
357
359
  type: EventType.REASONING_START,
358
360
  messageId: reasoningMessageId,
359
361
  model,
360
- timestamp,
362
+ timestamp: Date.now(),
361
363
  }
362
364
  yield {
363
365
  type: EventType.REASONING_MESSAGE_START,
364
366
  messageId: reasoningMessageId,
365
367
  role: 'reasoning' as const,
366
368
  model,
367
- timestamp,
369
+ timestamp: Date.now(),
368
370
  }
369
371
  yield {
370
372
  type: EventType.STEP_STARTED,
371
373
  stepName: stepId,
372
374
  stepId,
373
375
  model,
374
- timestamp,
376
+ timestamp: Date.now(),
375
377
  stepType: 'thinking',
376
378
  }
377
379
  }.bind(this)
@@ -414,7 +416,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
414
416
  runId: aguiState.runId,
415
417
  threadId: aguiState.threadId,
416
418
  model,
417
- timestamp,
419
+ timestamp: Date.now(),
418
420
  parentRunId: chatOptions.parentRunId,
419
421
  }
420
422
  }
@@ -438,7 +440,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
438
440
  type: EventType.RUN_ERROR,
439
441
  runId: aguiState.runId,
440
442
  model,
441
- timestamp,
443
+ timestamp: Date.now(),
442
444
  message: `Model refused: ${delta}`,
443
445
  code: 'refusal',
444
446
  error: { message: `Model refused: ${delta}`, code: 'refusal' },
@@ -467,7 +469,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
467
469
  messageId: reasoningMessageId,
468
470
  delta: reasoningDelta,
469
471
  model,
470
- timestamp,
472
+ timestamp: Date.now(),
471
473
  }
472
474
  continue
473
475
  }
@@ -489,7 +491,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
489
491
  type: EventType.TEXT_MESSAGE_START,
490
492
  messageId: aguiState.messageId,
491
493
  model,
492
- timestamp,
494
+ timestamp: Date.now(),
493
495
  role: 'assistant',
494
496
  }
495
497
  }
@@ -498,7 +500,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
498
500
  type: EventType.TEXT_MESSAGE_CONTENT,
499
501
  messageId: aguiState.messageId,
500
502
  model,
501
- timestamp,
503
+ timestamp: Date.now(),
502
504
  delta: textDelta,
503
505
  content: accumulatedContent,
504
506
  }
@@ -527,7 +529,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
527
529
  type: EventType.RUN_ERROR,
528
530
  runId: aguiState.runId,
529
531
  model,
530
- timestamp,
532
+ timestamp: Date.now(),
531
533
  message,
532
534
  ...(code !== undefined && { code }),
533
535
  error: { message, ...(code !== undefined && { code }) },
@@ -543,7 +545,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
543
545
  type: EventType.TEXT_MESSAGE_END,
544
546
  messageId: aguiState.messageId,
545
547
  model,
546
- timestamp,
548
+ timestamp: Date.now(),
547
549
  }
548
550
  }
549
551
 
@@ -552,7 +554,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
552
554
  type: EventType.RUN_ERROR,
553
555
  runId: aguiState.runId,
554
556
  model,
555
- timestamp,
557
+ timestamp: Date.now(),
556
558
  message: `${this.name}.structuredOutputStream: response contained no content`,
557
559
  code: 'empty-response',
558
560
  error: {
@@ -571,7 +573,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
571
573
  type: EventType.RUN_ERROR,
572
574
  runId: aguiState.runId,
573
575
  model,
574
- timestamp,
576
+ timestamp: Date.now(),
575
577
  message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? '...' : ''}`,
576
578
  code: 'parse-error',
577
579
  error: {
@@ -596,7 +598,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
596
598
  ...(accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}),
597
599
  },
598
600
  model,
599
- timestamp,
601
+ timestamp: Date.now(),
600
602
  }
601
603
 
602
604
  yield {
@@ -604,7 +606,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
604
606
  runId: aguiState.runId,
605
607
  threadId: aguiState.threadId,
606
608
  model,
607
- timestamp,
609
+ timestamp: Date.now(),
608
610
  finishReason: 'stop',
609
611
  ...(usage && {
610
612
  usage: buildResponsesUsage(usage),
@@ -618,7 +620,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
618
620
  runId: aguiState.runId,
619
621
  threadId: aguiState.threadId,
620
622
  model,
621
- timestamp,
623
+ timestamp: Date.now(),
622
624
  parentRunId: chatOptions.parentRunId,
623
625
  }
624
626
  }
@@ -637,7 +639,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
637
639
  type: EventType.RUN_ERROR,
638
640
  runId: aguiState.runId,
639
641
  model,
640
- timestamp,
642
+ timestamp: Date.now(),
641
643
  message: errorPayload.message,
642
644
  ...(resolvedCode !== undefined && { code: resolvedCode }),
643
645
  ...(rawEvent !== undefined && { rawEvent }),