@tanstack/ai 0.32.0 → 0.33.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.
Files changed (60) hide show
  1. package/dist/esm/activities/chat/index.js +24 -3
  2. package/dist/esm/activities/chat/index.js.map +1 -1
  3. package/dist/esm/activities/chat/middleware/types.d.ts +7 -0
  4. package/dist/esm/activities/chat/tools/lazy-tool-manager.d.ts +25 -1
  5. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +26 -2
  6. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
  7. package/dist/esm/activities/generateAudio/index.d.ts +7 -0
  8. package/dist/esm/activities/generateAudio/index.js +26 -1
  9. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  10. package/dist/esm/activities/generateImage/index.d.ts +7 -0
  11. package/dist/esm/activities/generateImage/index.js +26 -1
  12. package/dist/esm/activities/generateImage/index.js.map +1 -1
  13. package/dist/esm/activities/generateSpeech/index.d.ts +7 -0
  14. package/dist/esm/activities/generateSpeech/index.js +26 -1
  15. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  16. package/dist/esm/activities/generateTranscription/index.d.ts +7 -0
  17. package/dist/esm/activities/generateTranscription/index.js +26 -1
  18. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  19. package/dist/esm/activities/generateVideo/index.d.ts +9 -0
  20. package/dist/esm/activities/generateVideo/index.js +52 -2
  21. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  22. package/dist/esm/activities/middleware/index.d.ts +2 -0
  23. package/dist/esm/activities/middleware/run.d.ts +20 -0
  24. package/dist/esm/activities/middleware/run.js +42 -0
  25. package/dist/esm/activities/middleware/run.js.map +1 -0
  26. package/dist/esm/activities/middleware/types.d.ts +118 -0
  27. package/dist/esm/index.d.ts +2 -0
  28. package/dist/esm/index.js +2 -0
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/middlewares/otel.d.ts +8 -2
  31. package/dist/esm/middlewares/otel.js +145 -95
  32. package/dist/esm/middlewares/otel.js.map +1 -1
  33. package/dist/esm/middlewares/usage-attributes.d.ts +24 -0
  34. package/dist/esm/middlewares/usage-attributes.js +43 -0
  35. package/dist/esm/middlewares/usage-attributes.js.map +1 -0
  36. package/dist/esm/types.d.ts +7 -7
  37. package/dist/esm/utilities/errors.d.ts +13 -0
  38. package/dist/esm/utilities/errors.js +22 -0
  39. package/dist/esm/utilities/errors.js.map +1 -0
  40. package/dist/esm/utilities/numbers.d.ts +8 -0
  41. package/dist/esm/utilities/numbers.js +12 -0
  42. package/dist/esm/utilities/numbers.js.map +1 -0
  43. package/package.json +2 -2
  44. package/src/activities/chat/index.ts +32 -4
  45. package/src/activities/chat/middleware/types.ts +7 -0
  46. package/src/activities/chat/tools/lazy-tool-manager.ts +46 -4
  47. package/src/activities/generateAudio/index.ts +42 -1
  48. package/src/activities/generateImage/index.ts +42 -1
  49. package/src/activities/generateSpeech/index.ts +42 -1
  50. package/src/activities/generateTranscription/index.ts +42 -1
  51. package/src/activities/generateVideo/index.ts +88 -2
  52. package/src/activities/middleware/index.ts +20 -0
  53. package/src/activities/middleware/run.ts +88 -0
  54. package/src/activities/middleware/types.ts +173 -0
  55. package/src/index.ts +19 -0
  56. package/src/middlewares/otel.ts +195 -120
  57. package/src/middlewares/usage-attributes.ts +65 -0
  58. package/src/types.ts +7 -7
  59. package/src/utilities/errors.ts +29 -0
  60. package/src/utilities/numbers.ts +15 -0
@@ -0,0 +1,173 @@
1
+ import type { TokenUsage } from '../../types'
2
+
3
+ // ===========================
4
+ // Generation middleware
5
+ // ===========================
6
+ //
7
+ // The base, activity-agnostic middleware contract. Every activity — chat and
8
+ // the media activities — runs middleware that satisfies this shape. `chat()`
9
+ // accepts the richer `ChatMiddleware` superset (it adds config/chunk/tool
10
+ // hooks and capability primitives on top of these lifecycle hooks); media
11
+ // activities accept `GenerationMiddleware` directly.
12
+ //
13
+ // The relationship is intentionally STRUCTURAL, not nominal: `ChatMiddleware`
14
+ // does not `extends GenerationMiddleware`. Chat hooks use function-property
15
+ // syntax, so under `strictFunctionTypes` a narrowed-context subtype would be
16
+ // rejected; declaring it via inheritance would force method syntax and reopen
17
+ // a bivariance hole (a chat hook reading `ctx.messages` slotted where only a
18
+ // base context exists). Instead, the base context/info types are SUPERTYPES
19
+ // (fewer fields) and the chat context/info types are SUBTYPES (more fields),
20
+ // so a single value whose lifecycle hooks are authored against the base — like
21
+ // `otelMiddleware()` — satisfies `GenerationMiddleware & ChatMiddleware` by
22
+ // contravariance, while an arbitrary `ChatMiddleware` is NOT assignable to
23
+ // `GenerationMiddleware`.
24
+
25
+ /**
26
+ * The activity an observability event describes.
27
+ *
28
+ * Mirrors the public surface a caller reaches for: `'chat'` for `chat()`, and
29
+ * the media kinds for the `generate*` activities. `'tts'` matches the speech
30
+ * adapter's kind (the public discriminator avoids inventing a parallel
31
+ * `'speech'`/`'text'` vocabulary). `otelMiddleware` maps each to its
32
+ * `gen_ai.operation.name`.
33
+ */
34
+ export type GenerationActivity =
35
+ | 'chat'
36
+ | 'image'
37
+ | 'video'
38
+ | 'audio'
39
+ | 'tts'
40
+ | 'transcription'
41
+
42
+ /**
43
+ * Stable context passed to every {@link GenerationMiddleware} hook. Created
44
+ * once per activity call and shared across the hooks of that call.
45
+ *
46
+ * Carries only fields every activity can honor. `ChatMiddlewareContext`
47
+ * structurally includes all of these plus chat-only state (messages,
48
+ * iteration, capabilities, …), which is why a chat middleware that reads those
49
+ * extra fields is not assignable to `GenerationMiddleware`.
50
+ */
51
+ export interface GenerationMiddlewareContext<TContext = unknown> {
52
+ /**
53
+ * Stable id correlating the `onStart` / `onFinish` / `onError` / `onAbort`
54
+ * hooks of a single activity call.
55
+ */
56
+ requestId: string
57
+ /** Which activity this call is. Discriminates media from chat. */
58
+ activity: GenerationActivity
59
+ /** Provider/adapter name (e.g. `"openai"`). Emitted as `gen_ai.system`. */
60
+ provider: string
61
+ /** Model id. Emitted as `gen_ai.request.model`. */
62
+ model: string
63
+ /**
64
+ * Provider-specific options passed to the activity, if any. Typed `unknown`
65
+ * because each activity's options are strongly typed per model; a supertype
66
+ * of `ChatMiddlewareContext`'s `modelOptions`.
67
+ */
68
+ modelOptions?: unknown
69
+ /** Where the call originates. Always `'server'` for media activities. */
70
+ source: 'client' | 'server'
71
+ /** Generate a unique id with the given prefix. */
72
+ createId: (prefix: string) => string
73
+ /** Runtime context provided by the activity options, if any. */
74
+ context: TContext
75
+ }
76
+
77
+ // ===========================
78
+ // Hook payloads
79
+ // ===========================
80
+
81
+ /**
82
+ * Token usage passed to {@link GenerationMiddleware.onUsage}. Kept as an
83
+ * interface extending `TokenUsage` to preserve declaration merging for this
84
+ * publicly exported type.
85
+ */
86
+ export interface GenerationUsageInfo extends TokenUsage {}
87
+
88
+ /** Information passed to {@link GenerationMiddleware.onFinish}. */
89
+ export interface GenerationFinishInfo {
90
+ /** Wall-clock duration of the activity call, in milliseconds. */
91
+ duration: number
92
+ /** Unified usage, when the provider reported it. */
93
+ usage?: TokenUsage | undefined
94
+ }
95
+
96
+ /** Information passed to {@link GenerationMiddleware.onAbort}. */
97
+ export interface GenerationAbortInfo {
98
+ /** The reason for the abort, if provided. */
99
+ reason?: string
100
+ /** Wall-clock duration until the abort, in milliseconds. */
101
+ duration: number
102
+ }
103
+
104
+ /** Information passed to {@link GenerationMiddleware.onError}. */
105
+ export interface GenerationErrorInfo {
106
+ /** The thrown value (typically an `Error`). */
107
+ error: unknown
108
+ /** Wall-clock duration until the failure, in milliseconds. */
109
+ duration: number
110
+ }
111
+
112
+ // ===========================
113
+ // Middleware interface
114
+ // ===========================
115
+
116
+ /**
117
+ * Activity-agnostic, observe-only middleware.
118
+ *
119
+ * A thin lifecycle observer registerable on any activity via its `middleware`
120
+ * option. Unlike `ChatMiddleware` (which can also rewrite config, chunks, and
121
+ * tool calls), these hooks only observe — the right fit for the single
122
+ * request → response shape of media activities. Pass `otelMiddleware()` for
123
+ * OpenTelemetry, or implement the hooks directly for a custom backend.
124
+ *
125
+ * Hooks are awaited in registration order. A hook that throws PROPAGATES and
126
+ * fails the activity — matching `chat()` middleware semantics. Keep them cheap;
127
+ * they run inline with the request.
128
+ *
129
+ * Exactly one of `onFinish` / `onAbort` / `onError` fires per call.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * import { generateImage } from '@tanstack/ai'
134
+ * import { otelMiddleware } from '@tanstack/ai/middlewares/otel'
135
+ * import { openaiImage } from '@tanstack/ai-openai'
136
+ * import { trace } from '@opentelemetry/api'
137
+ *
138
+ * await generateImage({
139
+ * adapter: openaiImage('gpt-image-1'),
140
+ * prompt: 'A serene mountain landscape at sunset',
141
+ * middleware: [otelMiddleware({ tracer: trace.getTracer('my-app') })],
142
+ * })
143
+ * ```
144
+ */
145
+ export interface GenerationMiddleware<TContext = unknown> {
146
+ /** Optional name, surfaced in diagnostics. */
147
+ name?: string
148
+ /** Called before the adapter request begins. */
149
+ onStart?: (ctx: GenerationMiddlewareContext<TContext>) => void | Promise<void>
150
+ /** Called when the provider reports usage, before `onFinish`. */
151
+ onUsage?: (
152
+ ctx: GenerationMiddlewareContext<TContext>,
153
+ usage: GenerationUsageInfo,
154
+ ) => void | Promise<void>
155
+ /** Called after the activity completes successfully. */
156
+ onFinish?: (
157
+ ctx: GenerationMiddlewareContext<TContext>,
158
+ info: GenerationFinishInfo,
159
+ ) => void | Promise<void>
160
+ /** Called when the activity is aborted (e.g. an abandoned stream). */
161
+ onAbort?: (
162
+ ctx: GenerationMiddlewareContext<TContext>,
163
+ info: GenerationAbortInfo,
164
+ ) => void | Promise<void>
165
+ /** Called when the activity throws before completing. */
166
+ onError?: (
167
+ ctx: GenerationMiddlewareContext<TContext>,
168
+ info: GenerationErrorInfo,
169
+ ) => void | Promise<void>
170
+ }
171
+
172
+ /** A `GenerationMiddleware` with a permissive context — for use as a constraint. */
173
+ export type AnyGenerationMiddleware = GenerationMiddleware<any>
package/src/index.ts CHANGED
@@ -82,6 +82,10 @@ export {
82
82
  // Tool call management
83
83
  export { ToolCallManager } from './activities/chat/tools/tool-calls'
84
84
 
85
+ // Lazy tool discovery (name of the synthetic discovery tool, for custom
86
+ // message-compaction logic that needs to reference it)
87
+ export { DISCOVERY_TOOL_NAME } from './activities/chat/tools/lazy-tool-manager'
88
+
85
89
  // Provider tool type
86
90
  export type { ProviderTool } from './tools/provider-tool'
87
91
  export { brandProviderTool } from './tools/provider-tool'
@@ -118,6 +122,21 @@ export type {
118
122
  ErrorInfo,
119
123
  } from './activities/chat/middleware/index'
120
124
 
125
+ // Base, activity-agnostic middleware. The observe-only superset that media
126
+ // activities accept via their `middleware` option; `ChatMiddleware` adds the
127
+ // chat-only hooks on top. Pure types only — the `otelMiddleware` value lives at
128
+ // `@tanstack/ai/middlewares/otel` so the root barrel never requires the
129
+ // optional `@opentelemetry/api` peer dependency.
130
+ export type {
131
+ GenerationMiddleware,
132
+ GenerationMiddlewareContext,
133
+ GenerationActivity,
134
+ GenerationUsageInfo,
135
+ GenerationFinishInfo,
136
+ GenerationAbortInfo,
137
+ GenerationErrorInfo,
138
+ AnyGenerationMiddleware,
139
+ } from './activities/middleware/index'
121
140
  // Capability primitives + middleware builder
122
141
  export {
123
142
  createCapability,