@warlock.js/ai-live 5.29.0 → 5.31.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/CHANGELOG.md +13 -0
- package/README.md +5 -3
- package/cjs/index.cjs +4 -4
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/realtime.contract.d.mts +1 -1
- package/esm/contracts/realtime.contract.d.mts.map +1 -1
- package/esm/contracts/video.contract.d.mts +1 -1
- package/esm/contracts/video.contract.d.mts.map +1 -1
- package/esm/index.d.mts +1 -1
- package/esm/index.d.mts.map +1 -1
- package/esm/index.mjs +1 -1
- package/esm/index.mjs.map +1 -1
- package/esm/mock/index.d.mts +1 -1
- package/esm/mock/index.d.mts.map +1 -1
- package/esm/mock/index.mjs +1 -1
- package/esm/mock/index.mjs.map +1 -1
- package/esm/realtime/realtime.d.mts +1 -1
- package/esm/realtime/realtime.d.mts.map +1 -1
- package/esm/realtime/realtime.mjs +1 -1
- package/esm/realtime/realtime.mjs.map +1 -1
- package/esm/video/video.d.mts +1 -1
- package/esm/video/video.d.mts.map +1 -1
- package/esm/video/video.mjs +1 -1
- package/esm/video/video.mjs.map +1 -1
- package/llms-full.txt +39 -21
- package/package.json +8 -3
- package/skills/use-ai-live/SKILL.md +39 -21
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/ai-live` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.31.0 - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Lockstep release; package APIs are unchanged.
|
|
12
|
+
|
|
13
|
+
## 5.30.0 - 2026-10-16
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Lockstep release; package APIs are unchanged.
|
|
18
|
+
|
|
7
19
|
## 5.29.0 - 2026-10-01
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -75,6 +87,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
75
87
|
### Changed
|
|
76
88
|
|
|
77
89
|
- Lockstep release maintenance and dependency refresh.
|
|
90
|
+
|
|
78
91
|
## 5.20.0 - 2026-09-24
|
|
79
92
|
|
|
80
93
|
### Changed
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ import "@warlock.js/ai-live"; // side-effect: lights up ai.realtime + ai.video
|
|
|
15
15
|
## What it gives you
|
|
16
16
|
|
|
17
17
|
### `ai.realtime(options)` — duplex realtime voice sessions
|
|
18
|
+
|
|
18
19
|
A **stateful, bidirectional voice session** (OpenAI Realtime-class): stream
|
|
19
20
|
microphone audio in, stream synthesized audio + transcripts + tool-calls out,
|
|
20
21
|
with barge-in. Unlike every other `ai.*` verb (one-shot request → result), this
|
|
@@ -25,7 +26,7 @@ endpoint), push audio, and consume an async event stream until you `close()` it
|
|
|
25
26
|
|
|
26
27
|
```ts
|
|
27
28
|
const session = await ai.realtime({
|
|
28
|
-
transport: openAiRealtime({ apiKey }),
|
|
29
|
+
transport: openAiRealtime({ apiKey }), // provider transport (own adapter)
|
|
29
30
|
model: "gpt-realtime",
|
|
30
31
|
voice: "alloy",
|
|
31
32
|
instructions: "You are a friendly phone receptionist.",
|
|
@@ -40,6 +41,7 @@ const report = await session.close();
|
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
### `ai.video(params)` — text-to-video generation
|
|
44
|
+
|
|
43
45
|
Prompt → video (Sora / Veo / Kling-class). Generation is **async** (submit →
|
|
44
46
|
poll), but the adapter hides the polling, so the verb returns the same uniform
|
|
45
47
|
never-throws `{ data, error, usage, report }` envelope as `ai.image`, with
|
|
@@ -60,8 +62,8 @@ if (!error) download(data.video); // { type: "url" | "base64", ... }
|
|
|
60
62
|
- **Heavy / optional deps.** Realtime needs a WebSocket transport (`ws` is an
|
|
61
63
|
optional peer); video pulls long-poll + large-payload handling. Most apps
|
|
62
64
|
using text/image/speech shouldn't carry that weight.
|
|
63
|
-
- **Different shape.** `ai.realtime` is a
|
|
64
|
-
is a
|
|
65
|
+
- **Different shape.** `ai.realtime` is a _session_, not a request; `ai.video`
|
|
66
|
+
is a _long job_. Isolating them keeps the core verbs simple and synchronous.
|
|
65
67
|
- **Same contracts.** Both still produce a `BaseReport` (`type: "realtime"` /
|
|
66
68
|
`"video"`) and route through the shared `observe` seam, so panoptic and the
|
|
67
69
|
cost tree pick them up for free.
|
package/cjs/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
let _warlock_js_ai = require("@warlock.js/ai");
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../../packages/ai-live/src/realtime/realtime.ts
|
|
5
5
|
/**
|
|
6
6
|
* Open a live duplex voice session — the stateful primitive of
|
|
7
7
|
* `@warlock.js/ai-live`. Connects through the provided
|
|
@@ -58,7 +58,7 @@ async function realtime(options) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
//#endregion
|
|
61
|
-
//#region
|
|
61
|
+
//#region ../../packages/ai-live/src/video/video.ts
|
|
62
62
|
/**
|
|
63
63
|
* Generate a video from a text prompt — the moving-image verb of the
|
|
64
64
|
* output-modality track. The adapter hides the provider's submit→poll
|
|
@@ -172,7 +172,7 @@ function toMessage(thrown) {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
//#endregion
|
|
175
|
-
//#region
|
|
175
|
+
//#region ../../packages/ai-live/src/mock/index.ts
|
|
176
176
|
/** Deterministic {@link VideoModelContract} double for tests — no HTTP, no polling. */
|
|
177
177
|
var MockVideoModel = class {
|
|
178
178
|
constructor(name, responses, pricing) {
|
|
@@ -244,7 +244,7 @@ var MockRealtimeTransport = class {
|
|
|
244
244
|
};
|
|
245
245
|
|
|
246
246
|
//#endregion
|
|
247
|
-
//#region
|
|
247
|
+
//#region ../../packages/ai-live/src/index.ts
|
|
248
248
|
_warlock_js_ai.ai.video = video;
|
|
249
249
|
_warlock_js_ai.ai.realtime = realtime;
|
|
250
250
|
|
package/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["AIError","ProviderError","REPORT_SCHEMA_VERSION"],"sources":["../../../../../../ai-live/src/realtime/realtime.ts","../../../../../../ai-live/src/video/video.ts","../../../../../../ai-live/src/mock/index.ts","../../../../../../ai-live/src/index.ts"],"sourcesContent":["import { generateRunId } from \"@warlock.js/ai\";\nimport type {\n RealtimeOptions,\n RealtimeReport,\n RealtimeSession,\n} from \"../contracts/realtime.contract\";\n\n/**\n * Open a live duplex voice session — the stateful primitive of\n * `@warlock.js/ai-live`. Connects through the provided\n * {@link RealtimeOptions.transport} (an OpenAI Realtime WebSocket\n * adapter, a mock in tests), then hands back a {@link RealtimeSession}\n * you drive: push audio/text in, consume the event stream out, and\n * `close()` to end it and receive a `type: \"realtime\"` report for the\n * cost/observability surfaces.\n *\n * Unlike the one-shot `ai.*` verbs, this returns a long-lived session —\n * its closest sibling is `ai.orchestrator`. Keeping the transport\n * pluggable is what lets the session surface ship without hard-wiring a\n * WebSocket dependency.\n *\n * @example\n * const session = await ai.realtime({ transport, model: \"gpt-realtime\", voice: \"alloy\" });\n * session.sendAudio(micChunk, \"audio/pcm\");\n * for await (const event of session.events()) {\n * if (event.type === \"audio\") speaker.write(event.base64);\n * }\n * const report = await session.close();\n */\nexport async function realtime(options: RealtimeOptions): Promise<RealtimeSession> {\n const runId = generateRunId(\"realtime\");\n const startedAt = new Date().toISOString();\n const startPerf = performance.now();\n\n const connection = await options.transport.connect({\n model: options.model,\n voice: options.voice,\n instructions: options.instructions,\n });\n\n let report: RealtimeReport | undefined;\n\n return {\n sendAudio: (base64, mediaType) => connection.sendAudio(base64, mediaType),\n sendText: (text) => connection.sendText(text),\n events: () => connection.events(),\n async close(): Promise<RealtimeReport> {\n // Idempotent — closing twice returns the first report, never\n // re-tears-down the connection.\n if (report) {\n return report;\n }\n\n await connection.close();\n\n report = {\n runId,\n rootRunId: runId,\n type: \"realtime\",\n name: options.name ?? \"realtime\",\n status: \"completed\",\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - startPerf,\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n };\n\n return report;\n },\n };\n}\n","import {\n AIError,\n computeCost,\n generateRunId,\n ProviderError,\n REPORT_SCHEMA_VERSION,\n resolveObservers,\n stampReportLineage,\n type BaseReport,\n type ExecuteResult,\n type FlowObserveOption,\n type ModelPricing,\n type Usage,\n} from \"@warlock.js/ai\";\nimport type {\n GeneratedVideo,\n VideoModelContract,\n VideoModelPricing,\n} from \"../contracts/video.contract\";\n\n/** Parameters for {@link video}. `model` comes from an adapter's `video({ name })`. */\nexport type VideoParams = {\n model: VideoModelContract;\n prompt: string;\n durationSeconds?: number;\n aspectRatio?: string;\n resolution?: string;\n negativePrompt?: string;\n signal?: AbortSignal;\n observe?: FlowObserveOption;\n sessionId?: string;\n name?: string;\n options?: Record<string, unknown>;\n};\n\n/** Success payload of a {@link video} run. */\nexport type VideoData = { video: GeneratedVideo };\n\n/** The report node a {@link video} run produces (`type: \"video\"`). */\nexport type VideoReport = BaseReport & {\n type: \"video\";\n model: { name: string; provider: string };\n /** Final clip duration in seconds, when reported. */\n durationSeconds?: number;\n};\n\n/** Result envelope of {@link video} — the uniform `{ data, error, usage, report }`. */\nexport type VideoResult = ExecuteResult<VideoData> & { type: \"video\"; report: VideoReport };\n\n/**\n * Generate a video from a text prompt — the moving-image verb of the\n * output-modality track. The adapter hides the provider's submit→poll\n * job, so this returns the framework's uniform never-throws envelope:\n *\n * - **Never throws.** Provider failures surface as a typed `AIError`.\n * - **Cost-truth.** `usage.cost` is filled per-second (Sora / Veo) or\n * per-token, folding into the same `Usage.cost` rollup as everything else.\n * - **Observable.** The completed {@link VideoReport} routes to any\n * registered `Observer` via the shared `observe` seam.\n *\n * @example\n * const { data, error } = await ai.video({\n * model: sora.video({ name: \"sora-2\", pricing: { perSecond: 0.1 } }),\n * prompt: \"a timelapse of a city skyline at dusk, cinematic\",\n * durationSeconds: 8,\n * });\n * if (!error) download(data.video);\n */\nexport async function video(params: VideoParams): Promise<VideoResult> {\n const { model, prompt } = params;\n\n const runId = generateRunId(\"video\");\n const startedAt = new Date().toISOString();\n const startPerf = performance.now();\n\n const usage: Usage = { input: 0, output: 0, total: 0 };\n let data: VideoData | undefined;\n let error: AIError | undefined;\n let status: VideoReport[\"status\"] = \"completed\";\n let durationSeconds: number | undefined;\n\n try {\n const response = await model.generate(prompt, {\n durationSeconds: params.durationSeconds,\n aspectRatio: params.aspectRatio,\n resolution: params.resolution,\n negativePrompt: params.negativePrompt,\n signal: params.signal,\n ...params.options,\n });\n\n Object.assign(usage, response.usage);\n durationSeconds = response.durationSeconds;\n\n if (usage.cost === undefined) {\n const cost = computeVideoCost(usage, durationSeconds, model.pricing);\n if (cost !== undefined) {\n usage.cost = cost;\n }\n }\n\n data = { video: response.video };\n } catch (thrown) {\n error =\n thrown instanceof AIError ? thrown : new ProviderError(toMessage(thrown), { cause: thrown });\n status = params.signal?.aborted ? \"cancelled\" : \"failed\";\n }\n\n const report: VideoReport = {\n runId,\n rootRunId: runId,\n name: params.name ?? \"video\",\n type: \"video\",\n status,\n error,\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - startPerf,\n usage,\n children: [],\n model: { name: model.name, provider: model.provider },\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n };\n\n stampReportLineage(report, { rootRunId: runId, sessionId: params.sessionId });\n\n for (const observer of resolveObservers(params.observe)) {\n try {\n await observer.collect(report);\n } catch {\n // Isolate observer failures — never break the run.\n }\n }\n\n return { type: \"video\", data, error, usage, report };\n}\n\n/**\n * Price a video run: `perSecond × durationSeconds` (per-second metering,\n * attributed to `cost.output`) wins when configured, otherwise the\n * standard token math. Returns `undefined` when no usable pricing is\n * present.\n */\nfunction computeVideoCost(\n usage: Usage,\n durationSeconds: number | undefined,\n pricing: VideoModelPricing | undefined,\n): ModelPricing | undefined {\n if (!pricing) {\n return undefined;\n }\n\n if (pricing.perSecond !== undefined) {\n if (durationSeconds === undefined) {\n return undefined;\n }\n return { input: 0, output: durationSeconds * pricing.perSecond };\n }\n\n if (pricing.input !== undefined && pricing.output !== undefined) {\n return computeCost(usage, { input: pricing.input, output: pricing.output });\n }\n\n return undefined;\n}\n\n/** Best-effort message for a non-`AIError` thrown value. */\nfunction toMessage(thrown: unknown): string {\n return thrown instanceof Error ? thrown.message : String(thrown);\n}\n","import type { Usage } from \"@warlock.js/ai\";\nimport type {\n RealtimeConnectConfig,\n RealtimeConnection,\n RealtimeEvent,\n RealtimeTransport,\n} from \"../contracts/realtime.contract\";\nimport type {\n GeneratedVideo,\n VideoGenerationResponse,\n VideoModelContract,\n VideoModelPricing,\n VideoOptions,\n} from \"../contracts/video.contract\";\n\n/** One scripted response for a {@link MockVideoModel}. */\nexport type MockVideoResponse = {\n video?: GeneratedVideo;\n usage?: Usage;\n durationSeconds?: number;\n error?: Error;\n};\n\n/** Deterministic {@link VideoModelContract} double for tests — no HTTP, no polling. */\nexport class MockVideoModel implements VideoModelContract {\n public readonly provider = \"mock\";\n public readonly calls: { prompt: string; options: VideoOptions | undefined }[] = [];\n\n private callIndex = 0;\n\n public constructor(\n public readonly name: string,\n private readonly responses: MockVideoResponse[],\n public readonly pricing?: VideoModelPricing,\n ) {}\n\n public async generate(\n prompt: string,\n options?: VideoOptions,\n ): Promise<VideoGenerationResponse> {\n this.calls.push({ prompt, options });\n\n const response = this.responses[Math.min(this.callIndex, this.responses.length - 1)] ?? {};\n this.callIndex += 1;\n\n if (response.error) {\n throw response.error;\n }\n\n return {\n video: response.video ?? { type: \"url\", url: \"https://mock/video.mp4\", mediaType: \"video/mp4\" },\n usage: response.usage ?? { input: 0, output: 0, total: 0 },\n durationSeconds: response.durationSeconds ?? options?.durationSeconds ?? 5,\n };\n }\n}\n\n/** A recording {@link RealtimeConnection} the mock transport hands back. */\nexport class MockRealtimeConnection implements RealtimeConnection {\n public readonly sentAudio: { base64: string; mediaType: string }[] = [];\n public readonly sentText: string[] = [];\n public closed = false;\n\n public constructor(private readonly scripted: RealtimeEvent[]) {}\n\n public sendAudio(base64: string, mediaType: string): void {\n this.sentAudio.push({ base64, mediaType });\n }\n\n public sendText(text: string): void {\n this.sentText.push(text);\n }\n\n public async *events(): AsyncIterable<RealtimeEvent> {\n for (const event of this.scripted) {\n yield event;\n }\n }\n\n public async close(): Promise<void> {\n this.closed = true;\n }\n}\n\n/** Deterministic {@link RealtimeTransport} double — scripts the event stream, records sends. */\nexport class MockRealtimeTransport implements RealtimeTransport {\n public readonly connectConfigs: RealtimeConnectConfig[] = [];\n public lastConnection?: MockRealtimeConnection;\n\n public constructor(private readonly scriptedEvents: RealtimeEvent[] = []) {}\n\n public async connect(config: RealtimeConnectConfig): Promise<RealtimeConnection> {\n this.connectConfigs.push(config);\n this.lastConnection = new MockRealtimeConnection(this.scriptedEvents);\n return this.lastConnection;\n }\n}\n","import { ai } from \"@warlock.js/ai\";\nimport { realtime } from \"./realtime/realtime\";\nimport { video } from \"./video/video\";\n\n/**\n * Augment the shared `Ai` facade with this package's two heavy\n * modalities. Importing `@warlock.js/ai-live` mounts `ai.video` +\n * `ai.realtime` as a side effect, mirroring how `@warlock.js/ai-tools`\n * mounts `ai.mcp` / `ai.tools` and `@warlock.js/ai-workspace` mounts\n * `ai.workspace`.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /**\n * Text-to-video generation (Sora / Veo / Kling-class). Async under\n * the hood (submit→poll), surfaced as the uniform never-throws\n * `{ data, error, usage, report }` envelope. Ships in `@warlock.js/ai-live`.\n */\n video: typeof video;\n /**\n * Live duplex voice sessions (OpenAI Realtime-class). A stateful\n * session over a pluggable transport — push audio/text in, consume\n * events out, `close()` for a report. Ships in `@warlock.js/ai-live`.\n */\n realtime: typeof realtime;\n }\n}\n\nai.video = video;\nai.realtime = realtime;\n\nexport { realtime, video };\nexport type { VideoData, VideoParams, VideoReport, VideoResult } from \"./video/video\";\nexport * from \"./contracts/video.contract\";\nexport * from \"./contracts/realtime.contract\";\nexport * from \"./mock\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["AIError","ProviderError","REPORT_SCHEMA_VERSION"],"sources":["../../../../../../../packages/ai-live/src/realtime/realtime.ts","../../../../../../../packages/ai-live/src/video/video.ts","../../../../../../../packages/ai-live/src/mock/index.ts","../../../../../../../packages/ai-live/src/index.ts"],"sourcesContent":["import { generateRunId } from \"@warlock.js/ai\";\nimport type {\n RealtimeOptions,\n RealtimeReport,\n RealtimeSession,\n} from \"../contracts/realtime.contract\";\n\n/**\n * Open a live duplex voice session — the stateful primitive of\n * `@warlock.js/ai-live`. Connects through the provided\n * {@link RealtimeOptions.transport} (an OpenAI Realtime WebSocket\n * adapter, a mock in tests), then hands back a {@link RealtimeSession}\n * you drive: push audio/text in, consume the event stream out, and\n * `close()` to end it and receive a `type: \"realtime\"` report for the\n * cost/observability surfaces.\n *\n * Unlike the one-shot `ai.*` verbs, this returns a long-lived session —\n * its closest sibling is `ai.orchestrator`. Keeping the transport\n * pluggable is what lets the session surface ship without hard-wiring a\n * WebSocket dependency.\n *\n * @example\n * const session = await ai.realtime({ transport, model: \"gpt-realtime\", voice: \"alloy\" });\n * session.sendAudio(micChunk, \"audio/pcm\");\n * for await (const event of session.events()) {\n * if (event.type === \"audio\") speaker.write(event.base64);\n * }\n * const report = await session.close();\n */\nexport async function realtime(options: RealtimeOptions): Promise<RealtimeSession> {\n const runId = generateRunId(\"realtime\");\n const startedAt = new Date().toISOString();\n const startPerf = performance.now();\n\n const connection = await options.transport.connect({\n model: options.model,\n voice: options.voice,\n instructions: options.instructions,\n });\n\n let report: RealtimeReport | undefined;\n\n return {\n sendAudio: (base64, mediaType) => connection.sendAudio(base64, mediaType),\n sendText: (text) => connection.sendText(text),\n events: () => connection.events(),\n async close(): Promise<RealtimeReport> {\n // Idempotent — closing twice returns the first report, never\n // re-tears-down the connection.\n if (report) {\n return report;\n }\n\n await connection.close();\n\n report = {\n runId,\n rootRunId: runId,\n type: \"realtime\",\n name: options.name ?? \"realtime\",\n status: \"completed\",\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - startPerf,\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n };\n\n return report;\n },\n };\n}\n","import {\n AIError,\n computeCost,\n generateRunId,\n ProviderError,\n REPORT_SCHEMA_VERSION,\n resolveObservers,\n stampReportLineage,\n type BaseReport,\n type ExecuteResult,\n type FlowObserveOption,\n type ModelPricing,\n type Usage,\n} from \"@warlock.js/ai\";\nimport type {\n GeneratedVideo,\n VideoModelContract,\n VideoModelPricing,\n} from \"../contracts/video.contract\";\n\n/** Parameters for {@link video}. `model` comes from an adapter's `video({ name })`. */\nexport type VideoParams = {\n model: VideoModelContract;\n prompt: string;\n durationSeconds?: number;\n aspectRatio?: string;\n resolution?: string;\n negativePrompt?: string;\n signal?: AbortSignal;\n observe?: FlowObserveOption;\n sessionId?: string;\n name?: string;\n options?: Record<string, unknown>;\n};\n\n/** Success payload of a {@link video} run. */\nexport type VideoData = { video: GeneratedVideo };\n\n/** The report node a {@link video} run produces (`type: \"video\"`). */\nexport type VideoReport = BaseReport & {\n type: \"video\";\n model: { name: string; provider: string };\n /** Final clip duration in seconds, when reported. */\n durationSeconds?: number;\n};\n\n/** Result envelope of {@link video} — the uniform `{ data, error, usage, report }`. */\nexport type VideoResult = ExecuteResult<VideoData> & { type: \"video\"; report: VideoReport };\n\n/**\n * Generate a video from a text prompt — the moving-image verb of the\n * output-modality track. The adapter hides the provider's submit→poll\n * job, so this returns the framework's uniform never-throws envelope:\n *\n * - **Never throws.** Provider failures surface as a typed `AIError`.\n * - **Cost-truth.** `usage.cost` is filled per-second (Sora / Veo) or\n * per-token, folding into the same `Usage.cost` rollup as everything else.\n * - **Observable.** The completed {@link VideoReport} routes to any\n * registered `Observer` via the shared `observe` seam.\n *\n * @example\n * const { data, error } = await ai.video({\n * model: sora.video({ name: \"sora-2\", pricing: { perSecond: 0.1 } }),\n * prompt: \"a timelapse of a city skyline at dusk, cinematic\",\n * durationSeconds: 8,\n * });\n * if (!error) download(data.video);\n */\nexport async function video(params: VideoParams): Promise<VideoResult> {\n const { model, prompt } = params;\n\n const runId = generateRunId(\"video\");\n const startedAt = new Date().toISOString();\n const startPerf = performance.now();\n\n const usage: Usage = { input: 0, output: 0, total: 0 };\n let data: VideoData | undefined;\n let error: AIError | undefined;\n let status: VideoReport[\"status\"] = \"completed\";\n let durationSeconds: number | undefined;\n\n try {\n const response = await model.generate(prompt, {\n durationSeconds: params.durationSeconds,\n aspectRatio: params.aspectRatio,\n resolution: params.resolution,\n negativePrompt: params.negativePrompt,\n signal: params.signal,\n ...params.options,\n });\n\n Object.assign(usage, response.usage);\n durationSeconds = response.durationSeconds;\n\n if (usage.cost === undefined) {\n const cost = computeVideoCost(usage, durationSeconds, model.pricing);\n if (cost !== undefined) {\n usage.cost = cost;\n }\n }\n\n data = { video: response.video };\n } catch (thrown) {\n error =\n thrown instanceof AIError ? thrown : new ProviderError(toMessage(thrown), { cause: thrown });\n status = params.signal?.aborted ? \"cancelled\" : \"failed\";\n }\n\n const report: VideoReport = {\n runId,\n rootRunId: runId,\n name: params.name ?? \"video\",\n type: \"video\",\n status,\n error,\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - startPerf,\n usage,\n children: [],\n model: { name: model.name, provider: model.provider },\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n };\n\n stampReportLineage(report, { rootRunId: runId, sessionId: params.sessionId });\n\n for (const observer of resolveObservers(params.observe)) {\n try {\n await observer.collect(report);\n } catch {\n // Isolate observer failures — never break the run.\n }\n }\n\n return { type: \"video\", data, error, usage, report };\n}\n\n/**\n * Price a video run: `perSecond × durationSeconds` (per-second metering,\n * attributed to `cost.output`) wins when configured, otherwise the\n * standard token math. Returns `undefined` when no usable pricing is\n * present.\n */\nfunction computeVideoCost(\n usage: Usage,\n durationSeconds: number | undefined,\n pricing: VideoModelPricing | undefined,\n): ModelPricing | undefined {\n if (!pricing) {\n return undefined;\n }\n\n if (pricing.perSecond !== undefined) {\n if (durationSeconds === undefined) {\n return undefined;\n }\n return { input: 0, output: durationSeconds * pricing.perSecond };\n }\n\n if (pricing.input !== undefined && pricing.output !== undefined) {\n return computeCost(usage, { input: pricing.input, output: pricing.output });\n }\n\n return undefined;\n}\n\n/** Best-effort message for a non-`AIError` thrown value. */\nfunction toMessage(thrown: unknown): string {\n return thrown instanceof Error ? thrown.message : String(thrown);\n}\n","import type { Usage } from \"@warlock.js/ai\";\nimport type {\n RealtimeConnectConfig,\n RealtimeConnection,\n RealtimeEvent,\n RealtimeTransport,\n} from \"../contracts/realtime.contract\";\nimport type {\n GeneratedVideo,\n VideoGenerationResponse,\n VideoModelContract,\n VideoModelPricing,\n VideoOptions,\n} from \"../contracts/video.contract\";\n\n/** One scripted response for a {@link MockVideoModel}. */\nexport type MockVideoResponse = {\n video?: GeneratedVideo;\n usage?: Usage;\n durationSeconds?: number;\n error?: Error;\n};\n\n/** Deterministic {@link VideoModelContract} double for tests — no HTTP, no polling. */\nexport class MockVideoModel implements VideoModelContract {\n public readonly provider = \"mock\";\n public readonly calls: { prompt: string; options: VideoOptions | undefined }[] = [];\n\n private callIndex = 0;\n\n public constructor(\n public readonly name: string,\n private readonly responses: MockVideoResponse[],\n public readonly pricing?: VideoModelPricing,\n ) {}\n\n public async generate(prompt: string, options?: VideoOptions): Promise<VideoGenerationResponse> {\n this.calls.push({ prompt, options });\n\n const response = this.responses[Math.min(this.callIndex, this.responses.length - 1)] ?? {};\n this.callIndex += 1;\n\n if (response.error) {\n throw response.error;\n }\n\n return {\n video: response.video ?? {\n type: \"url\",\n url: \"https://mock/video.mp4\",\n mediaType: \"video/mp4\",\n },\n usage: response.usage ?? { input: 0, output: 0, total: 0 },\n durationSeconds: response.durationSeconds ?? options?.durationSeconds ?? 5,\n };\n }\n}\n\n/** A recording {@link RealtimeConnection} the mock transport hands back. */\nexport class MockRealtimeConnection implements RealtimeConnection {\n public readonly sentAudio: { base64: string; mediaType: string }[] = [];\n public readonly sentText: string[] = [];\n public closed = false;\n\n public constructor(private readonly scripted: RealtimeEvent[]) {}\n\n public sendAudio(base64: string, mediaType: string): void {\n this.sentAudio.push({ base64, mediaType });\n }\n\n public sendText(text: string): void {\n this.sentText.push(text);\n }\n\n public async *events(): AsyncIterable<RealtimeEvent> {\n for (const event of this.scripted) {\n yield event;\n }\n }\n\n public async close(): Promise<void> {\n this.closed = true;\n }\n}\n\n/** Deterministic {@link RealtimeTransport} double — scripts the event stream, records sends. */\nexport class MockRealtimeTransport implements RealtimeTransport {\n public readonly connectConfigs: RealtimeConnectConfig[] = [];\n public lastConnection?: MockRealtimeConnection;\n\n public constructor(private readonly scriptedEvents: RealtimeEvent[] = []) {}\n\n public async connect(config: RealtimeConnectConfig): Promise<RealtimeConnection> {\n this.connectConfigs.push(config);\n this.lastConnection = new MockRealtimeConnection(this.scriptedEvents);\n return this.lastConnection;\n }\n}\n","import { ai } from \"@warlock.js/ai\";\nimport { realtime } from \"./realtime/realtime\";\nimport { video } from \"./video/video\";\n\n/**\n * Augment the shared `Ai` facade with this package's two heavy\n * modalities. Importing `@warlock.js/ai-live` mounts `ai.video` +\n * `ai.realtime` as a side effect, mirroring how `@warlock.js/ai-tools`\n * mounts `ai.mcp` / `ai.tools` and `@warlock.js/ai-workspace` mounts\n * `ai.workspace`.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /**\n * Text-to-video generation (Sora / Veo / Kling-class). Async under\n * the hood (submit→poll), surfaced as the uniform never-throws\n * `{ data, error, usage, report }` envelope. Ships in `@warlock.js/ai-live`.\n */\n video: typeof video;\n /**\n * Live duplex voice sessions (OpenAI Realtime-class). A stateful\n * session over a pluggable transport — push audio/text in, consume\n * events out, `close()` for a report. Ships in `@warlock.js/ai-live`.\n */\n realtime: typeof realtime;\n }\n}\n\nai.video = video;\nai.realtime = realtime;\n\nexport { realtime, video };\nexport type { VideoData, VideoParams, VideoReport, VideoResult } from \"./video/video\";\nexport * from \"./contracts/video.contract\";\nexport * from \"./contracts/realtime.contract\";\nexport * from \"./mock\";\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AIError } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-live/src/contracts/realtime.contract.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* One event streamed out of a realtime voice session. A closed union so
|
|
6
6
|
* consumers can `switch` exhaustively over the duplex output.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.contract.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"realtime.contract.d.mts","names":[],"sources":["../../../../../../../../packages/ai-live/src/contracts/realtime.contract.ts"],"mappings":";;;;;AAYA;;;;;;;;KAAY,aAAA;EACN,IAAA;EAAe,MAAA;EAAgB,SAAA;AAAA;EAC/B,IAAA;EAAoB,IAAA;EAA4B,IAAA;EAAc,KAAA;AAAA;EAC9D,IAAA;EAAmB,EAAA;EAAY,IAAA;EAAc,KAAA;AAAA;EAC7C,IAAA;EAAe,KAAA,EAAO,OAAO;AAAA;EAC7B,IAAA;AAAA;;KAGM,qBAAA;EAgBK,iDAdf,KAAA;EAEA,KAAA,WAkBU;EAhBV,YAAA;AAAA;;;;;;;;UAUe,kBAAA;EAMS;EAJxB,SAAA,CAAU,MAAA,UAAgB,SAAA;EAMjB;EAJT,QAAA,CAAS,IAAA;EAIO;EAFhB,MAAA,IAAU,aAAA,CAAc,aAAA;EAMQ;EAJhC,KAAA,IAAS,OAAA;AAAA;;UAIM,iBAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,qBAAA,GAAwB,OAAA,CAAQ,kBAAA;AAAA;;KAItC,eAAA;EAJF,0EAMR,SAAA,EAAW,iBAAiB,EANoB;EAQhD,KAAA,UARkE;EAUlE,KAAA,WANyB;EAQzB,YAAA,WAN4B;EAQ5B,SAAA,WARW;EAUX,IAAA;AAAA;;;;;AAAI;AASN;UAAiB,eAAA;;EAEf,SAAA,CAAU,MAAA,UAAgB,SAAA;EAIhB;EAFV,QAAA,CAAS,IAAA;EAIA;EAFT,MAAA,IAAU,aAAA,CAAc,aAAA;EAER;EAAhB,KAAA,IAAS,OAAA,CAAQ,cAAA;AAAA;;;;;;KAQP,cAAA;EACV,KAAA;EACA,SAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;AAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Usage } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-live/src/contracts/video.contract.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* USD pricing for a text-to-video model. Video providers meter per
|
|
6
6
|
* second of generated video (Sora / Veo / Kling); a token channel is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.contract.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"video.contract.d.mts","names":[],"sources":["../../../../../../../../packages/ai-live/src/contracts/video.contract.ts"],"mappings":";;;;;AAWA;;;;;;;KAAY,iBAAA;EAMJ,qEAJN,SAAA,WAYwB;EAVxB,KAAA,WAUwB;EARxB,MAAA;AAAA;;;;;;KAQU,cAAA;EACN,IAAA;EAAa,GAAA;EAAa,SAAA;AAAA;EAC1B,IAAA;EAAgB,MAAA;EAAgB,SAAA;AAAA;;KAG1B,YAAA;EAYT,wCAVD,eAAA,WAUY;EARZ,WAAA,WAiBiC;EAfjC,UAAA,WAkBY;EAhBZ,cAAA,WAcO;EAZP,MAAA,GAAS,WAAW,EAcb;EAAA,CAZN,GAAA;AAAA;AAcc;AASjB;;;;;AATiB,KALL,uBAAA;EACV,KAAA,EAAO,cAAA,EAiBkD;EAfzD,KAAA,EAAO,KAAK,EAYH;EAVT,eAAA;AAAA;;;;;;;UASe,kBAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,iBAAA;EACnB,QAAA,CAAS,MAAA,UAAgB,OAAA,GAAU,YAAA,GAAe,OAAA,CAAQ,uBAAA;AAAA;;KAIhD,gBAAA;EACV,IAAA;EACA,OAAA,GAAU,iBAAiB;EAAA,CAC1B,GAAA;AAAA"}
|
package/esm/index.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { GeneratedVideo, VideoGenerationResponse, VideoModelConfig, VideoModelCo
|
|
|
4
4
|
import { VideoData, VideoParams, VideoReport, VideoResult, video } from "./video/video.mjs";
|
|
5
5
|
import { MockRealtimeConnection, MockRealtimeTransport, MockVideoModel, MockVideoResponse } from "./mock/index.mjs";
|
|
6
6
|
|
|
7
|
-
//#region
|
|
7
|
+
//#region ../../packages/ai-live/src/index.d.ts
|
|
8
8
|
/**
|
|
9
9
|
* Augment the shared `Ai` facade with this package's two heavy
|
|
10
10
|
* modalities. Importing `@warlock.js/ai-live` mounts `ai.video` +
|
package/esm/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../packages/ai-live/src/index.ts"],"mappings":";;;;;;;;;;;;AAEsC;;;YAU1B,EAAA;IAAA;;;;;IAMR,KAAA,SAAc,KAAA;IAMW;AAAA;;;;IAAzB,QAAA,SAAiB,QAAQ;EAAA;AAAA"}
|
package/esm/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { video } from "./video/video.mjs";
|
|
|
3
3
|
import { MockRealtimeConnection, MockRealtimeTransport, MockVideoModel } from "./mock/index.mjs";
|
|
4
4
|
import { ai } from "@warlock.js/ai";
|
|
5
5
|
|
|
6
|
-
//#region
|
|
6
|
+
//#region ../../packages/ai-live/src/index.ts
|
|
7
7
|
ai.video = video;
|
|
8
8
|
ai.realtime = realtime;
|
|
9
9
|
|
package/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../packages/ai-live/src/index.ts"],"sourcesContent":["import { ai } from \"@warlock.js/ai\";\nimport { realtime } from \"./realtime/realtime\";\nimport { video } from \"./video/video\";\n\n/**\n * Augment the shared `Ai` facade with this package's two heavy\n * modalities. Importing `@warlock.js/ai-live` mounts `ai.video` +\n * `ai.realtime` as a side effect, mirroring how `@warlock.js/ai-tools`\n * mounts `ai.mcp` / `ai.tools` and `@warlock.js/ai-workspace` mounts\n * `ai.workspace`.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /**\n * Text-to-video generation (Sora / Veo / Kling-class). Async under\n * the hood (submit→poll), surfaced as the uniform never-throws\n * `{ data, error, usage, report }` envelope. Ships in `@warlock.js/ai-live`.\n */\n video: typeof video;\n /**\n * Live duplex voice sessions (OpenAI Realtime-class). A stateful\n * session over a pluggable transport — push audio/text in, consume\n * events out, `close()` for a report. Ships in `@warlock.js/ai-live`.\n */\n realtime: typeof realtime;\n }\n}\n\nai.video = video;\nai.realtime = realtime;\n\nexport { realtime, video };\nexport type { VideoData, VideoParams, VideoReport, VideoResult } from \"./video/video\";\nexport * from \"./contracts/video.contract\";\nexport * from \"./contracts/realtime.contract\";\nexport * from \"./mock\";\n"],"mappings":";;;;;;AA4BA,GAAG,QAAQ;AACX,GAAG,WAAW"}
|
package/esm/mock/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { RealtimeConnectConfig, RealtimeConnection, RealtimeEvent, RealtimeTrans
|
|
|
2
2
|
import { GeneratedVideo, VideoGenerationResponse, VideoModelContract, VideoModelPricing, VideoOptions } from "../contracts/video.contract.mjs";
|
|
3
3
|
import { Usage } from "@warlock.js/ai";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../../packages/ai-live/src/mock/index.d.ts
|
|
6
6
|
/** One scripted response for a {@link MockVideoModel}. */
|
|
7
7
|
type MockVideoResponse = {
|
|
8
8
|
video?: GeneratedVideo;
|
package/esm/mock/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../../packages/ai-live/src/mock/index.ts"],"mappings":";;;;;;KAgBY,iBAAA;EACV,KAAA,GAAQ,cAAA;EACR,KAAA,GAAQ,KAAA;EACR,eAAA;EACA,KAAA,GAAQ,KAAA;AAAA;;cAIG,cAAA,YAA0B,kBAAA;EAAA,SAOnB,IAAA;EAAA,iBACC,SAAA;EAAA,SACD,OAAA,GAAU,iBAAA;EAAA,SARZ,QAAA;EAAA,SACA,KAAA;IAAS,MAAA;IAAgB,OAAA,EAAS,YAAA;EAAA;EAAA,QAE1C,SAAA;cAGU,IAAA,UACC,SAAA,EAAW,iBAAA,IACZ,OAAA,GAAU,iBAAA;EAGf,QAAA,CAAS,MAAA,UAAgB,OAAA,GAAU,YAAA,GAAe,OAAA,CAAQ,uBAAA;AAAA;;cAuB5D,sBAAA,YAAkC,kBAAA;EAAA,iBAKT,QAAA;EAAA,SAJpB,SAAA;IAAa,MAAA;IAAgB,SAAA;EAAA;EAAA,SAC7B,QAAA;EACT,MAAA;cAE6B,QAAA,EAAU,aAAA;EAEvC,SAAA,CAAU,MAAA,UAAgB,SAAA;EAI1B,QAAA,CAAS,IAAA;EAIF,MAAA,IAAU,aAAA,CAAc,aAAA;EAMzB,KAAA,IAAS,OAAA;AAAA;;cAMX,qBAAA,YAAiC,iBAAA;EAAA,iBAIR,cAAA;EAAA,SAHpB,cAAA,EAAgB,qBAAA;EACzB,cAAA,GAAiB,sBAAA;cAEY,cAAA,GAAgB,aAAA;EAEvC,OAAA,CAAQ,MAAA,EAAQ,qBAAA,GAAwB,OAAA,CAAQ,kBAAA;AAAA"}
|
package/esm/mock/index.mjs
CHANGED
package/esm/mock/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../packages/ai-live/src/mock/index.ts"],"sourcesContent":["import type { Usage } from \"@warlock.js/ai\";\nimport type {\n RealtimeConnectConfig,\n RealtimeConnection,\n RealtimeEvent,\n RealtimeTransport,\n} from \"../contracts/realtime.contract\";\nimport type {\n GeneratedVideo,\n VideoGenerationResponse,\n VideoModelContract,\n VideoModelPricing,\n VideoOptions,\n} from \"../contracts/video.contract\";\n\n/** One scripted response for a {@link MockVideoModel}. */\nexport type MockVideoResponse = {\n video?: GeneratedVideo;\n usage?: Usage;\n durationSeconds?: number;\n error?: Error;\n};\n\n/** Deterministic {@link VideoModelContract} double for tests — no HTTP, no polling. */\nexport class MockVideoModel implements VideoModelContract {\n public readonly provider = \"mock\";\n public readonly calls: { prompt: string; options: VideoOptions | undefined }[] = [];\n\n private callIndex = 0;\n\n public constructor(\n public readonly name: string,\n private readonly responses: MockVideoResponse[],\n public readonly pricing?: VideoModelPricing,\n ) {}\n\n public async generate(prompt: string, options?: VideoOptions): Promise<VideoGenerationResponse> {\n this.calls.push({ prompt, options });\n\n const response = this.responses[Math.min(this.callIndex, this.responses.length - 1)] ?? {};\n this.callIndex += 1;\n\n if (response.error) {\n throw response.error;\n }\n\n return {\n video: response.video ?? {\n type: \"url\",\n url: \"https://mock/video.mp4\",\n mediaType: \"video/mp4\",\n },\n usage: response.usage ?? { input: 0, output: 0, total: 0 },\n durationSeconds: response.durationSeconds ?? options?.durationSeconds ?? 5,\n };\n }\n}\n\n/** A recording {@link RealtimeConnection} the mock transport hands back. */\nexport class MockRealtimeConnection implements RealtimeConnection {\n public readonly sentAudio: { base64: string; mediaType: string }[] = [];\n public readonly sentText: string[] = [];\n public closed = false;\n\n public constructor(private readonly scripted: RealtimeEvent[]) {}\n\n public sendAudio(base64: string, mediaType: string): void {\n this.sentAudio.push({ base64, mediaType });\n }\n\n public sendText(text: string): void {\n this.sentText.push(text);\n }\n\n public async *events(): AsyncIterable<RealtimeEvent> {\n for (const event of this.scripted) {\n yield event;\n }\n }\n\n public async close(): Promise<void> {\n this.closed = true;\n }\n}\n\n/** Deterministic {@link RealtimeTransport} double — scripts the event stream, records sends. */\nexport class MockRealtimeTransport implements RealtimeTransport {\n public readonly connectConfigs: RealtimeConnectConfig[] = [];\n public lastConnection?: MockRealtimeConnection;\n\n public constructor(private readonly scriptedEvents: RealtimeEvent[] = []) {}\n\n public async connect(config: RealtimeConnectConfig): Promise<RealtimeConnection> {\n this.connectConfigs.push(config);\n this.lastConnection = new MockRealtimeConnection(this.scriptedEvents);\n return this.lastConnection;\n }\n}\n"],"mappings":";;AAwBA,IAAa,iBAAb,MAA0D;CAMxD,AAAO,YACL,AAAgB,MAChB,AAAiB,WACjB,AAAgB,SAChB;EAHgB;EACC;EACD;kBARS;eACsD,CAAC;mBAE9D;CAMjB;CAEH,MAAa,SAAS,QAAgB,SAA0D;EAC9F,KAAK,MAAM,KAAK;GAAE;GAAQ;EAAQ,CAAC;EAEnC,MAAM,WAAW,KAAK,UAAU,KAAK,IAAI,KAAK,WAAW,KAAK,UAAU,SAAS,CAAC,MAAM,CAAC;EACzF,KAAK,aAAa;EAElB,IAAI,SAAS,OACX,MAAM,SAAS;EAGjB,OAAO;GACL,OAAO,SAAS,SAAS;IACvB,MAAM;IACN,KAAK;IACL,WAAW;GACb;GACA,OAAO,SAAS,SAAS;IAAE,OAAO;IAAG,QAAQ;IAAG,OAAO;GAAE;GACzD,iBAAiB,SAAS,mBAAmB,SAAS,mBAAmB;EAC3E;CACF;AACF;;AAGA,IAAa,yBAAb,MAAkE;CAKhE,AAAO,YAAY,AAAiB,UAA2B;EAA3B;mBAJiC,CAAC;kBACjC,CAAC;gBACtB;CAEgD;CAEhE,AAAO,UAAU,QAAgB,WAAyB;EACxD,KAAK,UAAU,KAAK;GAAE;GAAQ;EAAU,CAAC;CAC3C;CAEA,AAAO,SAAS,MAAoB;EAClC,KAAK,SAAS,KAAK,IAAI;CACzB;CAEA,OAAc,SAAuC;EACnD,KAAK,MAAM,SAAS,KAAK,UACvB,MAAM;CAEV;CAEA,MAAa,QAAuB;EAClC,KAAK,SAAS;CAChB;AACF;;AAGA,IAAa,wBAAb,MAAgE;CAI9D,AAAO,YAAY,AAAiB,iBAAkC,CAAC,GAAG;EAAtC;wBAHsB,CAAC;CAGgB;CAE3E,MAAa,QAAQ,QAA4D;EAC/E,KAAK,eAAe,KAAK,MAAM;EAC/B,KAAK,iBAAiB,IAAI,uBAAuB,KAAK,cAAc;EACpE,OAAO,KAAK;CACd;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RealtimeOptions, RealtimeSession } from "../contracts/realtime.contract.mjs";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-live/src/realtime/realtime.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Open a live duplex voice session — the stateful primitive of
|
|
6
6
|
* `@warlock.js/ai-live`. Connects through the provided
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"realtime.d.mts","names":[],"sources":["../../../../../../../../packages/ai-live/src/realtime/realtime.ts"],"mappings":";;;;;AA6BA;;;;;;;;;;;;;AAAiF;;;;;;;iBAA3D,QAAA,CAAS,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,eAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { generateRunId } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-live/src/realtime/realtime.ts
|
|
4
4
|
/**
|
|
5
5
|
* Open a live duplex voice session — the stateful primitive of
|
|
6
6
|
* `@warlock.js/ai-live`. Connects through the provided
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"realtime.mjs","names":[],"sources":["../../../../../../../../packages/ai-live/src/realtime/realtime.ts"],"sourcesContent":["import { generateRunId } from \"@warlock.js/ai\";\nimport type {\n RealtimeOptions,\n RealtimeReport,\n RealtimeSession,\n} from \"../contracts/realtime.contract\";\n\n/**\n * Open a live duplex voice session — the stateful primitive of\n * `@warlock.js/ai-live`. Connects through the provided\n * {@link RealtimeOptions.transport} (an OpenAI Realtime WebSocket\n * adapter, a mock in tests), then hands back a {@link RealtimeSession}\n * you drive: push audio/text in, consume the event stream out, and\n * `close()` to end it and receive a `type: \"realtime\"` report for the\n * cost/observability surfaces.\n *\n * Unlike the one-shot `ai.*` verbs, this returns a long-lived session —\n * its closest sibling is `ai.orchestrator`. Keeping the transport\n * pluggable is what lets the session surface ship without hard-wiring a\n * WebSocket dependency.\n *\n * @example\n * const session = await ai.realtime({ transport, model: \"gpt-realtime\", voice: \"alloy\" });\n * session.sendAudio(micChunk, \"audio/pcm\");\n * for await (const event of session.events()) {\n * if (event.type === \"audio\") speaker.write(event.base64);\n * }\n * const report = await session.close();\n */\nexport async function realtime(options: RealtimeOptions): Promise<RealtimeSession> {\n const runId = generateRunId(\"realtime\");\n const startedAt = new Date().toISOString();\n const startPerf = performance.now();\n\n const connection = await options.transport.connect({\n model: options.model,\n voice: options.voice,\n instructions: options.instructions,\n });\n\n let report: RealtimeReport | undefined;\n\n return {\n sendAudio: (base64, mediaType) => connection.sendAudio(base64, mediaType),\n sendText: (text) => connection.sendText(text),\n events: () => connection.events(),\n async close(): Promise<RealtimeReport> {\n // Idempotent — closing twice returns the first report, never\n // re-tears-down the connection.\n if (report) {\n return report;\n }\n\n await connection.close();\n\n report = {\n runId,\n rootRunId: runId,\n type: \"realtime\",\n name: options.name ?? \"realtime\",\n status: \"completed\",\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - startPerf,\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n };\n\n return report;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,eAAsB,SAAS,SAAoD;CACjF,MAAM,QAAQ,cAAc,UAAU;CACtC,MAAM,6BAAY,IAAI,KAAK,EAAC,CAAC,YAAY;CACzC,MAAM,YAAY,YAAY,IAAI;CAElC,MAAM,aAAa,MAAM,QAAQ,UAAU,QAAQ;EACjD,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,cAAc,QAAQ;CACxB,CAAC;CAED,IAAI;CAEJ,OAAO;EACL,YAAY,QAAQ,cAAc,WAAW,UAAU,QAAQ,SAAS;EACxE,WAAW,SAAS,WAAW,SAAS,IAAI;EAC5C,cAAc,WAAW,OAAO;EAChC,MAAM,QAAiC;GAGrC,IAAI,QACF,OAAO;GAGT,MAAM,WAAW,MAAM;GAEvB,SAAS;IACP;IACA,WAAW;IACX,MAAM;IACN,MAAM,QAAQ,QAAQ;IACtB,QAAQ;IACR;IACA,0BAAS,IAAI,KAAK,EAAC,CAAC,YAAY;IAChC,UAAU,YAAY,IAAI,IAAI;IAC9B,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;GAC9D;GAEA,OAAO;EACT;CACF;AACF"}
|
package/esm/video/video.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GeneratedVideo, VideoModelContract } from "../contracts/video.contract.mjs";
|
|
2
2
|
import { BaseReport, ExecuteResult, FlowObserveOption } from "@warlock.js/ai";
|
|
3
3
|
|
|
4
|
-
//#region
|
|
4
|
+
//#region ../../packages/ai-live/src/video/video.d.ts
|
|
5
5
|
/** Parameters for {@link video}. `model` comes from an adapter's `video({ name })`. */
|
|
6
6
|
type VideoParams = {
|
|
7
7
|
model: VideoModelContract;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"video.d.mts","names":[],"sources":["../../../../../../../../packages/ai-live/src/video/video.ts"],"mappings":";;;;;KAqBY,WAAA;EACV,KAAA,EAAO,kBAAA;EACP,MAAA;EACA,eAAA;EACA,WAAA;EACA,UAAA;EACA,cAAA;EACA,MAAA,GAAS,WAAA;EACT,OAAA,GAAU,iBAAA;EACV,SAAA;EACA,IAAA;EACA,OAAA,GAAU,MAAA;AAAA;;KAIA,SAAA;EAAc,KAAA,EAAO,cAAc;AAAA;;KAGnC,WAAA,GAAc,UAAU;EAClC,IAAA;EACA,KAAA;IAAS,IAAA;IAAc,QAAA;EAAA,GATb;EAWV,eAAA;AAAA;AAPF;AAAA,KAWY,WAAA,GAAc,aAAA,CAAc,SAAA;EAAe,IAAA;EAAe,MAAA,EAAQ,WAAA;AAAA;AAR9E;;;;;;;;;;;AAIiB;AAIjB;;;;;;;AARA,iBA6BsB,KAAA,CAAM,MAAA,EAAQ,WAAA,GAAc,OAAA,CAAQ,WAAA"}
|
package/esm/video/video.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AIError, ProviderError, REPORT_SCHEMA_VERSION, computeCost, generateRunId, resolveObservers, stampReportLineage } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-live/src/video/video.ts
|
|
4
4
|
/**
|
|
5
5
|
* Generate a video from a text prompt — the moving-image verb of the
|
|
6
6
|
* output-modality track. The adapter hides the provider's submit→poll
|
package/esm/video/video.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"video.mjs","names":[],"sources":["../../../../../../../../packages/ai-live/src/video/video.ts"],"sourcesContent":["import {\n AIError,\n computeCost,\n generateRunId,\n ProviderError,\n REPORT_SCHEMA_VERSION,\n resolveObservers,\n stampReportLineage,\n type BaseReport,\n type ExecuteResult,\n type FlowObserveOption,\n type ModelPricing,\n type Usage,\n} from \"@warlock.js/ai\";\nimport type {\n GeneratedVideo,\n VideoModelContract,\n VideoModelPricing,\n} from \"../contracts/video.contract\";\n\n/** Parameters for {@link video}. `model` comes from an adapter's `video({ name })`. */\nexport type VideoParams = {\n model: VideoModelContract;\n prompt: string;\n durationSeconds?: number;\n aspectRatio?: string;\n resolution?: string;\n negativePrompt?: string;\n signal?: AbortSignal;\n observe?: FlowObserveOption;\n sessionId?: string;\n name?: string;\n options?: Record<string, unknown>;\n};\n\n/** Success payload of a {@link video} run. */\nexport type VideoData = { video: GeneratedVideo };\n\n/** The report node a {@link video} run produces (`type: \"video\"`). */\nexport type VideoReport = BaseReport & {\n type: \"video\";\n model: { name: string; provider: string };\n /** Final clip duration in seconds, when reported. */\n durationSeconds?: number;\n};\n\n/** Result envelope of {@link video} — the uniform `{ data, error, usage, report }`. */\nexport type VideoResult = ExecuteResult<VideoData> & { type: \"video\"; report: VideoReport };\n\n/**\n * Generate a video from a text prompt — the moving-image verb of the\n * output-modality track. The adapter hides the provider's submit→poll\n * job, so this returns the framework's uniform never-throws envelope:\n *\n * - **Never throws.** Provider failures surface as a typed `AIError`.\n * - **Cost-truth.** `usage.cost` is filled per-second (Sora / Veo) or\n * per-token, folding into the same `Usage.cost` rollup as everything else.\n * - **Observable.** The completed {@link VideoReport} routes to any\n * registered `Observer` via the shared `observe` seam.\n *\n * @example\n * const { data, error } = await ai.video({\n * model: sora.video({ name: \"sora-2\", pricing: { perSecond: 0.1 } }),\n * prompt: \"a timelapse of a city skyline at dusk, cinematic\",\n * durationSeconds: 8,\n * });\n * if (!error) download(data.video);\n */\nexport async function video(params: VideoParams): Promise<VideoResult> {\n const { model, prompt } = params;\n\n const runId = generateRunId(\"video\");\n const startedAt = new Date().toISOString();\n const startPerf = performance.now();\n\n const usage: Usage = { input: 0, output: 0, total: 0 };\n let data: VideoData | undefined;\n let error: AIError | undefined;\n let status: VideoReport[\"status\"] = \"completed\";\n let durationSeconds: number | undefined;\n\n try {\n const response = await model.generate(prompt, {\n durationSeconds: params.durationSeconds,\n aspectRatio: params.aspectRatio,\n resolution: params.resolution,\n negativePrompt: params.negativePrompt,\n signal: params.signal,\n ...params.options,\n });\n\n Object.assign(usage, response.usage);\n durationSeconds = response.durationSeconds;\n\n if (usage.cost === undefined) {\n const cost = computeVideoCost(usage, durationSeconds, model.pricing);\n if (cost !== undefined) {\n usage.cost = cost;\n }\n }\n\n data = { video: response.video };\n } catch (thrown) {\n error =\n thrown instanceof AIError ? thrown : new ProviderError(toMessage(thrown), { cause: thrown });\n status = params.signal?.aborted ? \"cancelled\" : \"failed\";\n }\n\n const report: VideoReport = {\n runId,\n rootRunId: runId,\n name: params.name ?? \"video\",\n type: \"video\",\n status,\n error,\n startedAt,\n endedAt: new Date().toISOString(),\n duration: performance.now() - startPerf,\n usage,\n children: [],\n model: { name: model.name, provider: model.provider },\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n };\n\n stampReportLineage(report, { rootRunId: runId, sessionId: params.sessionId });\n\n for (const observer of resolveObservers(params.observe)) {\n try {\n await observer.collect(report);\n } catch {\n // Isolate observer failures — never break the run.\n }\n }\n\n return { type: \"video\", data, error, usage, report };\n}\n\n/**\n * Price a video run: `perSecond × durationSeconds` (per-second metering,\n * attributed to `cost.output`) wins when configured, otherwise the\n * standard token math. Returns `undefined` when no usable pricing is\n * present.\n */\nfunction computeVideoCost(\n usage: Usage,\n durationSeconds: number | undefined,\n pricing: VideoModelPricing | undefined,\n): ModelPricing | undefined {\n if (!pricing) {\n return undefined;\n }\n\n if (pricing.perSecond !== undefined) {\n if (durationSeconds === undefined) {\n return undefined;\n }\n return { input: 0, output: durationSeconds * pricing.perSecond };\n }\n\n if (pricing.input !== undefined && pricing.output !== undefined) {\n return computeCost(usage, { input: pricing.input, output: pricing.output });\n }\n\n return undefined;\n}\n\n/** Best-effort message for a non-`AIError` thrown value. */\nfunction toMessage(thrown: unknown): string {\n return thrown instanceof Error ? thrown.message : String(thrown);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoEA,eAAsB,MAAM,QAA2C;CACrE,MAAM,EAAE,OAAO,WAAW;CAE1B,MAAM,QAAQ,cAAc,OAAO;CACnC,MAAM,6BAAY,IAAI,KAAK,EAAC,CAAC,YAAY;CACzC,MAAM,YAAY,YAAY,IAAI;CAElC,MAAM,QAAe;EAAE,OAAO;EAAG,QAAQ;EAAG,OAAO;CAAE;CACrD,IAAI;CACJ,IAAI;CACJ,IAAI,SAAgC;CACpC,IAAI;CAEJ,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,SAAS,QAAQ;GAC5C,iBAAiB,OAAO;GACxB,aAAa,OAAO;GACpB,YAAY,OAAO;GACnB,gBAAgB,OAAO;GACvB,QAAQ,OAAO;GACf,GAAG,OAAO;EACZ,CAAC;EAED,OAAO,OAAO,OAAO,SAAS,KAAK;EACnC,kBAAkB,SAAS;EAE3B,IAAI,MAAM,SAAS,QAAW;GAC5B,MAAM,OAAO,iBAAiB,OAAO,iBAAiB,MAAM,OAAO;GACnE,IAAI,SAAS,QACX,MAAM,OAAO;EAEjB;EAEA,OAAO,EAAE,OAAO,SAAS,MAAM;CACjC,SAAS,QAAQ;EACf,QACE,kBAAkB,UAAU,SAAS,IAAI,cAAc,UAAU,MAAM,GAAG,EAAE,OAAO,OAAO,CAAC;EAC7F,SAAS,OAAO,QAAQ,UAAU,cAAc;CAClD;CAEA,MAAM,SAAsB;EAC1B;EACA,WAAW;EACX,MAAM,OAAO,QAAQ;EACrB,MAAM;EACN;EACA;EACA;EACA,0BAAS,IAAI,KAAK,EAAC,CAAC,YAAY;EAChC,UAAU,YAAY,IAAI,IAAI;EAC9B;EACA,UAAU,CAAC;EACX,OAAO;GAAE,MAAM,MAAM;GAAM,UAAU,MAAM;EAAS;EACpD,GAAI,oBAAoB,SAAY,EAAE,gBAAgB,IAAI,CAAC;EAC3D,qBAAqB;CACvB;CAEA,mBAAmB,QAAQ;EAAE,WAAW;EAAO,WAAW,OAAO;CAAU,CAAC;CAE5E,KAAK,MAAM,YAAY,iBAAiB,OAAO,OAAO,GACpD,IAAI;EACF,MAAM,SAAS,QAAQ,MAAM;CAC/B,QAAQ,CAER;CAGF,OAAO;EAAE,MAAM;EAAS;EAAM;EAAO;EAAO;CAAO;AACrD;;;;;;;AAQA,SAAS,iBACP,OACA,iBACA,SAC0B;CAC1B,IAAI,CAAC,SACH;CAGF,IAAI,QAAQ,cAAc,QAAW;EACnC,IAAI,oBAAoB,QACtB;EAEF,OAAO;GAAE,OAAO;GAAG,QAAQ,kBAAkB,QAAQ;EAAU;CACjE;CAEA,IAAI,QAAQ,UAAU,UAAa,QAAQ,WAAW,QACpD,OAAO,YAAY,OAAO;EAAE,OAAO,QAAQ;EAAO,QAAQ,QAAQ;CAAO,CAAC;AAI9E;;AAGA,SAAS,UAAU,QAAyB;CAC1C,OAAO,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM;AACjE"}
|
package/llms-full.txt
CHANGED
|
@@ -39,9 +39,9 @@ const { data, error, usage, report } = await ai.video({
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
if (error) {
|
|
42
|
-
console.warn(error.code);
|
|
42
|
+
console.warn(error.code); // typed AIError — NEVER thrown
|
|
43
43
|
} else {
|
|
44
|
-
const clip = data.video;
|
|
44
|
+
const clip = data.video; // GeneratedVideo (discriminated)
|
|
45
45
|
if (clip.type === "url") download(clip.url);
|
|
46
46
|
else save(Buffer.from(clip.base64, "base64"), clip.mediaType);
|
|
47
47
|
}
|
|
@@ -59,17 +59,17 @@ type GeneratedVideo =
|
|
|
59
59
|
|
|
60
60
|
// VideoParams (provider-neutral) — the model is the only required field beyond prompt:
|
|
61
61
|
await ai.video({
|
|
62
|
-
model,
|
|
62
|
+
model, // VideoModelContract from an adapter's video({ name })
|
|
63
63
|
prompt: "...",
|
|
64
|
-
durationSeconds: 8,
|
|
64
|
+
durationSeconds: 8, // requested clip length
|
|
65
65
|
aspectRatio: "9:16",
|
|
66
|
-
resolution: "1080p",
|
|
66
|
+
resolution: "1080p", // hint
|
|
67
67
|
negativePrompt: "blurry, watermark",
|
|
68
|
-
signal,
|
|
69
|
-
observe: collector,
|
|
68
|
+
signal, // AbortSignal → status "cancelled"
|
|
69
|
+
observe: collector, // route the report to an Observer (panoptic), like agents
|
|
70
70
|
sessionId: "campaign-42",
|
|
71
|
-
name: "hero-clip",
|
|
72
|
-
options: { seed: 7 },
|
|
71
|
+
name: "hero-clip", // report node name (defaults to "video")
|
|
72
|
+
options: { seed: 7 }, // provider-specific escape hatch, forwarded verbatim
|
|
73
73
|
});
|
|
74
74
|
```
|
|
75
75
|
|
|
@@ -97,13 +97,23 @@ const session = await ai.realtime({
|
|
|
97
97
|
session.sendAudio(micChunkBase64, "audio/pcm"); // push mic audio upstream
|
|
98
98
|
session.sendText("Please hold for one moment."); // or a text turn
|
|
99
99
|
|
|
100
|
-
for await (const event of session.events()) {
|
|
100
|
+
for await (const event of session.events()) {
|
|
101
|
+
// duplex output
|
|
101
102
|
switch (event.type) {
|
|
102
|
-
case "audio":
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
case "
|
|
106
|
-
|
|
103
|
+
case "audio":
|
|
104
|
+
speaker.write(event.base64);
|
|
105
|
+
break;
|
|
106
|
+
case "transcript":
|
|
107
|
+
if (event.final) log(event.role, event.text);
|
|
108
|
+
break;
|
|
109
|
+
case "tool-call":
|
|
110
|
+
await handleTool(event.name, event.input);
|
|
111
|
+
break;
|
|
112
|
+
case "error":
|
|
113
|
+
console.warn(event.error.code);
|
|
114
|
+
break;
|
|
115
|
+
case "done":
|
|
116
|
+
break;
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
|
|
@@ -124,13 +134,19 @@ type RealtimeEvent =
|
|
|
124
134
|
| { type: "audio"; base64: string; mediaType: string }
|
|
125
135
|
| { type: "transcript"; role: "user" | "assistant"; text: string; final: boolean }
|
|
126
136
|
| { type: "tool-call"; id: string; name: string; input: unknown }
|
|
127
|
-
| { type: "error"; error: AIError }
|
|
128
|
-
| { type: "done" };
|
|
137
|
+
| { type: "error"; error: AIError } // typed, non-fatal
|
|
138
|
+
| { type: "done" }; // server ended the session
|
|
129
139
|
|
|
130
140
|
type RealtimeReport = {
|
|
131
|
-
runId: string;
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
runId: string;
|
|
142
|
+
rootRunId: string;
|
|
143
|
+
type: "realtime";
|
|
144
|
+
name: string;
|
|
145
|
+
status: "completed" | "failed" | "cancelled";
|
|
146
|
+
startedAt: string;
|
|
147
|
+
endedAt: string;
|
|
148
|
+
duration: number;
|
|
149
|
+
sessionId?: string;
|
|
134
150
|
};
|
|
135
151
|
```
|
|
136
152
|
|
|
@@ -187,7 +203,9 @@ const transport = new MockRealtimeTransport([
|
|
|
187
203
|
]);
|
|
188
204
|
const session = await ai.realtime({ transport, model: "mock-realtime" });
|
|
189
205
|
session.sendText("hello");
|
|
190
|
-
for await (const e of session.events()) {
|
|
206
|
+
for await (const e of session.events()) {
|
|
207
|
+
/* ... */
|
|
208
|
+
}
|
|
191
209
|
const report = await session.close();
|
|
192
210
|
// transport.lastConnection.sentText → ["hello"]
|
|
193
211
|
// transport.connectConfigs[0] → { model: "mock-realtime", ... }
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warlock.js/ai-live",
|
|
3
3
|
"description": "Live & generative rich-media for @warlock.js/ai — ai.realtime() duplex voice sessions and ai.video() text-to-video. The heavy, demand-gated modalities, kept out of core so text/image/speech stay dependency-light.",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/warlockjs/framework.git",
|
|
7
|
+
"directory": "packages/ai-live"
|
|
8
|
+
},
|
|
4
9
|
"keywords": [
|
|
5
10
|
"ai",
|
|
6
11
|
"realtime",
|
|
@@ -10,17 +15,17 @@
|
|
|
10
15
|
],
|
|
11
16
|
"license": "MIT",
|
|
12
17
|
"dependencies": {
|
|
13
|
-
"@warlock.js/logger": "5.
|
|
18
|
+
"@warlock.js/logger": "5.31.0"
|
|
14
19
|
},
|
|
15
20
|
"peerDependencies": {
|
|
16
|
-
"@warlock.js/ai": "5.
|
|
21
|
+
"@warlock.js/ai": "5.31.0"
|
|
17
22
|
},
|
|
18
23
|
"peerDependenciesMeta": {
|
|
19
24
|
"ws": {
|
|
20
25
|
"optional": true
|
|
21
26
|
}
|
|
22
27
|
},
|
|
23
|
-
"version": "5.
|
|
28
|
+
"version": "5.31.0",
|
|
24
29
|
"main": "./cjs/index.cjs",
|
|
25
30
|
"module": "./esm/index.mjs",
|
|
26
31
|
"types": "./esm/index.d.mts",
|
|
@@ -31,9 +31,9 @@ const { data, error, usage, report } = await ai.video({
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
if (error) {
|
|
34
|
-
console.warn(error.code);
|
|
34
|
+
console.warn(error.code); // typed AIError — NEVER thrown
|
|
35
35
|
} else {
|
|
36
|
-
const clip = data.video;
|
|
36
|
+
const clip = data.video; // GeneratedVideo (discriminated)
|
|
37
37
|
if (clip.type === "url") download(clip.url);
|
|
38
38
|
else save(Buffer.from(clip.base64, "base64"), clip.mediaType);
|
|
39
39
|
}
|
|
@@ -51,17 +51,17 @@ type GeneratedVideo =
|
|
|
51
51
|
|
|
52
52
|
// VideoParams (provider-neutral) — the model is the only required field beyond prompt:
|
|
53
53
|
await ai.video({
|
|
54
|
-
model,
|
|
54
|
+
model, // VideoModelContract from an adapter's video({ name })
|
|
55
55
|
prompt: "...",
|
|
56
|
-
durationSeconds: 8,
|
|
56
|
+
durationSeconds: 8, // requested clip length
|
|
57
57
|
aspectRatio: "9:16",
|
|
58
|
-
resolution: "1080p",
|
|
58
|
+
resolution: "1080p", // hint
|
|
59
59
|
negativePrompt: "blurry, watermark",
|
|
60
|
-
signal,
|
|
61
|
-
observe: collector,
|
|
60
|
+
signal, // AbortSignal → status "cancelled"
|
|
61
|
+
observe: collector, // route the report to an Observer (panoptic), like agents
|
|
62
62
|
sessionId: "campaign-42",
|
|
63
|
-
name: "hero-clip",
|
|
64
|
-
options: { seed: 7 },
|
|
63
|
+
name: "hero-clip", // report node name (defaults to "video")
|
|
64
|
+
options: { seed: 7 }, // provider-specific escape hatch, forwarded verbatim
|
|
65
65
|
});
|
|
66
66
|
```
|
|
67
67
|
|
|
@@ -89,13 +89,23 @@ const session = await ai.realtime({
|
|
|
89
89
|
session.sendAudio(micChunkBase64, "audio/pcm"); // push mic audio upstream
|
|
90
90
|
session.sendText("Please hold for one moment."); // or a text turn
|
|
91
91
|
|
|
92
|
-
for await (const event of session.events()) {
|
|
92
|
+
for await (const event of session.events()) {
|
|
93
|
+
// duplex output
|
|
93
94
|
switch (event.type) {
|
|
94
|
-
case "audio":
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
case "
|
|
98
|
-
|
|
95
|
+
case "audio":
|
|
96
|
+
speaker.write(event.base64);
|
|
97
|
+
break;
|
|
98
|
+
case "transcript":
|
|
99
|
+
if (event.final) log(event.role, event.text);
|
|
100
|
+
break;
|
|
101
|
+
case "tool-call":
|
|
102
|
+
await handleTool(event.name, event.input);
|
|
103
|
+
break;
|
|
104
|
+
case "error":
|
|
105
|
+
console.warn(event.error.code);
|
|
106
|
+
break;
|
|
107
|
+
case "done":
|
|
108
|
+
break;
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
|
|
@@ -116,13 +126,19 @@ type RealtimeEvent =
|
|
|
116
126
|
| { type: "audio"; base64: string; mediaType: string }
|
|
117
127
|
| { type: "transcript"; role: "user" | "assistant"; text: string; final: boolean }
|
|
118
128
|
| { type: "tool-call"; id: string; name: string; input: unknown }
|
|
119
|
-
| { type: "error"; error: AIError }
|
|
120
|
-
| { type: "done" };
|
|
129
|
+
| { type: "error"; error: AIError } // typed, non-fatal
|
|
130
|
+
| { type: "done" }; // server ended the session
|
|
121
131
|
|
|
122
132
|
type RealtimeReport = {
|
|
123
|
-
runId: string;
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
runId: string;
|
|
134
|
+
rootRunId: string;
|
|
135
|
+
type: "realtime";
|
|
136
|
+
name: string;
|
|
137
|
+
status: "completed" | "failed" | "cancelled";
|
|
138
|
+
startedAt: string;
|
|
139
|
+
endedAt: string;
|
|
140
|
+
duration: number;
|
|
141
|
+
sessionId?: string;
|
|
126
142
|
};
|
|
127
143
|
```
|
|
128
144
|
|
|
@@ -179,7 +195,9 @@ const transport = new MockRealtimeTransport([
|
|
|
179
195
|
]);
|
|
180
196
|
const session = await ai.realtime({ transport, model: "mock-realtime" });
|
|
181
197
|
session.sendText("hello");
|
|
182
|
-
for await (const e of session.events()) {
|
|
198
|
+
for await (const e of session.events()) {
|
|
199
|
+
/* ... */
|
|
200
|
+
}
|
|
183
201
|
const report = await session.close();
|
|
184
202
|
// transport.lastConnection.sentText → ["hello"]
|
|
185
203
|
// transport.connectConfigs[0] → { model: "mock-realtime", ... }
|