experimental-a2 0.12.0 → 0.13.0

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/src/ai.ts CHANGED
@@ -64,6 +64,7 @@ export type GenerationStartedPayload = {
64
64
  responseMessageId: string
65
65
  attempt: number
66
66
  model: string
67
+ promptThroughIndex?: number
67
68
  }
68
69
  export type GenerationProgressPayload = {
69
70
  requestId: string
@@ -80,6 +81,7 @@ export type GenerationCompletedPayload = {
80
81
  responseMessageId: string
81
82
  finishReason?: FinishReason
82
83
  usage?: LanguageModelUsage
84
+ inputTokenEstimate?: number
83
85
  }
84
86
  export type GenerationFailedPayload = {
85
87
  requestId: string
@@ -152,15 +154,33 @@ export type InputRespondedPayload = {
152
154
  name: string
153
155
  value: JSONValue
154
156
  }
157
+ export type ModelLimits = {
158
+ contextWindow: number
159
+ maxOutputTokens: number
160
+ }
161
+ export type ModelMetadataRequestedPayload = { modelId: string }
162
+ export type ModelMetadataResolvedPayload = {
163
+ modelId: string
164
+ limits: ModelLimits | null
165
+ }
166
+ export type ModelMetadataState =
167
+ | { status: 'pending' }
168
+ | { status: 'resolved'; limits: ModelLimits }
169
+ | { status: 'unavailable' }
170
+
155
171
  export type CompactionRequestedPayload = {
156
172
  generationId: string
157
173
  throughMessageId: string
174
+ throughIndex?: number
158
175
  }
159
176
  export type CompactionCompletedPayload<M extends UIMessage> = {
160
177
  generationId: string
161
178
  throughMessageId: string
179
+ throughIndex?: number
162
180
  messages: M[]
163
181
  retainedMessageIds?: string[]
182
+ summary?: string
183
+ usage?: LanguageModelUsage
164
184
  }
165
185
 
166
186
  export type AIEventDefs<M extends UIMessage = UIMessage> = {
@@ -183,6 +203,8 @@ export type AIEventDefs<M extends UIMessage = UIMessage> = {
183
203
  'ai.input.responded': StandardSchemaV1<InputRespondedPayload>
184
204
  'ai.compaction.requested': StandardSchemaV1<CompactionRequestedPayload>
185
205
  'ai.compaction.completed': StandardSchemaV1<CompactionCompletedPayload<M>>
206
+ 'ai.model.metadata.requested': StandardSchemaV1<ModelMetadataRequestedPayload>
207
+ 'ai.model.metadata.resolved': StandardSchemaV1<ModelMetadataResolvedPayload>
186
208
  }
187
209
 
188
210
  export type ActiveGeneration = GenerationStartedPayload
@@ -242,8 +264,11 @@ export type CompactionState<M extends UIMessage> = {
242
264
  status: 'running' | 'completed'
243
265
  generationId: string
244
266
  throughMessageId: string
267
+ throughIndex?: number
245
268
  messages?: M[]
246
269
  retainedMessageIds?: string[]
270
+ summary?: string
271
+ usage?: LanguageModelUsage
247
272
  }
248
273
 
249
274
  export type GenerationUsage = {
@@ -269,6 +294,7 @@ export type AIState<M extends UIMessage = UIMessage> = {
269
294
  pendingInputs: PendingInput[]
270
295
  tools: ToolActivity[]
271
296
  compaction: CompactionState<M> | null
297
+ modelMetadata: Record<string, ModelMetadataState>
272
298
  usage: GenerationUsage[]
273
299
  error: string | null
274
300
  }
@@ -373,6 +399,15 @@ const jsonObject = <T extends object>(
373
399
  const stringField = (value: Record<string, unknown>, key: string): boolean =>
374
400
  typeof value[key] === 'string' && value[key].length > 0
375
401
 
402
+ const optionalIndexField = (
403
+ value: Record<string, unknown>,
404
+ key: string,
405
+ ): boolean =>
406
+ value[key] === undefined ||
407
+ (typeof value[key] === 'number' &&
408
+ Number.isSafeInteger(value[key]) &&
409
+ value[key] >= 0)
410
+
376
411
  const optionalStringField = (
377
412
  value: Record<string, unknown>,
378
413
  key: string,
@@ -449,6 +484,7 @@ const completedPayloadSchema: StandardSchemaV1<GenerationCompletedPayload> =
449
484
  !stringField(value, 'messageId') ||
450
485
  !stringField(value, 'generationId') ||
451
486
  !stringField(value, 'responseMessageId') ||
487
+ !optionalIndexField(value, 'inputTokenEstimate') ||
452
488
  !optionalStringField(value, 'finishReason')
453
489
  ) {
454
490
  return issue('invalid ai.generation.completed payload')
@@ -465,6 +501,8 @@ const compactionPayloadSchema = <M extends UIMessage>(
465
501
  !isJSONCompatible(value) ||
466
502
  !stringField(value, 'generationId') ||
467
503
  !stringField(value, 'throughMessageId') ||
504
+ !optionalIndexField(value, 'throughIndex') ||
505
+ !optionalStringField(value, 'summary') ||
468
506
  !Array.isArray(value['messages']) ||
469
507
  (value['retainedMessageIds'] !== undefined &&
470
508
  (!Array.isArray(value['retainedMessageIds']) ||
@@ -484,7 +522,16 @@ const compactionPayloadSchema = <M extends UIMessage>(
484
522
  value: {
485
523
  generationId: value['generationId'] as string,
486
524
  throughMessageId: value['throughMessageId'] as string,
525
+ ...(value['throughIndex'] === undefined
526
+ ? {}
527
+ : { throughIndex: value['throughIndex'] as number }),
487
528
  messages,
529
+ ...(value['summary'] === undefined
530
+ ? {}
531
+ : { summary: value['summary'] as string }),
532
+ ...(value['usage'] === undefined
533
+ ? {}
534
+ : { usage: value['usage'] as LanguageModelUsage }),
488
535
  ...(value['retainedMessageIds'] === undefined
489
536
  ? {}
490
537
  : { retainedMessageIds: value['retainedMessageIds'] as string[] }),
@@ -547,7 +594,8 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
547
594
  typeof value['attempt'] === 'number' &&
548
595
  Number.isInteger(value['attempt']) &&
549
596
  value['attempt'] > 0 &&
550
- stringField(value, 'model'),
597
+ stringField(value, 'model') &&
598
+ optionalIndexField(value, 'promptThroughIndex'),
551
599
  ),
552
600
  'ai.generation.progress': progressPayloadSchema,
553
601
  'ai.generation.completed': completedPayloadSchema,
@@ -641,9 +689,27 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
641
689
  'ai.compaction.requested',
642
690
  (value) =>
643
691
  stringField(value, 'generationId') &&
644
- stringField(value, 'throughMessageId'),
692
+ stringField(value, 'throughMessageId') &&
693
+ optionalIndexField(value, 'throughIndex'),
645
694
  ),
646
695
  'ai.compaction.completed': compactionPayloadSchema(messageSchema),
696
+ 'ai.model.metadata.requested': jsonObject(
697
+ 'ai.model.metadata.requested',
698
+ (value) => stringField(value, 'modelId'),
699
+ ),
700
+ 'ai.model.metadata.resolved': jsonObject(
701
+ 'ai.model.metadata.resolved',
702
+ (value) =>
703
+ stringField(value, 'modelId') &&
704
+ (value['limits'] === null ||
705
+ (isRecord(value['limits']) &&
706
+ typeof value['limits']['contextWindow'] === 'number' &&
707
+ Number.isSafeInteger(value['limits']['contextWindow']) &&
708
+ value['limits']['contextWindow'] > 0 &&
709
+ typeof value['limits']['maxOutputTokens'] === 'number' &&
710
+ Number.isSafeInteger(value['limits']['maxOutputTokens']) &&
711
+ value['limits']['maxOutputTokens'] > 0)),
712
+ ),
647
713
  }
648
714
  return definition
649
715
  }
@@ -663,6 +729,7 @@ const initialState = <M extends UIMessage>(): AIState<M> => ({
663
729
  pendingInputs: [],
664
730
  tools: [],
665
731
  compaction: null,
732
+ modelMetadata: {},
666
733
  usage: [],
667
734
  error: null,
668
735
  })
@@ -1084,6 +1151,30 @@ export function reduceAIState<M extends UIMessage>(
1084
1151
  ): AIState<M> {
1085
1152
  if (state.status === 'closed') return state
1086
1153
  switch (event.type) {
1154
+ case 'ai.model.metadata.requested': {
1155
+ const { modelId } = event.payload as ModelMetadataRequestedPayload
1156
+ if (Object.hasOwn(state.modelMetadata, modelId)) return state
1157
+ return {
1158
+ ...state,
1159
+ modelMetadata: {
1160
+ ...state.modelMetadata,
1161
+ [modelId]: { status: 'pending' },
1162
+ },
1163
+ }
1164
+ }
1165
+ case 'ai.model.metadata.resolved': {
1166
+ const { modelId, limits } = event.payload as ModelMetadataResolvedPayload
1167
+ return {
1168
+ ...state,
1169
+ modelMetadata: {
1170
+ ...state.modelMetadata,
1171
+ [modelId]:
1172
+ limits === null
1173
+ ? { status: 'unavailable' }
1174
+ : { status: 'resolved', limits },
1175
+ },
1176
+ }
1177
+ }
1087
1178
  case 'ai.session.created': {
1088
1179
  const payload = event.payload as SessionCreatedPayload
1089
1180
  return {
@@ -1102,6 +1193,10 @@ export function reduceAIState<M extends UIMessage>(
1102
1193
  return {
1103
1194
  ...state,
1104
1195
  status: 'closed',
1196
+ compaction:
1197
+ state.compaction?.status === 'running'
1198
+ ? (state.activeProjection?.baseCompaction ?? null)
1199
+ : state.compaction,
1105
1200
  activeGeneration: null,
1106
1201
  activeRequestId: null,
1107
1202
  activeResponseMessageId: null,
@@ -1968,7 +2063,7 @@ export function createReducer<D extends EventDefs>(options: {
1968
2063
  type M = AIMessageFrom<D>
1969
2064
  return options.contract
1970
2065
  .reducer({
1971
- name: options.name ?? 'a2.ai.state.v8',
2066
+ name: options.name ?? 'a2.ai.state.v10',
1972
2067
  initialState: initialState<M>(),
1973
2068
  })
1974
2069
  .fold((state, event) => reduceAIState(state, event))
@@ -2134,7 +2229,7 @@ export function agent<
2134
2229
  })
2135
2230
  const reducer = agentContract
2136
2231
  .reducer({
2137
- name: options.reducerName ?? 'a2.ai.state.v8',
2232
+ name: options.reducerName ?? 'a2.ai.state.v10',
2138
2233
  initialState: initialState<M>(),
2139
2234
  })
2140
2235
  .fold((state, event) => reduceAIState(state, event))