@tanstack/ai 0.42.0 → 0.43.1

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 (273) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/activities/chat/adapter.js +23 -16
  3. package/dist/esm/activities/chat/adapter.js.map +1 -1
  4. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +5 -36
  5. package/dist/esm/activities/chat/agent-loop-strategies.js +75 -21
  6. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
  7. package/dist/esm/activities/chat/cancel.d.ts +40 -0
  8. package/dist/esm/activities/chat/cancel.js +54 -0
  9. package/dist/esm/activities/chat/cancel.js.map +1 -0
  10. package/dist/esm/activities/chat/index.d.ts +28 -21
  11. package/dist/esm/activities/chat/index.js +2100 -1813
  12. package/dist/esm/activities/chat/index.js.map +1 -1
  13. package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
  14. package/dist/esm/activities/chat/mcp/manager.js +90 -77
  15. package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
  16. package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
  17. package/dist/esm/activities/chat/messages.js +397 -346
  18. package/dist/esm/activities/chat/messages.js.map +1 -1
  19. package/dist/esm/activities/chat/middleware/builder.js +17 -15
  20. package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
  21. package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
  22. package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
  23. package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
  24. package/dist/esm/activities/chat/middleware/compose.js +623 -531
  25. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  26. package/dist/esm/activities/chat/middleware/define.js +12 -5
  27. package/dist/esm/activities/chat/middleware/define.js.map +1 -1
  28. package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
  29. package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
  30. package/dist/esm/activities/chat/middleware/locks.js +71 -0
  31. package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
  32. package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
  33. package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
  34. package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
  35. package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
  36. package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
  37. package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
  38. package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
  39. package/dist/esm/activities/chat/middleware/run-store.js +176 -0
  40. package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
  41. package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
  42. package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
  43. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
  44. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
  45. package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
  46. package/dist/esm/activities/chat/middleware/validate.js +23 -28
  47. package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
  48. package/dist/esm/activities/chat/stream/json-parser.js +39 -25
  49. package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
  50. package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
  51. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  52. package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
  53. package/dist/esm/activities/chat/stream/processor.js +1341 -1542
  54. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  55. package/dist/esm/activities/chat/stream/strategies.js +69 -53
  56. package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
  57. package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
  58. package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
  59. package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
  60. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
  61. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
  62. package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
  63. package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
  64. package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
  65. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  66. package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
  67. package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
  68. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  69. package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
  70. package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
  71. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  72. package/dist/esm/activities/error-payload.js +85 -47
  73. package/dist/esm/activities/error-payload.js.map +1 -1
  74. package/dist/esm/activities/generateAudio/adapter.js +22 -15
  75. package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
  76. package/dist/esm/activities/generateAudio/index.d.ts +4 -0
  77. package/dist/esm/activities/generateAudio/index.js +141 -105
  78. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  79. package/dist/esm/activities/generateImage/adapter.js +22 -15
  80. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  81. package/dist/esm/activities/generateImage/index.d.ts +4 -0
  82. package/dist/esm/activities/generateImage/index.js +155 -111
  83. package/dist/esm/activities/generateImage/index.js.map +1 -1
  84. package/dist/esm/activities/generateSpeech/adapter.js +22 -15
  85. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
  86. package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
  87. package/dist/esm/activities/generateSpeech/index.js +159 -110
  88. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  89. package/dist/esm/activities/generateTranscription/adapter.js +22 -15
  90. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
  91. package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
  92. package/dist/esm/activities/generateTranscription/index.js +159 -100
  93. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  94. package/dist/esm/activities/generateVideo/adapter.js +36 -29
  95. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  96. package/dist/esm/activities/generateVideo/index.d.ts +143 -19
  97. package/dist/esm/activities/generateVideo/index.js +456 -279
  98. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  99. package/dist/esm/activities/generateVideo/snap.js +60 -48
  100. package/dist/esm/activities/generateVideo/snap.js.map +1 -1
  101. package/dist/esm/activities/index.js +8 -34
  102. package/dist/esm/activities/middleware/index.d.ts +1 -1
  103. package/dist/esm/activities/middleware/run.d.ts +10 -0
  104. package/dist/esm/activities/middleware/run.js +53 -29
  105. package/dist/esm/activities/middleware/run.js.map +1 -1
  106. package/dist/esm/activities/middleware/types.d.ts +44 -6
  107. package/dist/esm/activities/stream-generation-result.d.ts +4 -1
  108. package/dist/esm/activities/stream-generation-result.js +79 -44
  109. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  110. package/dist/esm/activities/summarize/adapter.js +22 -15
  111. package/dist/esm/activities/summarize/adapter.js.map +1 -1
  112. package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
  113. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  114. package/dist/esm/activities/summarize/index.d.ts +27 -0
  115. package/dist/esm/activities/summarize/index.js +268 -102
  116. package/dist/esm/activities/summarize/index.js.map +1 -1
  117. package/dist/esm/adapter-internals.d.ts +2 -1
  118. package/dist/esm/adapter-internals.js +4 -11
  119. package/dist/esm/client.d.ts +25 -3
  120. package/dist/esm/client.js +131 -64
  121. package/dist/esm/client.js.map +1 -1
  122. package/dist/esm/custom-events.d.ts +76 -0
  123. package/dist/esm/custom-events.js +37 -0
  124. package/dist/esm/custom-events.js.map +1 -0
  125. package/dist/esm/delivery-detach.d.ts +50 -0
  126. package/dist/esm/delivery-detach.js +71 -0
  127. package/dist/esm/delivery-detach.js.map +1 -0
  128. package/dist/esm/delivery-disconnect.d.ts +62 -0
  129. package/dist/esm/delivery-disconnect.js +81 -0
  130. package/dist/esm/delivery-disconnect.js.map +1 -0
  131. package/dist/esm/extend-adapter.js +19 -17
  132. package/dist/esm/extend-adapter.js.map +1 -1
  133. package/dist/esm/index.d.ts +24 -6
  134. package/dist/esm/index.js +30 -98
  135. package/dist/esm/interrupt-resume.d.ts +71 -0
  136. package/dist/esm/interrupt-resume.js +438 -0
  137. package/dist/esm/interrupt-resume.js.map +1 -0
  138. package/dist/esm/interrupt-serialization.d.ts +12 -0
  139. package/dist/esm/interrupt-serialization.js +178 -0
  140. package/dist/esm/interrupt-serialization.js.map +1 -0
  141. package/dist/esm/interrupts.d.ts +84 -0
  142. package/dist/esm/interrupts.js +31 -0
  143. package/dist/esm/interrupts.js.map +1 -0
  144. package/dist/esm/locks.d.ts +10 -0
  145. package/dist/esm/locks.js +2 -0
  146. package/dist/esm/logger/console-logger.js +101 -78
  147. package/dist/esm/logger/console-logger.js.map +1 -1
  148. package/dist/esm/logger/internal-logger.js +104 -89
  149. package/dist/esm/logger/internal-logger.js.map +1 -1
  150. package/dist/esm/logger/resolve.js +54 -49
  151. package/dist/esm/logger/resolve.js.map +1 -1
  152. package/dist/esm/logger/types.d.ts +1 -1
  153. package/dist/esm/middlewares/content-guard.js +142 -148
  154. package/dist/esm/middlewares/content-guard.js.map +1 -1
  155. package/dist/esm/middlewares/index.js +2 -6
  156. package/dist/esm/middlewares/otel.d.ts +3 -1
  157. package/dist/esm/middlewares/otel.js +599 -732
  158. package/dist/esm/middlewares/otel.js.map +1 -1
  159. package/dist/esm/middlewares/usage-attributes.js +47 -40
  160. package/dist/esm/middlewares/usage-attributes.js.map +1 -1
  161. package/dist/esm/realtime/event-emitter.js +24 -25
  162. package/dist/esm/realtime/event-emitter.js.map +1 -1
  163. package/dist/esm/realtime/index.d.ts +5 -9
  164. package/dist/esm/realtime/index.js +29 -6
  165. package/dist/esm/realtime/index.js.map +1 -1
  166. package/dist/esm/scope.d.ts +47 -0
  167. package/dist/esm/stream-durability.d.ts +171 -0
  168. package/dist/esm/stream-durability.js +295 -0
  169. package/dist/esm/stream-durability.js.map +1 -0
  170. package/dist/esm/stream-to-response.d.ts +178 -13
  171. package/dist/esm/stream-to-response.js +663 -115
  172. package/dist/esm/stream-to-response.js.map +1 -1
  173. package/dist/esm/strip-to-spec-middleware.js +30 -16
  174. package/dist/esm/strip-to-spec-middleware.js.map +1 -1
  175. package/dist/esm/system-prompts.js +27 -21
  176. package/dist/esm/system-prompts.js.map +1 -1
  177. package/dist/esm/tool-registry.js +72 -45
  178. package/dist/esm/tool-registry.js.map +1 -1
  179. package/dist/esm/tools/provider-tool.js +14 -5
  180. package/dist/esm/tools/provider-tool.js.map +1 -1
  181. package/dist/esm/types.d.ts +321 -42
  182. package/dist/esm/types.js +2 -0
  183. package/dist/esm/utilities/ag-ui-wire.js +79 -93
  184. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  185. package/dist/esm/utilities/chat-params.d.ts +26 -4
  186. package/dist/esm/utilities/chat-params.js +218 -92
  187. package/dist/esm/utilities/chat-params.js.map +1 -1
  188. package/dist/esm/utilities/errors.js +28 -18
  189. package/dist/esm/utilities/errors.js.map +1 -1
  190. package/dist/esm/utilities/media-prompt.js +46 -41
  191. package/dist/esm/utilities/media-prompt.js.map +1 -1
  192. package/dist/esm/utilities/numbers.js +13 -10
  193. package/dist/esm/utilities/numbers.js.map +1 -1
  194. package/dist/esm/utilities/provider-executed.js +20 -11
  195. package/dist/esm/utilities/provider-executed.js.map +1 -1
  196. package/dist/esm/utilities/sampling-keys.js +31 -19
  197. package/dist/esm/utilities/sampling-keys.js.map +1 -1
  198. package/dist/esm/utilities/tool-result.js +42 -30
  199. package/dist/esm/utilities/tool-result.js.map +1 -1
  200. package/dist/esm/utilities/usage.js +27 -9
  201. package/dist/esm/utilities/usage.js.map +1 -1
  202. package/dist/esm/utils.js +26 -18
  203. package/dist/esm/utils.js.map +1 -1
  204. package/package.json +10 -6
  205. package/skills/ai-core/SKILL.md +69 -18
  206. package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
  207. package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
  208. package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
  209. package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
  210. package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
  211. package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
  212. package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
  213. package/skills/ai-core/chat-experience/SKILL.md +98 -11
  214. package/skills/ai-core/client-persistence/SKILL.md +277 -0
  215. package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
  216. package/skills/ai-core/debug-logging/SKILL.md +1 -1
  217. package/skills/ai-core/locks/SKILL.md +143 -0
  218. package/skills/ai-core/media-generation/SKILL.md +144 -12
  219. package/skills/ai-core/middleware/SKILL.md +258 -33
  220. package/skills/ai-core/structured-outputs/SKILL.md +1 -1
  221. package/skills/ai-core/tool-calling/SKILL.md +54 -61
  222. package/src/activities/chat/agent-loop-strategies.ts +5 -39
  223. package/src/activities/chat/cancel.ts +81 -0
  224. package/src/activities/chat/index.ts +1091 -200
  225. package/src/activities/chat/mcp/manager.ts +4 -4
  226. package/src/activities/chat/mcp/types.ts +2 -2
  227. package/src/activities/chat/messages.ts +5 -3
  228. package/src/activities/chat/middleware/builder.ts +1 -1
  229. package/src/activities/chat/middleware/compose.ts +186 -9
  230. package/src/activities/chat/middleware/index.ts +26 -0
  231. package/src/activities/chat/middleware/locks.ts +102 -0
  232. package/src/activities/chat/middleware/pending-turn.ts +47 -0
  233. package/src/activities/chat/middleware/run-disconnect.ts +62 -0
  234. package/src/activities/chat/middleware/run-store.ts +412 -0
  235. package/src/activities/chat/middleware/types.ts +62 -1
  236. package/src/activities/chat/stream/processor.ts +189 -5
  237. package/src/activities/chat/tools/approval-schema.ts +205 -0
  238. package/src/activities/chat/tools/tool-calls.ts +106 -13
  239. package/src/activities/chat/tools/tool-definition.ts +210 -39
  240. package/src/activities/generateAudio/index.ts +20 -3
  241. package/src/activities/generateImage/index.ts +20 -3
  242. package/src/activities/generateSpeech/index.ts +25 -3
  243. package/src/activities/generateTranscription/index.ts +26 -3
  244. package/src/activities/generateVideo/index.ts +345 -82
  245. package/src/activities/middleware/index.ts +2 -0
  246. package/src/activities/middleware/run.ts +31 -0
  247. package/src/activities/middleware/types.ts +49 -5
  248. package/src/activities/stream-generation-result.ts +30 -2
  249. package/src/activities/summarize/chat-stream-summarize.ts +5 -0
  250. package/src/activities/summarize/index.ts +200 -10
  251. package/src/adapter-internals.ts +10 -1
  252. package/src/client.ts +244 -0
  253. package/src/custom-events.ts +107 -0
  254. package/src/delivery-detach.ts +72 -0
  255. package/src/delivery-disconnect.ts +84 -0
  256. package/src/index.ts +138 -1
  257. package/src/interrupt-resume.ts +824 -0
  258. package/src/interrupt-serialization.ts +183 -0
  259. package/src/interrupts.ts +146 -0
  260. package/src/locks.ts +17 -0
  261. package/src/logger/types.ts +1 -1
  262. package/src/middlewares/otel.ts +23 -5
  263. package/src/realtime/index.ts +5 -9
  264. package/src/scope.ts +47 -0
  265. package/src/stream-durability.ts +598 -0
  266. package/src/stream-to-response.ts +1051 -95
  267. package/src/strip-to-spec-middleware.ts +3 -3
  268. package/src/types.ts +405 -45
  269. package/src/utilities/chat-params.ts +245 -55
  270. package/dist/esm/activities/index.js.map +0 -1
  271. package/dist/esm/adapter-internals.js.map +0 -1
  272. package/dist/esm/index.js.map +0 -1
  273. package/dist/esm/middlewares/index.js.map +0 -1
@@ -1,295 +1,472 @@
1
- import { aiEventClient } from "@tanstack/ai-event-client";
2
1
  import { toRunErrorPayload } from "../error-payload.js";
3
2
  import { resolveDebugOption } from "../../logger/resolve.js";
4
- import { createGenerationContext, runGenerationStart, runGenerationUsage, runGenerationFinish, runGenerationError, runGenerationAbort } from "../middleware/run.js";
5
- const kind = "video";
3
+ import { applyGenerationResultTransforms, createGenerationContext, runGenerationAbort, runGenerationError, runGenerationFinish, runGenerationStart, runGenerationUsage } from "../middleware/run.js";
4
+ import "./adapter.js";
5
+ import { aiEventClient } from "@tanstack/ai-event-client";
6
+ //#region src/activities/generateVideo/index.ts
7
+ /**
8
+ * Video Activity (Experimental)
9
+ *
10
+ * Generates videos from text prompts using a jobs/polling architecture.
11
+ * This is a self-contained module with implementation, types, and JSDoc.
12
+ *
13
+ * @experimental Video generation is an experimental feature and may change.
14
+ */
15
+ /** The adapter kind this activity handles */
16
+ var kind = "video";
6
17
  function createId(prefix) {
7
- return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
18
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
8
19
  }
20
+ /**
21
+ * Generate video - creates a video generation job from a text prompt.
22
+ *
23
+ * Uses AI video generation models to create videos based on natural language descriptions.
24
+ * Unlike image generation, video generation is asynchronous and requires polling for completion.
25
+ *
26
+ * When `stream: true` is passed, handles the full job lifecycle automatically:
27
+ * create job → poll for status → stream updates → yield final result.
28
+ *
29
+ * @experimental Video generation is an experimental feature and may change.
30
+ *
31
+ * @example Create a video generation job
32
+ * ```ts
33
+ * import { generateVideo, getVideoJobStatus } from '@tanstack/ai'
34
+ * import { openaiVideo } from '@tanstack/ai-openai'
35
+ *
36
+ * // Start a video generation job
37
+ * const { jobId } = await generateVideo({
38
+ * adapter: openaiVideo('sora-2'),
39
+ * prompt: 'A cat chasing a dog in a sunny park'
40
+ * })
41
+ *
42
+ * console.log('Job started:', jobId)
43
+ *
44
+ * // The submission only OPENS the run; the poll that sees a terminal state is
45
+ * // what completes it. The `jobId` is the whole correlation — pass the same
46
+ * // `middleware` and `threadId` when you use them.
47
+ * const status = await getVideoJobStatus({
48
+ * adapter: openaiVideo('sora-2'),
49
+ * jobId,
50
+ * })
51
+ * ```
52
+ *
53
+ * @example Stream the full video generation lifecycle
54
+ * ```ts
55
+ * import { generateVideo, toServerSentEventsResponse } from '@tanstack/ai'
56
+ * import { openaiVideo } from '@tanstack/ai-openai'
57
+ *
58
+ * const stream = generateVideo({
59
+ * adapter: openaiVideo('sora-2'),
60
+ * prompt: 'A cat chasing a dog in a sunny park',
61
+ * stream: true,
62
+ * pollingInterval: 3000,
63
+ * })
64
+ *
65
+ * return toServerSentEventsResponse(stream)
66
+ * ```
67
+ */
9
68
  function generateVideo(options) {
10
- if (options.stream) {
11
- return runStreamingVideoGeneration(
12
- options
13
- );
14
- }
15
- return runCreateVideoJob(options);
69
+ if (options.stream) return runStreamingVideoGeneration(options);
70
+ return runCreateVideoJob(options);
71
+ }
72
+ /**
73
+ * The run id a non-streaming video job is filed under, derived from the
74
+ * provider job itself.
75
+ *
76
+ * A submit-and-poll run spans two calls in two different requests, so the two
77
+ * halves need to agree on an id. Deriving it from the `jobId` — the one id a
78
+ * poller structurally cannot be missing, because it cannot poll without it —
79
+ * means no correlation state has to survive the boundary and there is no
80
+ * "forgot to pass the run id" failure to document. The provider is part of the
81
+ * key so two providers' job-id spaces cannot collide, and both halves are
82
+ * percent-encoded so the joined string stays unambiguous (and url-safe, since
83
+ * run ids end up in storage keys and query strings).
84
+ */
85
+ function videoRunIdForJob(provider, jobId) {
86
+ return `video:${encodeURIComponent(provider)}:${encodeURIComponent(jobId)}`;
16
87
  }
88
+ /**
89
+ * Internal implementation of non-streaming video job creation.
90
+ *
91
+ * Submitting a job OPENS a run, it does not complete one: the video does not
92
+ * exist yet, and the bytes only appear on a later poll. So this fires `onStart`
93
+ * and runs the result transforms over the submission result — the jobId lands
94
+ * on the run record, which is what lets a later request resume polling — but
95
+ * fires NO terminal hook. {@link getVideoJobStatus} finishes the run when the
96
+ * job settles, keyed on the same derived id.
97
+ *
98
+ * `onStart` therefore runs AFTER the submit request: the run's id comes from
99
+ * the job, which does not exist until the provider accepts it. A submission
100
+ * that fails has no job, so it opens and immediately fails a run under this
101
+ * call's `requestId` — terminal and unresumable by construction, but it puts
102
+ * the failure where a client hydrating the thread will see it instead of
103
+ * showing nothing.
104
+ */
17
105
  async function runCreateVideoJob(options) {
18
- const { adapter, prompt, size, duration, modelOptions, middleware } = options;
19
- const model = adapter.model;
20
- const requestId = createId("video");
21
- const startTime = Date.now();
22
- const logger = resolveDebugOption(options.debug);
23
- const providerName = adapter.provider ?? adapter.name ?? "unknown";
24
- const mwCtx = createGenerationContext({
25
- requestId,
26
- activity: "video",
27
- provider: adapter.name,
28
- model,
29
- modelOptions,
30
- createId
31
- });
32
- await runGenerationStart(middleware, mwCtx);
33
- logger.request(`activity=generateVideo provider=${providerName}`, {
34
- provider: providerName,
35
- model
36
- });
37
- try {
38
- const result = await adapter.createVideoJob({
39
- model,
40
- prompt,
41
- size,
42
- duration,
43
- modelOptions,
44
- logger
45
- });
46
- logger.output(`activity=generateVideo jobId=${result.jobId}`, {
47
- jobId: result.jobId,
48
- model: result.model
49
- });
50
- await runGenerationFinish(middleware, mwCtx, {
51
- duration: Date.now() - startTime
52
- });
53
- return result;
54
- } catch (error) {
55
- await runGenerationError(middleware, mwCtx, {
56
- error,
57
- duration: Date.now() - startTime
58
- });
59
- logger.errors("generateVideo activity failed", {
60
- error,
61
- source: "generateVideo"
62
- });
63
- throw error;
64
- }
106
+ const { adapter, prompt, size, duration, modelOptions, middleware } = options;
107
+ const model = adapter.model;
108
+ const requestId = createId("video");
109
+ const startTime = Date.now();
110
+ const logger = resolveDebugOption(options.debug);
111
+ const providerName = adapter.provider ?? adapter.name ?? "unknown";
112
+ const contextFor = (runId) => createGenerationContext({
113
+ requestId,
114
+ activity: "video",
115
+ provider: adapter.name,
116
+ model,
117
+ modelOptions,
118
+ threadId: options.threadId,
119
+ runId,
120
+ artifactInputs: { prompt },
121
+ createId
122
+ });
123
+ logger.request(`activity=generateVideo provider=${providerName}`, {
124
+ provider: providerName,
125
+ model
126
+ });
127
+ let jobResult;
128
+ try {
129
+ jobResult = await adapter.createVideoJob({
130
+ model,
131
+ prompt,
132
+ size,
133
+ duration,
134
+ modelOptions,
135
+ logger
136
+ });
137
+ } catch (error) {
138
+ const failedCtx = contextFor();
139
+ await runGenerationStart(middleware, failedCtx);
140
+ await runGenerationError(middleware, failedCtx, {
141
+ error,
142
+ duration: Date.now() - startTime
143
+ });
144
+ logger.errors("generateVideo activity failed", {
145
+ error,
146
+ source: "generateVideo"
147
+ });
148
+ throw error;
149
+ }
150
+ logger.output(`activity=generateVideo jobId=${jobResult.jobId}`, {
151
+ jobId: jobResult.jobId,
152
+ model: jobResult.model
153
+ });
154
+ const mwCtx = contextFor(videoRunIdForJob(adapter.name, jobResult.jobId));
155
+ await runGenerationStart(middleware, mwCtx);
156
+ return await applyGenerationResultTransforms(mwCtx, jobResult);
65
157
  }
66
158
  function sleep(ms) {
67
- return new Promise((resolve) => setTimeout(resolve, ms));
159
+ return new Promise((resolve) => setTimeout(resolve, ms));
68
160
  }
161
+ /**
162
+ * Internal streaming implementation for video generation.
163
+ * Handles the full job lifecycle: create job → poll for status → stream updates → yield final result.
164
+ */
69
165
  async function* runStreamingVideoGeneration(options) {
70
- const { adapter, prompt, size, duration, modelOptions, middleware } = options;
71
- const model = adapter.model;
72
- const runId = options.runId ?? createId("run");
73
- const requestId = createId("video");
74
- const obsStartTime = Date.now();
75
- const pollingInterval = options.pollingInterval ?? 2e3;
76
- const maxDuration = options.maxDuration ?? 6e5;
77
- const logger = resolveDebugOption(options.debug);
78
- const providerName = adapter.provider ?? adapter.name ?? "unknown";
79
- const threadId = createId("thread");
80
- yield {
81
- type: "RUN_STARTED",
82
- runId,
83
- threadId,
84
- timestamp: Date.now()
85
- };
86
- const mwCtx = createGenerationContext({
87
- requestId,
88
- activity: "video",
89
- provider: adapter.name,
90
- model,
91
- modelOptions,
92
- createId
93
- });
94
- await runGenerationStart(middleware, mwCtx);
95
- logger.request(
96
- `activity=generateVideo provider=${providerName} stream=true`,
97
- {
98
- provider: providerName,
99
- model
100
- }
101
- );
102
- let settled = false;
103
- try {
104
- const jobResult = await adapter.createVideoJob({
105
- model,
106
- prompt,
107
- size,
108
- duration,
109
- modelOptions,
110
- logger
111
- });
112
- yield {
113
- type: "CUSTOM",
114
- name: "video:job:created",
115
- value: { jobId: jobResult.jobId },
116
- timestamp: Date.now()
117
- };
118
- const startTime = Date.now();
119
- while (Date.now() - startTime < maxDuration) {
120
- await sleep(pollingInterval);
121
- const statusResult = await adapter.getVideoStatus(jobResult.jobId);
122
- yield {
123
- type: "CUSTOM",
124
- name: "video:status",
125
- value: {
126
- jobId: jobResult.jobId,
127
- status: statusResult.status,
128
- progress: statusResult.progress,
129
- error: statusResult.error
130
- },
131
- timestamp: Date.now()
132
- };
133
- if (statusResult.status === "completed") {
134
- const urlResult = await adapter.getVideoUrl(jobResult.jobId);
135
- logger.output(
136
- `activity=generateVideo jobId=${jobResult.jobId} status=completed`,
137
- {
138
- jobId: jobResult.jobId,
139
- url: urlResult.url
140
- }
141
- );
142
- if (urlResult.usage)
143
- await runGenerationUsage(middleware, mwCtx, urlResult.usage);
144
- await runGenerationFinish(middleware, mwCtx, {
145
- duration: Date.now() - obsStartTime,
146
- usage: urlResult.usage
147
- });
148
- settled = true;
149
- yield {
150
- type: "CUSTOM",
151
- name: "generation:result",
152
- value: {
153
- jobId: jobResult.jobId,
154
- status: "completed",
155
- url: urlResult.url,
156
- expiresAt: urlResult.expiresAt,
157
- ...urlResult.usage ? { usage: urlResult.usage } : {}
158
- },
159
- timestamp: Date.now()
160
- };
161
- yield {
162
- type: "RUN_FINISHED",
163
- runId,
164
- threadId,
165
- finishReason: "stop",
166
- timestamp: Date.now()
167
- };
168
- return;
169
- }
170
- if (statusResult.status === "failed") {
171
- throw new Error(statusResult.error || "Video generation failed");
172
- }
173
- }
174
- throw new Error("Video generation timed out");
175
- } catch (error) {
176
- const payload = toRunErrorPayload(error, "Video generation failed");
177
- settled = true;
178
- await runGenerationError(middleware, mwCtx, {
179
- error,
180
- duration: Date.now() - obsStartTime
181
- });
182
- logger.errors("generateVideo activity failed", {
183
- message: payload.message,
184
- code: payload.code,
185
- source: "generateVideo"
186
- });
187
- yield {
188
- type: "RUN_ERROR",
189
- runId,
190
- threadId,
191
- message: payload.message,
192
- code: payload.code,
193
- error: payload,
194
- timestamp: Date.now()
195
- };
196
- } finally {
197
- if (!settled) {
198
- await runGenerationAbort(middleware, mwCtx, {
199
- reason: "Video generation stream abandoned before completion",
200
- duration: Date.now() - obsStartTime
201
- });
202
- }
203
- }
166
+ const { adapter, prompt, size, duration, modelOptions, middleware } = options;
167
+ const model = adapter.model;
168
+ const runId = options.runId ?? createId("run");
169
+ const requestId = createId("video");
170
+ const obsStartTime = Date.now();
171
+ const pollingInterval = options.pollingInterval ?? 2e3;
172
+ const maxDuration = options.maxDuration ?? 6e5;
173
+ const logger = resolveDebugOption(options.debug);
174
+ const providerName = adapter.provider ?? adapter.name ?? "unknown";
175
+ const wireThreadId = options.threadId ?? createId("thread");
176
+ yield {
177
+ type: "RUN_STARTED",
178
+ runId,
179
+ threadId: wireThreadId,
180
+ timestamp: Date.now()
181
+ };
182
+ const mwCtx = createGenerationContext({
183
+ requestId,
184
+ activity: "video",
185
+ provider: adapter.name,
186
+ model,
187
+ modelOptions,
188
+ threadId: options.threadId,
189
+ runId,
190
+ artifactInputs: { prompt },
191
+ createId
192
+ });
193
+ await runGenerationStart(middleware, mwCtx);
194
+ logger.request(`activity=generateVideo provider=${providerName} stream=true`, {
195
+ provider: providerName,
196
+ model
197
+ });
198
+ let settled = false;
199
+ try {
200
+ const jobResult = await adapter.createVideoJob({
201
+ model,
202
+ prompt,
203
+ size,
204
+ duration,
205
+ modelOptions,
206
+ logger
207
+ });
208
+ yield {
209
+ type: "CUSTOM",
210
+ name: "video:job:created",
211
+ value: { jobId: jobResult.jobId },
212
+ timestamp: Date.now()
213
+ };
214
+ const startTime = Date.now();
215
+ while (Date.now() - startTime < maxDuration) {
216
+ await sleep(pollingInterval);
217
+ const statusResult = await adapter.getVideoStatus(jobResult.jobId);
218
+ yield {
219
+ type: "CUSTOM",
220
+ name: "video:status",
221
+ value: {
222
+ jobId: jobResult.jobId,
223
+ status: statusResult.status,
224
+ progress: statusResult.progress,
225
+ error: statusResult.error
226
+ },
227
+ timestamp: Date.now()
228
+ };
229
+ if (statusResult.status === "completed") {
230
+ const urlResult = await adapter.getVideoUrl(jobResult.jobId);
231
+ logger.output(`activity=generateVideo jobId=${jobResult.jobId} status=completed`, {
232
+ jobId: jobResult.jobId,
233
+ url: urlResult.url
234
+ });
235
+ const result = await applyGenerationResultTransforms(mwCtx, {
236
+ jobId: jobResult.jobId,
237
+ status: "completed",
238
+ url: urlResult.url,
239
+ expiresAt: urlResult.expiresAt,
240
+ ...urlResult.usage ? { usage: urlResult.usage } : {}
241
+ });
242
+ if (urlResult.usage) await runGenerationUsage(middleware, mwCtx, urlResult.usage);
243
+ await runGenerationFinish(middleware, mwCtx, {
244
+ duration: Date.now() - obsStartTime,
245
+ usage: urlResult.usage
246
+ });
247
+ settled = true;
248
+ yield {
249
+ type: "CUSTOM",
250
+ name: "generation:result",
251
+ value: result,
252
+ timestamp: Date.now()
253
+ };
254
+ yield {
255
+ type: "RUN_FINISHED",
256
+ runId,
257
+ threadId: wireThreadId,
258
+ finishReason: "stop",
259
+ timestamp: Date.now()
260
+ };
261
+ return;
262
+ }
263
+ if (statusResult.status === "failed") throw new Error(statusResult.error || "Video generation failed");
264
+ }
265
+ throw new Error("Video generation timed out");
266
+ } catch (error) {
267
+ const payload = toRunErrorPayload(error, "Video generation failed");
268
+ settled = true;
269
+ await runGenerationError(middleware, mwCtx, {
270
+ error,
271
+ duration: Date.now() - obsStartTime
272
+ });
273
+ logger.errors("generateVideo activity failed", {
274
+ message: payload.message,
275
+ code: payload.code,
276
+ source: "generateVideo"
277
+ });
278
+ yield {
279
+ type: "RUN_ERROR",
280
+ runId,
281
+ threadId: wireThreadId,
282
+ message: payload.message,
283
+ code: payload.code,
284
+ error: payload,
285
+ timestamp: Date.now()
286
+ };
287
+ } finally {
288
+ if (!settled) await runGenerationAbort(middleware, mwCtx, {
289
+ reason: "Video generation stream abandoned before completion",
290
+ duration: Date.now() - obsStartTime
291
+ });
292
+ }
204
293
  }
294
+ /**
295
+ * Get video job status - returns the current status, progress, and URL if available.
296
+ *
297
+ * This function combines status checking and URL retrieval. If the job is completed,
298
+ * it will automatically fetch and include the video URL.
299
+ *
300
+ * It is also where a non-streaming `generateVideo()` run ENDS: pass the same
301
+ * `middleware` and `threadId`, and the poll that first sees a terminal job state
302
+ * finishes the run (recording the result and its artifacts) or fails it. The run
303
+ * is identified by `adapter` + `jobId`, exactly what the submission derived it
304
+ * from, so there is nothing else to carry between the two calls.
305
+ *
306
+ * @experimental Video generation is an experimental feature and may change.
307
+ *
308
+ * @example Check job status
309
+ * ```ts
310
+ * import { getVideoJobStatus } from '@tanstack/ai'
311
+ * import { openaiVideo } from '@tanstack/ai-openai'
312
+ *
313
+ * const result = await getVideoJobStatus({
314
+ * adapter: openaiVideo('sora-2'),
315
+ * jobId: 'job-123'
316
+ * })
317
+ *
318
+ * console.log('Status:', result.status)
319
+ * console.log('Progress:', result.progress)
320
+ * if (result.url) {
321
+ * console.log('Video URL:', result.url)
322
+ * }
323
+ * ```
324
+ *
325
+ * @example Submit and poll one persisted run
326
+ * ```ts
327
+ * import { generateVideo, getVideoJobStatus } from '@tanstack/ai'
328
+ * import { withGenerationPersistence } from '@tanstack/ai-persistence'
329
+ * import { openaiVideo } from '@tanstack/ai-openai'
330
+ *
331
+ * const adapter = openaiVideo('sora-2')
332
+ * const middleware = [withGenerationPersistence(persistence)]
333
+ *
334
+ * // Opens the run (status `running`, jobId recorded). Its run id is derived
335
+ * // from the provider job, so nothing has to be stored to resume it.
336
+ * const { jobId } = await generateVideo({
337
+ * adapter,
338
+ * prompt: 'A cat chasing a dog in a sunny park',
339
+ * threadId,
340
+ * middleware,
341
+ * })
342
+ *
343
+ * // Completes the SAME run once the job settles — this is what writes the
344
+ * // video, its artifacts, and the terminal status. Works from a different
345
+ * // request or process: the jobId is the only correlation.
346
+ * const status = await getVideoJobStatus({
347
+ * adapter,
348
+ * jobId,
349
+ * threadId,
350
+ * middleware,
351
+ * })
352
+ * ```
353
+ */
205
354
  async function getVideoJobStatus(options) {
206
- const { adapter, jobId } = options;
207
- const requestId = createId("video-status");
208
- const startTime = Date.now();
209
- aiEventClient.emit("video:request:started", {
210
- requestId,
211
- provider: adapter.name,
212
- model: adapter.model,
213
- requestType: "status",
214
- jobId,
215
- timestamp: startTime
216
- });
217
- const statusResult = await adapter.getVideoStatus(jobId);
218
- if (statusResult.status === "completed") {
219
- try {
220
- const urlResult = await adapter.getVideoUrl(jobId);
221
- aiEventClient.emit("video:request:completed", {
222
- requestId,
223
- provider: adapter.name,
224
- model: adapter.model,
225
- requestType: "status",
226
- jobId,
227
- status: statusResult.status,
228
- progress: statusResult.progress,
229
- url: urlResult.url,
230
- duration: Date.now() - startTime,
231
- timestamp: Date.now()
232
- });
233
- if (urlResult.usage) {
234
- aiEventClient.emit("video:usage", {
235
- requestId,
236
- model: adapter.model,
237
- usage: urlResult.usage,
238
- timestamp: Date.now()
239
- });
240
- }
241
- return {
242
- status: statusResult.status,
243
- progress: statusResult.progress,
244
- url: urlResult.url,
245
- ...urlResult.usage ? { usage: urlResult.usage } : {}
246
- };
247
- } catch (error) {
248
- const errorMessage = error instanceof Error ? error.message : "Failed to get video URL";
249
- aiEventClient.emit("video:request:completed", {
250
- requestId,
251
- provider: adapter.name,
252
- model: adapter.model,
253
- requestType: "status",
254
- jobId,
255
- status: "failed",
256
- progress: statusResult.progress,
257
- error: errorMessage,
258
- duration: Date.now() - startTime,
259
- timestamp: Date.now()
260
- });
261
- return {
262
- status: "failed",
263
- progress: statusResult.progress,
264
- error: errorMessage
265
- };
266
- }
267
- }
268
- aiEventClient.emit("video:request:completed", {
269
- requestId,
270
- provider: adapter.name,
271
- model: adapter.model,
272
- requestType: "status",
273
- jobId,
274
- status: statusResult.status,
275
- progress: statusResult.progress,
276
- error: statusResult.error,
277
- duration: Date.now() - startTime,
278
- timestamp: Date.now()
279
- });
280
- return {
281
- status: statusResult.status,
282
- progress: statusResult.progress,
283
- error: statusResult.error
284
- };
355
+ const { adapter, jobId, middleware } = options;
356
+ const requestId = createId("video-status");
357
+ const startTime = Date.now();
358
+ const terminalContext = () => createGenerationContext({
359
+ requestId,
360
+ activity: "video",
361
+ provider: adapter.name,
362
+ model: adapter.model,
363
+ threadId: options.threadId,
364
+ runId: videoRunIdForJob(adapter.name, jobId),
365
+ createId
366
+ });
367
+ aiEventClient.emit("video:request:started", {
368
+ requestId,
369
+ provider: adapter.name,
370
+ model: adapter.model,
371
+ requestType: "status",
372
+ jobId,
373
+ timestamp: startTime
374
+ });
375
+ const statusResult = await adapter.getVideoStatus(jobId);
376
+ if (statusResult.status === "completed") {
377
+ let urlResult;
378
+ try {
379
+ urlResult = await adapter.getVideoUrl(jobId);
380
+ } catch (error) {
381
+ const errorMessage = error instanceof Error ? error.message : "Failed to get video URL";
382
+ aiEventClient.emit("video:request:completed", {
383
+ requestId,
384
+ provider: adapter.name,
385
+ model: adapter.model,
386
+ requestType: "status",
387
+ jobId,
388
+ status: "failed",
389
+ progress: statusResult.progress,
390
+ error: errorMessage,
391
+ duration: Date.now() - startTime,
392
+ timestamp: Date.now()
393
+ });
394
+ await runGenerationError(middleware, terminalContext(), {
395
+ error,
396
+ duration: Date.now() - startTime
397
+ });
398
+ return {
399
+ jobId,
400
+ status: "failed",
401
+ progress: statusResult.progress,
402
+ error: errorMessage
403
+ };
404
+ }
405
+ aiEventClient.emit("video:request:completed", {
406
+ requestId,
407
+ provider: adapter.name,
408
+ model: adapter.model,
409
+ requestType: "status",
410
+ jobId,
411
+ status: statusResult.status,
412
+ progress: statusResult.progress,
413
+ url: urlResult.url,
414
+ duration: Date.now() - startTime,
415
+ timestamp: Date.now()
416
+ });
417
+ if (urlResult.usage) aiEventClient.emit("video:usage", {
418
+ requestId,
419
+ model: adapter.model,
420
+ usage: urlResult.usage,
421
+ timestamp: Date.now()
422
+ });
423
+ const mwCtx = terminalContext();
424
+ await runGenerationStart(middleware, mwCtx);
425
+ const result = await applyGenerationResultTransforms(mwCtx, {
426
+ jobId,
427
+ status: "completed",
428
+ ...statusResult.progress !== void 0 ? { progress: statusResult.progress } : {},
429
+ url: urlResult.url,
430
+ ...urlResult.expiresAt ? { expiresAt: urlResult.expiresAt } : {},
431
+ ...urlResult.usage ? { usage: urlResult.usage } : {}
432
+ });
433
+ if (urlResult.usage) await runGenerationUsage(middleware, mwCtx, urlResult.usage);
434
+ await runGenerationFinish(middleware, mwCtx, {
435
+ duration: Date.now() - startTime,
436
+ usage: urlResult.usage
437
+ });
438
+ return result;
439
+ }
440
+ aiEventClient.emit("video:request:completed", {
441
+ requestId,
442
+ provider: adapter.name,
443
+ model: adapter.model,
444
+ requestType: "status",
445
+ jobId,
446
+ status: statusResult.status,
447
+ progress: statusResult.progress,
448
+ error: statusResult.error,
449
+ duration: Date.now() - startTime,
450
+ timestamp: Date.now()
451
+ });
452
+ if (statusResult.status === "failed") await runGenerationError(middleware, terminalContext(), {
453
+ error: new Error(statusResult.error || "Video generation failed"),
454
+ duration: Date.now() - startTime
455
+ });
456
+ return {
457
+ jobId,
458
+ status: statusResult.status,
459
+ progress: statusResult.progress,
460
+ error: statusResult.error
461
+ };
285
462
  }
463
+ /**
464
+ * Create typed options for the generateVideo() function without executing.
465
+ */
286
466
  function createVideoOptions(options) {
287
- return options;
467
+ return options;
288
468
  }
289
- export {
290
- createVideoOptions,
291
- generateVideo,
292
- getVideoJobStatus,
293
- kind
294
- };
295
- //# sourceMappingURL=index.js.map
469
+ //#endregion
470
+ export { createVideoOptions, generateVideo, getVideoJobStatus, kind };
471
+
472
+ //# sourceMappingURL=index.js.map