@warlock.js/ai-openai 4.12.0 → 4.14.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/esm/index.d.mts CHANGED
@@ -2,7 +2,6 @@ import { OpenAIEmbedderConfig, OpenAIImageConfig, OpenAISDKConfig, OpenAISpeechC
2
2
  import { OpenAISDK } from "./sdk.mjs";
3
3
  import { OpenAIEmbedder } from "./embedder.mjs";
4
4
  import { OpenAIImageModel } from "./image.mjs";
5
- import { OPENAI_IMAGE_MODEL_PREFIXES, isOpenAIImageModel } from "./known-image-models.mjs";
6
- import { OpenAISpeechModel, isOpenAISpeechModel } from "./speech.mjs";
7
- import { OpenAITranscriptionModel, isOpenAITranscriptionModel } from "./transcription.mjs";
8
- export { OPENAI_IMAGE_MODEL_PREFIXES, OpenAIEmbedder, type OpenAIEmbedderConfig, type OpenAIImageConfig, OpenAIImageModel, OpenAISDK, type OpenAISDKConfig, type OpenAISpeechConfig, OpenAISpeechModel, type OpenAITranscriptionConfig, OpenAITranscriptionModel, isOpenAIImageModel, isOpenAISpeechModel, isOpenAITranscriptionModel };
5
+ import { OpenAISpeechModel } from "./speech.mjs";
6
+ import { OpenAITranscriptionModel } from "./transcription.mjs";
7
+ export { OpenAIEmbedder, type OpenAIEmbedderConfig, type OpenAIImageConfig, OpenAIImageModel, OpenAISDK, type OpenAISDKConfig, type OpenAISpeechConfig, OpenAISpeechModel, type OpenAITranscriptionConfig, OpenAITranscriptionModel };
package/esm/index.mjs CHANGED
@@ -1,8 +1,7 @@
1
1
  import { OpenAIEmbedder } from "./embedder.mjs";
2
- import { OPENAI_IMAGE_MODEL_PREFIXES, isOpenAIImageModel } from "./known-image-models.mjs";
3
2
  import { OpenAIImageModel } from "./image.mjs";
4
- import { OpenAISpeechModel, isOpenAISpeechModel } from "./speech.mjs";
5
- import { OpenAITranscriptionModel, isOpenAITranscriptionModel } from "./transcription.mjs";
3
+ import { OpenAISpeechModel } from "./speech.mjs";
4
+ import { OpenAITranscriptionModel } from "./transcription.mjs";
6
5
  import { OpenAISDK } from "./sdk.mjs";
7
6
 
8
- export { OPENAI_IMAGE_MODEL_PREFIXES, OpenAIEmbedder, OpenAIImageModel, OpenAISDK, OpenAISpeechModel, OpenAITranscriptionModel, isOpenAIImageModel, isOpenAISpeechModel, isOpenAITranscriptionModel };
7
+ export { OpenAIEmbedder, OpenAIImageModel, OpenAISDK, OpenAISpeechModel, OpenAITranscriptionModel };
package/esm/sdk.d.mts CHANGED
@@ -75,9 +75,10 @@ declare class OpenAISDK implements SDKAdapterContract {
75
75
  embedder(config: OpenAIEmbedderConfig): EmbedderContract;
76
76
  /**
77
77
  * Build an `OpenAIImageModel` bound to this SDK's client for use with
78
- * `ai.image({ model, prompt })`. Accepts the `gpt-image-*` (token-metered)
79
- * and `dall-e-*` (per-image-metered) families; a non-image model id
80
- * is rejected at construction.
78
+ * `ai.image({ model, prompt })`. `config.name` is passed through to
79
+ * `client.images.generate` as given no id is rejected locally, so
80
+ * an unsupported model fails at OpenAI, not here. Metering follows
81
+ * the family: `gpt-image-*` is token-metered, `dall-e-*` per-image.
81
82
  *
82
83
  * Pricing resolution mirrors `model()`: per-model `config.pricing`
83
84
  * wins, otherwise the SDK-level registry entry keyed by `config.name`,
@@ -92,9 +93,10 @@ declare class OpenAISDK implements SDKAdapterContract {
92
93
  image(config: OpenAIImageConfig): ImageModelContract;
93
94
  /**
94
95
  * Build an `OpenAISpeechModel` (text-to-speech) bound to this SDK's
95
- * client, for use with `ai.speech({ model, text })`. Accepts the
96
- * `tts-1` / `gpt-4o-mini-tts` families; a non-TTS model id is rejected
97
- * at construction.
96
+ * client, for use with `ai.speech({ model, text })`. `config.name` is
97
+ * passed through to `audio.speech.create` as given no id is
98
+ * rejected locally, so an unsupported model fails at OpenAI, not
99
+ * here.
98
100
  *
99
101
  * @example
100
102
  * const tts = openai.speech({ name: "tts-1", voice: "alloy" });
@@ -104,8 +106,9 @@ declare class OpenAISDK implements SDKAdapterContract {
104
106
  /**
105
107
  * Build an `OpenAITranscriptionModel` (speech-to-text) bound to this
106
108
  * SDK's client, for use with `ai.transcribe({ model, audio })`.
107
- * Accepts the `whisper-1` / `gpt-4o-transcribe` families; a non-STT
108
- * model id is rejected at construction.
109
+ * `config.name` is passed through to `audio.transcriptions.create`
110
+ * as given — no id is rejected locally, so an unsupported model
111
+ * fails at OpenAI, not here.
109
112
  *
110
113
  * @example
111
114
  * const stt = openai.transcribe({ name: "whisper-1" });
package/esm/sdk.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../../ai-openai/src/sdk.ts"],"mappings":";;;;;;AA2DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,SAAA,YAAqB,kBAAA;EAAA,iBACf,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,OAAA;cAEE,MAAA,EAAQ,eAAA;EAyEpB;;;;;;;;;;;;EAjDA,KAAA,CAAM,MAAA,EAAQ,iBAAA,GAAoB,aAAA;EAiFuC;;;;;;;EAlEnE,KAAA,CAAM,IAAA,UAAc,MAAA,YAAkB,OAAA;;;;;;;;;;;EAc5C,QAAA,CAAS,MAAA,EAAQ,oBAAA,GAAuB,gBAAA;;;;;;;;;;;;;;;;;EAoBxC,KAAA,CAAM,MAAA,EAAQ,iBAAA,GAAoB,kBAAA;;;;;;;;;;;EAkBlC,MAAA,CAAO,MAAA,EAAQ,kBAAA,GAAqB,mBAAA;;;;;;;;;;;EAcpC,UAAA,CAAW,MAAA,EAAQ,yBAAA,GAA4B,0BAAA;AAAA"}
1
+ {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../../ai-openai/src/sdk.ts"],"mappings":";;;;;;AA2DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,SAAA,YAAqB,kBAAA;EAAA,iBACf,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,OAAA;cAEE,MAAA,EAAQ,eAAA;EA0EpB;;;;;;;;;;;;EAlDA,KAAA,CAAM,MAAA,EAAQ,iBAAA,GAAoB,aAAA;EAoFuC;;;;;;;EArEnE,KAAA,CAAM,IAAA,UAAc,MAAA,YAAkB,OAAA;;;;;;;;;;;EAc5C,QAAA,CAAS,MAAA,EAAQ,oBAAA,GAAuB,gBAAA;;;;;;;;;;;;;;;;;;EAqBxC,KAAA,CAAM,MAAA,EAAQ,iBAAA,GAAoB,kBAAA;;;;;;;;;;;;EAmBlC,MAAA,CAAO,MAAA,EAAQ,kBAAA,GAAqB,mBAAA;;;;;;;;;;;;EAepC,UAAA,CAAW,MAAA,EAAQ,yBAAA,GAA4B,0BAAA;AAAA"}
package/esm/sdk.mjs CHANGED
@@ -93,9 +93,10 @@ var OpenAISDK = class {
93
93
  }
94
94
  /**
95
95
  * Build an `OpenAIImageModel` bound to this SDK's client for use with
96
- * `ai.image({ model, prompt })`. Accepts the `gpt-image-*` (token-metered)
97
- * and `dall-e-*` (per-image-metered) families; a non-image model id
98
- * is rejected at construction.
96
+ * `ai.image({ model, prompt })`. `config.name` is passed through to
97
+ * `client.images.generate` as given no id is rejected locally, so
98
+ * an unsupported model fails at OpenAI, not here. Metering follows
99
+ * the family: `gpt-image-*` is token-metered, `dall-e-*` per-image.
99
100
  *
100
101
  * Pricing resolution mirrors `model()`: per-model `config.pricing`
101
102
  * wins, otherwise the SDK-level registry entry keyed by `config.name`,
@@ -117,9 +118,10 @@ var OpenAISDK = class {
117
118
  }
118
119
  /**
119
120
  * Build an `OpenAISpeechModel` (text-to-speech) bound to this SDK's
120
- * client, for use with `ai.speech({ model, text })`. Accepts the
121
- * `tts-1` / `gpt-4o-mini-tts` families; a non-TTS model id is rejected
122
- * at construction.
121
+ * client, for use with `ai.speech({ model, text })`. `config.name` is
122
+ * passed through to `audio.speech.create` as given no id is
123
+ * rejected locally, so an unsupported model fails at OpenAI, not
124
+ * here.
123
125
  *
124
126
  * @example
125
127
  * const tts = openai.speech({ name: "tts-1", voice: "alloy" });
@@ -131,8 +133,9 @@ var OpenAISDK = class {
131
133
  /**
132
134
  * Build an `OpenAITranscriptionModel` (speech-to-text) bound to this
133
135
  * SDK's client, for use with `ai.transcribe({ model, audio })`.
134
- * Accepts the `whisper-1` / `gpt-4o-transcribe` families; a non-STT
135
- * model id is rejected at construction.
136
+ * `config.name` is passed through to `audio.transcriptions.create`
137
+ * as given — no id is rejected locally, so an unsupported model
138
+ * fails at OpenAI, not here.
136
139
  *
137
140
  * @example
138
141
  * const stt = openai.transcribe({ name: "whisper-1" });
package/esm/sdk.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.mjs","names":[],"sources":["../../../../../../ai-openai/src/sdk.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport type {\n EmbedderContract,\n ImageModelContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n SpeechModelContract,\n TranscriptionModelContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n OpenAIEmbedderConfig,\n OpenAIImageConfig,\n OpenAIModelConfig,\n OpenAISDKConfig,\n OpenAISpeechConfig,\n OpenAITranscriptionConfig,\n} from \"./config.type\";\nimport { OpenAIEmbedder } from \"./embedder\";\nimport { OpenAIImageModel } from \"./image\";\nimport { OpenAIModel } from \"./model\";\nimport { OpenAISpeechModel } from \"./speech\";\nimport { OpenAITranscriptionModel } from \"./transcription\";\n\n/**\n * OpenAI-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for any OpenAI-compatible provider\n * (OpenAI, Azure OpenAI, OpenRouter, local gateways speaking the Chat\n * Completions protocol). A single `OpenAISDK` instance holds one live\n * `OpenAI` client, shared by every `ModelContract` it produces via\n * `model()`. Users construct one SDK per provider/account and reuse it\n * across all agents, workflows, and supervisors that target that\n * provider.\n *\n * **Responsibility.**\n * - Owns: a long-lived `OpenAI` client (authentication, base URL) and\n * its lifetime scope. Factory for `OpenAIModel` instances — each\n * model call gets a reference to the same client.\n * - Does NOT own: anything per-call (tool execution, message history,\n * streaming loop) — those live in `OpenAIModel` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the `OpenAI` client is heavy to construct and\n * designed to be reused; keeping it on `this` makes that reuse\n * explicit and aligns with the PascalCase naming convention readers\n * expect from a constructor.\n *\n * @example\n * const openai = new OpenAISDK({ apiKey: process.env.OPENAI_API_KEY! });\n * const model = openai.model({ name: \"gpt-4o\", temperature: 0.7 });\n * const tokens = await openai.count(\"Hello world\");\n *\n * @example\n * // Compose into an `ai.openai` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, persona, instruction, openai: new OpenAISDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.openai.model({ name: \"gpt-4o-mini\" }) });\n */\nexport class OpenAISDK implements SDKAdapterContract {\n private readonly client: OpenAI;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: OpenAISDKConfig) {\n // Peel off the framework-only keys and forward every other upstream\n // `ClientOptions` (timeout, maxRetries, defaultHeaders, fetch,\n // organization, project, …) verbatim — they type-check, so dropping them\n // is a silent footgun. Mirrors the Bedrock/Google/Ollama adapters.\n const { provider, pricing, ...clientOptions } = config;\n\n this.client = new OpenAI(clientOptions);\n this.provider = provider ?? \"openai\";\n this.pricing = pricing;\n }\n\n /**\n * Build an `OpenAIModel` bound to this SDK's client. Each call returns\n * a fresh model instance, but all instances share the underlying\n * `OpenAI` client — connection pools, rate limits, and authentication\n * state stay unified across every model produced here. The SDK's\n * `provider` label is forwarded so every model self-identifies as\n * coming from the same upstream.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: OpenAIModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: OpenAIModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new OpenAIModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate for a given text. Uses a\n * character-heuristic (`approximateTokenCount`) from the core package\n * — good enough for budgeting and quota guards, not for billing.\n * Accepts an optional model id for future per-model tokenizer\n * dispatch; currently ignored.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build an `OpenAIEmbedder` bound to this SDK's client. Each call\n * returns a fresh embedder instance sharing the same underlying\n * `OpenAI` client — connection pools and authentication stay unified\n * across every embedder produced here.\n *\n * @example\n * const embedder = openai.embedder({ name: \"text-embedding-3-small\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: OpenAIEmbedderConfig): EmbedderContract {\n return new OpenAIEmbedder(this.client, config);\n }\n\n /**\n * Build an `OpenAIImageModel` bound to this SDK's client for use with\n * `ai.image({ model, prompt })`. Accepts the `gpt-image-*` (token-metered)\n * and `dall-e-*` (per-image-metered) families; a non-image model id\n * is rejected at construction.\n *\n * Pricing resolution mirrors `model()`: per-model `config.pricing`\n * wins, otherwise the SDK-level registry entry keyed by `config.name`,\n * otherwise `undefined` (no cost computed). A token-priced\n * `gpt-image-1` entry can live in the same SDK registry as the chat\n * models.\n *\n * @example\n * const model = openai.image({ name: \"gpt-image-1\" });\n * const { data } = await ai.image({ model, prompt: \"a red bicycle\" });\n */\n public image(config: OpenAIImageConfig): ImageModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: OpenAIImageConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new OpenAIImageModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Build an `OpenAISpeechModel` (text-to-speech) bound to this SDK's\n * client, for use with `ai.speech({ model, text })`. Accepts the\n * `tts-1` / `gpt-4o-mini-tts` families; a non-TTS model id is rejected\n * at construction.\n *\n * @example\n * const tts = openai.speech({ name: \"tts-1\", voice: \"alloy\" });\n * const { data } = await ai.speech({ model: tts, text: \"Hello\" });\n */\n public speech(config: OpenAISpeechConfig): SpeechModelContract {\n return new OpenAISpeechModel(this.client, config, this.provider);\n }\n\n /**\n * Build an `OpenAITranscriptionModel` (speech-to-text) bound to this\n * SDK's client, for use with `ai.transcribe({ model, audio })`.\n * Accepts the `whisper-1` / `gpt-4o-transcribe` families; a non-STT\n * model id is rejected at construction.\n *\n * @example\n * const stt = openai.transcribe({ name: \"whisper-1\" });\n * const { data } = await ai.transcribe({ model: stt, audio });\n */\n public transcribe(config: OpenAITranscriptionConfig): TranscriptionModelContract {\n return new OpenAITranscriptionModel(this.client, config, this.provider);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,IAAa,YAAb,MAAqD;CAKnD,AAAO,YAAY,QAAyB;EAK1C,MAAM,EAAE,UAAU,SAAS,GAAG,kBAAkB;EAEhD,KAAK,SAAS,IAAI,OAAO,aAAa;EACtC,KAAK,WAAW,YAAY;EAC5B,KAAK,UAAU;CACjB;;;;;;;;;;;;;CAcA,AAAO,MAAM,QAA0C;EACrD,MAAM,kBAAkB,OAAO,WAAW,KAAK,UAAU,OAAO;EAChE,MAAM,iBACJ,oBAAoB,OAAO,UAAU,SAAS;GAAE,GAAG;GAAQ,SAAS;EAAgB;EAEtF,OAAO,IAAI,YAAY,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;CACnE;;;;;;;;CASA,MAAa,MAAM,MAAc,QAAkC;EACjE,OAAO,sBAAsB,IAAI;CACnC;;;;;;;;;;;CAYA,AAAO,SAAS,QAAgD;EAC9D,OAAO,IAAI,eAAe,KAAK,QAAQ,MAAM;CAC/C;;;;;;;;;;;;;;;;;CAkBA,AAAO,MAAM,QAA+C;EAC1D,MAAM,kBAAkB,OAAO,WAAW,KAAK,UAAU,OAAO;EAChE,MAAM,iBACJ,oBAAoB,OAAO,UAAU,SAAS;GAAE,GAAG;GAAQ,SAAS;EAAgB;EAEtF,OAAO,IAAI,iBAAiB,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;CACxE;;;;;;;;;;;CAYA,AAAO,OAAO,QAAiD;EAC7D,OAAO,IAAI,kBAAkB,KAAK,QAAQ,QAAQ,KAAK,QAAQ;CACjE;;;;;;;;;;;CAYA,AAAO,WAAW,QAA+D;EAC/E,OAAO,IAAI,yBAAyB,KAAK,QAAQ,QAAQ,KAAK,QAAQ;CACxE;AACF"}
1
+ {"version":3,"file":"sdk.mjs","names":[],"sources":["../../../../../../ai-openai/src/sdk.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport type {\n EmbedderContract,\n ImageModelContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n SpeechModelContract,\n TranscriptionModelContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n OpenAIEmbedderConfig,\n OpenAIImageConfig,\n OpenAIModelConfig,\n OpenAISDKConfig,\n OpenAISpeechConfig,\n OpenAITranscriptionConfig,\n} from \"./config.type\";\nimport { OpenAIEmbedder } from \"./embedder\";\nimport { OpenAIImageModel } from \"./image\";\nimport { OpenAIModel } from \"./model\";\nimport { OpenAISpeechModel } from \"./speech\";\nimport { OpenAITranscriptionModel } from \"./transcription\";\n\n/**\n * OpenAI-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for any OpenAI-compatible provider\n * (OpenAI, Azure OpenAI, OpenRouter, local gateways speaking the Chat\n * Completions protocol). A single `OpenAISDK` instance holds one live\n * `OpenAI` client, shared by every `ModelContract` it produces via\n * `model()`. Users construct one SDK per provider/account and reuse it\n * across all agents, workflows, and supervisors that target that\n * provider.\n *\n * **Responsibility.**\n * - Owns: a long-lived `OpenAI` client (authentication, base URL) and\n * its lifetime scope. Factory for `OpenAIModel` instances — each\n * model call gets a reference to the same client.\n * - Does NOT own: anything per-call (tool execution, message history,\n * streaming loop) — those live in `OpenAIModel` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the `OpenAI` client is heavy to construct and\n * designed to be reused; keeping it on `this` makes that reuse\n * explicit and aligns with the PascalCase naming convention readers\n * expect from a constructor.\n *\n * @example\n * const openai = new OpenAISDK({ apiKey: process.env.OPENAI_API_KEY! });\n * const model = openai.model({ name: \"gpt-4o\", temperature: 0.7 });\n * const tokens = await openai.count(\"Hello world\");\n *\n * @example\n * // Compose into an `ai.openai` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, persona, instruction, openai: new OpenAISDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.openai.model({ name: \"gpt-4o-mini\" }) });\n */\nexport class OpenAISDK implements SDKAdapterContract {\n private readonly client: OpenAI;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: OpenAISDKConfig) {\n // Peel off the framework-only keys and forward every other upstream\n // `ClientOptions` (timeout, maxRetries, defaultHeaders, fetch,\n // organization, project, …) verbatim — they type-check, so dropping them\n // is a silent footgun. Mirrors the Bedrock/Google/Ollama adapters.\n const { provider, pricing, ...clientOptions } = config;\n\n this.client = new OpenAI(clientOptions);\n this.provider = provider ?? \"openai\";\n this.pricing = pricing;\n }\n\n /**\n * Build an `OpenAIModel` bound to this SDK's client. Each call returns\n * a fresh model instance, but all instances share the underlying\n * `OpenAI` client — connection pools, rate limits, and authentication\n * state stay unified across every model produced here. The SDK's\n * `provider` label is forwarded so every model self-identifies as\n * coming from the same upstream.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: OpenAIModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: OpenAIModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new OpenAIModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate for a given text. Uses a\n * character-heuristic (`approximateTokenCount`) from the core package\n * — good enough for budgeting and quota guards, not for billing.\n * Accepts an optional model id for future per-model tokenizer\n * dispatch; currently ignored.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build an `OpenAIEmbedder` bound to this SDK's client. Each call\n * returns a fresh embedder instance sharing the same underlying\n * `OpenAI` client — connection pools and authentication stay unified\n * across every embedder produced here.\n *\n * @example\n * const embedder = openai.embedder({ name: \"text-embedding-3-small\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: OpenAIEmbedderConfig): EmbedderContract {\n return new OpenAIEmbedder(this.client, config);\n }\n\n /**\n * Build an `OpenAIImageModel` bound to this SDK's client for use with\n * `ai.image({ model, prompt })`. `config.name` is passed through to\n * `client.images.generate` as given no id is rejected locally, so\n * an unsupported model fails at OpenAI, not here. Metering follows\n * the family: `gpt-image-*` is token-metered, `dall-e-*` per-image.\n *\n * Pricing resolution mirrors `model()`: per-model `config.pricing`\n * wins, otherwise the SDK-level registry entry keyed by `config.name`,\n * otherwise `undefined` (no cost computed). A token-priced\n * `gpt-image-1` entry can live in the same SDK registry as the chat\n * models.\n *\n * @example\n * const model = openai.image({ name: \"gpt-image-1\" });\n * const { data } = await ai.image({ model, prompt: \"a red bicycle\" });\n */\n public image(config: OpenAIImageConfig): ImageModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: OpenAIImageConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new OpenAIImageModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Build an `OpenAISpeechModel` (text-to-speech) bound to this SDK's\n * client, for use with `ai.speech({ model, text })`. `config.name` is\n * passed through to `audio.speech.create` as given no id is\n * rejected locally, so an unsupported model fails at OpenAI, not\n * here.\n *\n * @example\n * const tts = openai.speech({ name: \"tts-1\", voice: \"alloy\" });\n * const { data } = await ai.speech({ model: tts, text: \"Hello\" });\n */\n public speech(config: OpenAISpeechConfig): SpeechModelContract {\n return new OpenAISpeechModel(this.client, config, this.provider);\n }\n\n /**\n * Build an `OpenAITranscriptionModel` (speech-to-text) bound to this\n * SDK's client, for use with `ai.transcribe({ model, audio })`.\n * `config.name` is passed through to `audio.transcriptions.create`\n * as given — no id is rejected locally, so an unsupported model\n * fails at OpenAI, not here.\n *\n * @example\n * const stt = openai.transcribe({ name: \"whisper-1\" });\n * const { data } = await ai.transcribe({ model: stt, audio });\n */\n public transcribe(config: OpenAITranscriptionConfig): TranscriptionModelContract {\n return new OpenAITranscriptionModel(this.client, config, this.provider);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,IAAa,YAAb,MAAqD;CAKnD,AAAO,YAAY,QAAyB;EAK1C,MAAM,EAAE,UAAU,SAAS,GAAG,kBAAkB;EAEhD,KAAK,SAAS,IAAI,OAAO,aAAa;EACtC,KAAK,WAAW,YAAY;EAC5B,KAAK,UAAU;CACjB;;;;;;;;;;;;;CAcA,AAAO,MAAM,QAA0C;EACrD,MAAM,kBAAkB,OAAO,WAAW,KAAK,UAAU,OAAO;EAChE,MAAM,iBACJ,oBAAoB,OAAO,UAAU,SAAS;GAAE,GAAG;GAAQ,SAAS;EAAgB;EAEtF,OAAO,IAAI,YAAY,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;CACnE;;;;;;;;CASA,MAAa,MAAM,MAAc,QAAkC;EACjE,OAAO,sBAAsB,IAAI;CACnC;;;;;;;;;;;CAYA,AAAO,SAAS,QAAgD;EAC9D,OAAO,IAAI,eAAe,KAAK,QAAQ,MAAM;CAC/C;;;;;;;;;;;;;;;;;;CAmBA,AAAO,MAAM,QAA+C;EAC1D,MAAM,kBAAkB,OAAO,WAAW,KAAK,UAAU,OAAO;EAChE,MAAM,iBACJ,oBAAoB,OAAO,UAAU,SAAS;GAAE,GAAG;GAAQ,SAAS;EAAgB;EAEtF,OAAO,IAAI,iBAAiB,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;CACxE;;;;;;;;;;;;CAaA,AAAO,OAAO,QAAiD;EAC7D,OAAO,IAAI,kBAAkB,KAAK,QAAQ,QAAQ,KAAK,QAAQ;CACjE;;;;;;;;;;;;CAaA,AAAO,WAAW,QAA+D;EAC/E,OAAO,IAAI,yBAAyB,KAAK,QAAQ,QAAQ,KAAK,QAAQ;CACxE;AACF"}
package/esm/speech.d.mts CHANGED
@@ -3,16 +3,16 @@ import OpenAI from "openai";
3
3
  import { SpeechGenerationResponse, SpeechModelContract, SpeechModelPricing, SpeechOptions } from "@warlock.js/ai";
4
4
 
5
5
  //#region ../ai-openai/src/speech.d.ts
6
- /** True when `name` is a recognized OpenAI text-to-speech model. */
7
- declare function isOpenAISpeechModel(name: string): boolean;
8
6
  /**
9
7
  * OpenAI-backed implementation of `SpeechModelContract` (text-to-speech)
10
8
  * via `audio.speech.create`. Standalone primitive — no relation to chat
11
9
  * completions or the agent loop. Consumed by the `ai.speech()` verb.
12
10
  *
13
- * **Capability guard.** The constructor rejects a non-TTS model id up
14
- * front (`tts-1` / `gpt-4o-mini-tts` only) so the mistake surfaces at
15
- * wiring time, mirroring the embedder / image guards.
11
+ * **No model-id validation.** `config.name` is forwarded to
12
+ * `audio.speech.create` exactly as given the constructor never
13
+ * inspects it. OpenAI ships and retires TTS model ids on its own
14
+ * schedule, so an unrecognized id fails at OpenAI (wrapped into the
15
+ * typed `AIError` hierarchy by `generate()`), not here.
16
16
  *
17
17
  * @example
18
18
  * const tts = new OpenAISpeechModel(client, { name: "tts-1", voice: "alloy" }, "openai");
@@ -29,5 +29,5 @@ declare class OpenAISpeechModel implements SpeechModelContract {
29
29
  generate(text: string, options?: SpeechOptions): Promise<SpeechGenerationResponse>;
30
30
  }
31
31
  //#endregion
32
- export { OpenAISpeechModel, isOpenAISpeechModel };
32
+ export { OpenAISpeechModel };
33
33
  //# sourceMappingURL=speech.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"speech.d.mts","names":[],"sources":["../../../../../../ai-openai/src/speech.ts"],"mappings":";;;;;;iBAkBgB,mBAAA,CAAoB,IAAY;AAAhD;;;;AAAgD;AAmChD;;;;;;;;AAnCA,cAmCa,iBAAA,YAA6B,mBAAA;EAAA,SACxB,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,kBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,YAAA;EAAA,iBACA,MAAA;cAEE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,kBAAA,EAAoB,QAAA;EAelD,QAAA,CAAS,IAAA,UAAc,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,wBAAA;AAAA"}
1
+ {"version":3,"file":"speech.d.mts","names":[],"sources":["../../../../../../ai-openai/src/speech.ts"],"mappings":";;;;;;;AA8CA;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,mBAAA;EAAA,SACxB,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,kBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,YAAA;EAAA,iBACA,MAAA;cAEE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,kBAAA,EAAoB,QAAA;EAQlD,QAAA,CAAS,IAAA,UAAc,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,wBAAA;AAAA"}
package/esm/speech.mjs CHANGED
@@ -1,20 +1,9 @@
1
1
  import { wrapOpenAIError } from "./utils/wrap-openai-error.mjs";
2
2
  import "./utils/index.mjs";
3
- import { InvalidRequestError } from "@warlock.js/ai";
4
3
  import { log } from "@warlock.js/logger";
5
4
 
6
5
  //#region ../ai-openai/src/speech.ts
7
6
  const LOG_MODULE = "ai.openai";
8
- /** Model-id prefixes OpenAI exposes through the **Speech** (TTS) API. */
9
- const SPEECH_MODEL_PREFIXES = [
10
- "tts-1",
11
- "gpt-4o-mini-tts",
12
- "gpt-audio"
13
- ];
14
- /** True when `name` is a recognized OpenAI text-to-speech model. */
15
- function isOpenAISpeechModel(name) {
16
- return SPEECH_MODEL_PREFIXES.some((prefix) => name.startsWith(prefix));
17
- }
18
7
  /** Map a neutral output container hint to its IANA audio media type. */
19
8
  function audioMediaType(format) {
20
9
  switch (format) {
@@ -31,9 +20,11 @@ function audioMediaType(format) {
31
20
  * via `audio.speech.create`. Standalone primitive — no relation to chat
32
21
  * completions or the agent loop. Consumed by the `ai.speech()` verb.
33
22
  *
34
- * **Capability guard.** The constructor rejects a non-TTS model id up
35
- * front (`tts-1` / `gpt-4o-mini-tts` only) so the mistake surfaces at
36
- * wiring time, mirroring the embedder / image guards.
23
+ * **No model-id validation.** `config.name` is forwarded to
24
+ * `audio.speech.create` exactly as given the constructor never
25
+ * inspects it. OpenAI ships and retires TTS model ids on its own
26
+ * schedule, so an unrecognized id fails at OpenAI (wrapped into the
27
+ * typed `AIError` hierarchy by `generate()`), not here.
37
28
  *
38
29
  * @example
39
30
  * const tts = new OpenAISpeechModel(client, { name: "tts-1", voice: "alloy" }, "openai");
@@ -42,7 +33,6 @@ function audioMediaType(format) {
42
33
  var OpenAISpeechModel = class {
43
34
  constructor(client, config, provider = "openai") {
44
35
  this.logger = log;
45
- if (!isOpenAISpeechModel(config.name)) throw new InvalidRequestError(`"${config.name}" is not a known OpenAI text-to-speech model. Use a \`tts-1\` / \`tts-1-hd\` / \`gpt-4o-mini-tts\` model with openai.speech({ name }).`);
46
36
  this.client = client;
47
37
  this.name = config.name;
48
38
  this.provider = provider;
@@ -90,5 +80,5 @@ var OpenAISpeechModel = class {
90
80
  };
91
81
 
92
82
  //#endregion
93
- export { OpenAISpeechModel, isOpenAISpeechModel };
83
+ export { OpenAISpeechModel };
94
84
  //# sourceMappingURL=speech.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"speech.mjs","names":[],"sources":["../../../../../../ai-openai/src/speech.ts"],"sourcesContent":["import {\n InvalidRequestError,\n type SpeechGenerationResponse,\n type SpeechModelContract,\n type SpeechModelPricing,\n type SpeechOptions,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport type { OpenAISpeechConfig } from \"./config.type\";\nimport { wrapOpenAIError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/** Model-id prefixes OpenAI exposes through the **Speech** (TTS) API. */\nconst SPEECH_MODEL_PREFIXES = [\"tts-1\", \"gpt-4o-mini-tts\", \"gpt-audio\"] as const;\n\n/** True when `name` is a recognized OpenAI text-to-speech model. */\nexport function isOpenAISpeechModel(name: string): boolean {\n return SPEECH_MODEL_PREFIXES.some((prefix) => name.startsWith(prefix));\n}\n\n/** Map a neutral output container hint to its IANA audio media type. */\nfunction audioMediaType(format: string | undefined): string {\n switch (format) {\n case \"opus\":\n return \"audio/opus\";\n case \"aac\":\n return \"audio/aac\";\n case \"flac\":\n return \"audio/flac\";\n case \"wav\":\n return \"audio/wav\";\n case \"pcm\":\n return \"audio/pcm\";\n default:\n return \"audio/mpeg\";\n }\n}\n\n/**\n * OpenAI-backed implementation of `SpeechModelContract` (text-to-speech)\n * via `audio.speech.create`. Standalone primitive — no relation to chat\n * completions or the agent loop. Consumed by the `ai.speech()` verb.\n *\n * **Capability guard.** The constructor rejects a non-TTS model id up\n * front (`tts-1` / `gpt-4o-mini-tts` only) so the mistake surfaces at\n * wiring time, mirroring the embedder / image guards.\n *\n * @example\n * const tts = new OpenAISpeechModel(client, { name: \"tts-1\", voice: \"alloy\" }, \"openai\");\n * const { audio } = await tts.generate(\"Welcome aboard.\");\n */\nexport class OpenAISpeechModel implements SpeechModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: SpeechModelPricing;\n\n private readonly client: OpenAI;\n private readonly defaultVoice?: string;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAISpeechConfig, provider: string = \"openai\") {\n if (!isOpenAISpeechModel(config.name)) {\n throw new InvalidRequestError(\n `\"${config.name}\" is not a known OpenAI text-to-speech model. ` +\n \"Use a `tts-1` / `tts-1-hd` / `gpt-4o-mini-tts` model with openai.speech({ name }).\",\n );\n }\n\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.defaultVoice = config.voice;\n }\n\n public async generate(text: string, options?: SpeechOptions): Promise<SpeechGenerationResponse> {\n const format = options?.format ?? \"mp3\";\n\n this.logger.debug(LOG_MODULE, \"speech.request\", \"audio.speech.create\", {\n model: this.name,\n characters: text.length,\n });\n\n let response: Response;\n\n try {\n response = await this.client.audio.speech.create(\n {\n model: this.name,\n input: text,\n voice: options?.voice ?? this.defaultVoice ?? \"alloy\",\n response_format: format as OpenAI.Audio.SpeechCreateParams[\"response_format\"],\n ...(options?.speed !== undefined ? { speed: options.speed } : {}),\n ...(options?.instructions !== undefined ? { instructions: options.instructions } : {}),\n },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n this.logger.error(LOG_MODULE, \"speech.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n throw wrapped;\n }\n\n const base64 = Buffer.from(await response.arrayBuffer()).toString(\"base64\");\n\n return {\n audio: { type: \"base64\", base64, mediaType: audioMediaType(format) },\n // The Speech API reports no token usage; spend is priced per\n // character (or per token for gpt-4o-mini-tts) by `ai.speech()`.\n usage: { input: 0, output: 0, total: 0 },\n characters: text.length,\n };\n }\n}\n"],"mappings":";;;;;;AAYA,MAAM,aAAa;;AAGnB,MAAM,wBAAwB;CAAC;CAAS;CAAmB;AAAW;;AAGtE,SAAgB,oBAAoB,MAAuB;CACzD,OAAO,sBAAsB,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC;AACvE;;AAGA,SAAS,eAAe,QAAoC;CAC1D,QAAQ,QAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;;;;;;;;;;;AAeA,IAAa,oBAAb,MAA8D;CAS5D,AAAO,YAAY,QAAgB,QAA4B,WAAmB,UAAU;gBAF1D;EAGhC,IAAI,CAAC,oBAAoB,OAAO,IAAI,GAClC,MAAM,IAAI,oBACR,IAAI,OAAO,KAAK,uIAElB;EAGF,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU,OAAO;EACtB,KAAK,eAAe,OAAO;CAC7B;CAEA,MAAa,SAAS,MAAc,SAA4D;EAC9F,MAAM,SAAS,SAAS,UAAU;EAElC,KAAK,OAAO,MAAM,YAAY,kBAAkB,uBAAuB;GACrE,OAAO,KAAK;GACZ,YAAY,KAAK;EACnB,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,OACxC;IACE,OAAO,KAAK;IACZ,OAAO;IACP,OAAO,SAAS,SAAS,KAAK,gBAAgB;IAC9C,iBAAiB;IACjB,GAAI,SAAS,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;IAC/D,GAAI,SAAS,iBAAiB,SAAY,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;GACtF,GACA,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,UAAU,gBAAgB,MAAM;GACtC,KAAK,OAAO,MAAM,YAAY,gBAAgB,QAAQ,SAAS;IAC7D,MAAM,QAAQ;IACd,SAAS,QAAQ;GACnB,CAAC;GACD,MAAM;EACR;EAIA,OAAO;GACL,OAAO;IAAE,MAAM;IAAU,QAHZ,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC,CAAC,CAAC,SAAS,QAGlC;IAAG,WAAW,eAAe,MAAM;GAAE;GAGnE,OAAO;IAAE,OAAO;IAAG,QAAQ;IAAG,OAAO;GAAE;GACvC,YAAY,KAAK;EACnB;CACF;AACF"}
1
+ {"version":3,"file":"speech.mjs","names":[],"sources":["../../../../../../ai-openai/src/speech.ts"],"sourcesContent":["import {\n type SpeechGenerationResponse,\n type SpeechModelContract,\n type SpeechModelPricing,\n type SpeechOptions,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport type { OpenAISpeechConfig } from \"./config.type\";\nimport { wrapOpenAIError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/** Map a neutral output container hint to its IANA audio media type. */\nfunction audioMediaType(format: string | undefined): string {\n switch (format) {\n case \"opus\":\n return \"audio/opus\";\n case \"aac\":\n return \"audio/aac\";\n case \"flac\":\n return \"audio/flac\";\n case \"wav\":\n return \"audio/wav\";\n case \"pcm\":\n return \"audio/pcm\";\n default:\n return \"audio/mpeg\";\n }\n}\n\n/**\n * OpenAI-backed implementation of `SpeechModelContract` (text-to-speech)\n * via `audio.speech.create`. Standalone primitive — no relation to chat\n * completions or the agent loop. Consumed by the `ai.speech()` verb.\n *\n * **No model-id validation.** `config.name` is forwarded to\n * `audio.speech.create` exactly as given — the constructor never\n * inspects it. OpenAI ships and retires TTS model ids on its own\n * schedule, so an unrecognized id fails at OpenAI (wrapped into the\n * typed `AIError` hierarchy by `generate()`), not here.\n *\n * @example\n * const tts = new OpenAISpeechModel(client, { name: \"tts-1\", voice: \"alloy\" }, \"openai\");\n * const { audio } = await tts.generate(\"Welcome aboard.\");\n */\nexport class OpenAISpeechModel implements SpeechModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: SpeechModelPricing;\n\n private readonly client: OpenAI;\n private readonly defaultVoice?: string;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAISpeechConfig, provider: string = \"openai\") {\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.defaultVoice = config.voice;\n }\n\n public async generate(text: string, options?: SpeechOptions): Promise<SpeechGenerationResponse> {\n const format = options?.format ?? \"mp3\";\n\n this.logger.debug(LOG_MODULE, \"speech.request\", \"audio.speech.create\", {\n model: this.name,\n characters: text.length,\n });\n\n let response: Response;\n\n try {\n response = await this.client.audio.speech.create(\n {\n model: this.name,\n input: text,\n voice: options?.voice ?? this.defaultVoice ?? \"alloy\",\n response_format: format as OpenAI.Audio.SpeechCreateParams[\"response_format\"],\n ...(options?.speed !== undefined ? { speed: options.speed } : {}),\n ...(options?.instructions !== undefined ? { instructions: options.instructions } : {}),\n },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n this.logger.error(LOG_MODULE, \"speech.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n throw wrapped;\n }\n\n const base64 = Buffer.from(await response.arrayBuffer()).toString(\"base64\");\n\n return {\n audio: { type: \"base64\", base64, mediaType: audioMediaType(format) },\n // The Speech API reports no token usage; spend is priced per\n // character (or per token for gpt-4o-mini-tts) by `ai.speech()`.\n usage: { input: 0, output: 0, total: 0 },\n characters: text.length,\n };\n }\n}\n"],"mappings":";;;;;AAWA,MAAM,aAAa;;AAGnB,SAAS,eAAe,QAAoC;CAC1D,QAAQ,QAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;;;;;;;;;;;;;AAiBA,IAAa,oBAAb,MAA8D;CAS5D,AAAO,YAAY,QAAgB,QAA4B,WAAmB,UAAU;gBAF1D;EAGhC,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU,OAAO;EACtB,KAAK,eAAe,OAAO;CAC7B;CAEA,MAAa,SAAS,MAAc,SAA4D;EAC9F,MAAM,SAAS,SAAS,UAAU;EAElC,KAAK,OAAO,MAAM,YAAY,kBAAkB,uBAAuB;GACrE,OAAO,KAAK;GACZ,YAAY,KAAK;EACnB,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,OACxC;IACE,OAAO,KAAK;IACZ,OAAO;IACP,OAAO,SAAS,SAAS,KAAK,gBAAgB;IAC9C,iBAAiB;IACjB,GAAI,SAAS,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;IAC/D,GAAI,SAAS,iBAAiB,SAAY,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;GACtF,GACA,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,UAAU,gBAAgB,MAAM;GACtC,KAAK,OAAO,MAAM,YAAY,gBAAgB,QAAQ,SAAS;IAC7D,MAAM,QAAQ;IACd,SAAS,QAAQ;GACnB,CAAC;GACD,MAAM;EACR;EAIA,OAAO;GACL,OAAO;IAAE,MAAM;IAAU,QAHZ,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC,CAAC,CAAC,SAAS,QAGlC;IAAG,WAAW,eAAe,MAAM;GAAE;GAGnE,OAAO;IAAE,OAAO;IAAG,QAAQ;IAAG,OAAO;GAAE;GACvC,YAAY,KAAK;EACnB;CACF;AACF"}
@@ -3,13 +3,16 @@ import OpenAI from "openai";
3
3
  import { AudioInput, TranscribeOptions, TranscriptionModelContract, TranscriptionModelPricing, TranscriptionResponse } from "@warlock.js/ai";
4
4
 
5
5
  //#region ../ai-openai/src/transcription.d.ts
6
- /** True when `name` is a recognized OpenAI speech-to-text model. */
7
- declare function isOpenAITranscriptionModel(name: string): boolean;
8
6
  /**
9
7
  * OpenAI-backed implementation of `TranscriptionModelContract`
10
8
  * (speech-to-text) via `audio.transcriptions.create`. Consumed by the
11
9
  * `ai.transcribe()` verb.
12
10
  *
11
+ * **No model-id validation.** `config.name` is forwarded to
12
+ * `audio.transcriptions.create` exactly as given — the constructor
13
+ * never inspects it. An unrecognized id fails at OpenAI (wrapped into
14
+ * the typed `AIError` hierarchy by `transcribe()`), not here.
15
+ *
13
16
  * **Response format.** Defaults to `verbose_json` for `whisper-1` (so
14
17
  * the run gets a `duration` + timestamped `segments`) and `json` for
15
18
  * the token-metered `gpt-4o-transcribe` family. Base64 audio is wrapped
@@ -29,5 +32,5 @@ declare class OpenAITranscriptionModel implements TranscriptionModelContract {
29
32
  transcribe(audio: AudioInput, options?: TranscribeOptions): Promise<TranscriptionResponse>;
30
33
  }
31
34
  //#endregion
32
- export { OpenAITranscriptionModel, isOpenAITranscriptionModel };
35
+ export { OpenAITranscriptionModel };
33
36
  //# sourceMappingURL=transcription.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.d.mts","names":[],"sources":["../../../../../../ai-openai/src/transcription.ts"],"mappings":";;;;;;iBAoBgB,0BAAA,CAA2B,IAAY;AAAvD;;;;AAAuD;AAiCvD;;;;;;;;;AAjCA,cAiCa,wBAAA,YAAoC,0BAAA;EAAA,SAC/B,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,yBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,MAAA;cAGf,MAAA,EAAQ,MAAA,EACR,MAAA,EAAQ,yBAAA,EACR,QAAA;EAeW,UAAA,CACX,KAAA,EAAO,UAAA,EACP,OAAA,GAAU,iBAAA,GACT,OAAA,CAAQ,qBAAA;AAAA"}
1
+ {"version":3,"file":"transcription.d.mts","names":[],"sources":["../../../../../../ai-openai/src/transcription.ts"],"mappings":";;;;;;;AAiDA;;;;;;;;;;;;;;;;;cAAa,wBAAA,YAAoC,0BAAA;EAAA,SAC/B,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,yBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,MAAA;cAGf,MAAA,EAAQ,MAAA,EACR,MAAA,EAAQ,yBAAA,EACR,QAAA;EAQW,UAAA,CACX,KAAA,EAAO,UAAA,EACP,OAAA,GAAU,iBAAA,GACT,OAAA,CAAQ,qBAAA;AAAA"}
@@ -1,26 +1,20 @@
1
1
  import { wrapOpenAIError } from "./utils/wrap-openai-error.mjs";
2
2
  import "./utils/index.mjs";
3
3
  import { toFile } from "openai";
4
- import { InvalidRequestError } from "@warlock.js/ai";
5
4
  import { log } from "@warlock.js/logger";
6
5
 
7
6
  //#region ../ai-openai/src/transcription.ts
8
7
  const LOG_MODULE = "ai.openai";
9
- /** Model-id prefixes OpenAI exposes through the **Transcription** (STT) API. */
10
- const TRANSCRIPTION_MODEL_PREFIXES = [
11
- "whisper",
12
- "gpt-4o-transcribe",
13
- "gpt-4o-mini-transcribe"
14
- ];
15
- /** True when `name` is a recognized OpenAI speech-to-text model. */
16
- function isOpenAITranscriptionModel(name) {
17
- return TRANSCRIPTION_MODEL_PREFIXES.some((prefix) => name.startsWith(prefix));
18
- }
19
8
  /**
20
9
  * OpenAI-backed implementation of `TranscriptionModelContract`
21
10
  * (speech-to-text) via `audio.transcriptions.create`. Consumed by the
22
11
  * `ai.transcribe()` verb.
23
12
  *
13
+ * **No model-id validation.** `config.name` is forwarded to
14
+ * `audio.transcriptions.create` exactly as given — the constructor
15
+ * never inspects it. An unrecognized id fails at OpenAI (wrapped into
16
+ * the typed `AIError` hierarchy by `transcribe()`), not here.
17
+ *
24
18
  * **Response format.** Defaults to `verbose_json` for `whisper-1` (so
25
19
  * the run gets a `duration` + timestamped `segments`) and `json` for
26
20
  * the token-metered `gpt-4o-transcribe` family. Base64 audio is wrapped
@@ -33,7 +27,6 @@ function isOpenAITranscriptionModel(name) {
33
27
  var OpenAITranscriptionModel = class {
34
28
  constructor(client, config, provider = "openai") {
35
29
  this.logger = log;
36
- if (!isOpenAITranscriptionModel(config.name)) throw new InvalidRequestError(`"${config.name}" is not a known OpenAI transcription model. Use a \`whisper-1\` / \`gpt-4o-transcribe\` / \`gpt-4o-mini-transcribe\` model with openai.transcribe({ name }).`);
37
30
  this.client = client;
38
31
  this.name = config.name;
39
32
  this.provider = provider;
@@ -90,5 +83,5 @@ var OpenAITranscriptionModel = class {
90
83
  };
91
84
 
92
85
  //#endregion
93
- export { OpenAITranscriptionModel, isOpenAITranscriptionModel };
86
+ export { OpenAITranscriptionModel };
94
87
  //# sourceMappingURL=transcription.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.mjs","names":[],"sources":["../../../../../../ai-openai/src/transcription.ts"],"sourcesContent":["import {\n InvalidRequestError,\n type AudioInput,\n type TranscribeOptions,\n type TranscriptionModelContract,\n type TranscriptionModelPricing,\n type TranscriptionResponse,\n type TranscriptionSegment,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport OpenAI, { toFile } from \"openai\";\nimport type { OpenAITranscriptionConfig } from \"./config.type\";\nimport { wrapOpenAIError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/** Model-id prefixes OpenAI exposes through the **Transcription** (STT) API. */\nconst TRANSCRIPTION_MODEL_PREFIXES = [\"whisper\", \"gpt-4o-transcribe\", \"gpt-4o-mini-transcribe\"] as const;\n\n/** True when `name` is a recognized OpenAI speech-to-text model. */\nexport function isOpenAITranscriptionModel(name: string): boolean {\n return TRANSCRIPTION_MODEL_PREFIXES.some((prefix) => name.startsWith(prefix));\n}\n\n/** Defensive view over the response, whose shape varies by `response_format`. */\ntype RawTranscription = {\n text: string;\n duration?: number;\n language?: string;\n segments?: Array<{ text: string; start?: number; end?: number }>;\n usage?: {\n type?: string;\n seconds?: number;\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n };\n};\n\n/**\n * OpenAI-backed implementation of `TranscriptionModelContract`\n * (speech-to-text) via `audio.transcriptions.create`. Consumed by the\n * `ai.transcribe()` verb.\n *\n * **Response format.** Defaults to `verbose_json` for `whisper-1` (so\n * the run gets a `duration` + timestamped `segments`) and `json` for\n * the token-metered `gpt-4o-transcribe` family. Base64 audio is wrapped\n * in an uploadable via the SDK's `toFile`.\n *\n * @example\n * const stt = new OpenAITranscriptionModel(client, { name: \"whisper-1\" }, \"openai\");\n * const { text } = await stt.transcribe({ base64, mediaType: \"audio/mpeg\" });\n */\nexport class OpenAITranscriptionModel implements TranscriptionModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: TranscriptionModelPricing;\n\n private readonly client: OpenAI;\n private readonly logger: Logger = log;\n\n public constructor(\n client: OpenAI,\n config: OpenAITranscriptionConfig,\n provider: string = \"openai\",\n ) {\n if (!isOpenAITranscriptionModel(config.name)) {\n throw new InvalidRequestError(\n `\"${config.name}\" is not a known OpenAI transcription model. ` +\n \"Use a `whisper-1` / `gpt-4o-transcribe` / `gpt-4o-mini-transcribe` model with openai.transcribe({ name }).\",\n );\n }\n\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n }\n\n public async transcribe(\n audio: AudioInput,\n options?: TranscribeOptions,\n ): Promise<TranscriptionResponse> {\n const isWhisper = this.name.startsWith(\"whisper\");\n const format = options?.format ?? (isWhisper ? \"verbose_json\" : \"json\");\n\n const file = await toFile(Buffer.from(audio.base64, \"base64\"), audio.filename ?? \"audio\", {\n type: audio.mediaType,\n });\n\n this.logger.debug(LOG_MODULE, \"transcription.request\", \"audio.transcriptions.create\", {\n model: this.name,\n format,\n });\n\n let raw: unknown;\n\n try {\n raw = await this.client.audio.transcriptions.create(\n {\n model: this.name,\n file,\n response_format: format as OpenAI.Audio.TranscriptionCreateParams[\"response_format\"],\n ...(options?.language ? { language: options.language } : {}),\n ...(options?.prompt ? { prompt: options.prompt } : {}),\n } as OpenAI.Audio.TranscriptionCreateParamsNonStreaming,\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n this.logger.error(LOG_MODULE, \"transcription.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n throw wrapped;\n }\n\n const response = raw as RawTranscription;\n\n const segments: TranscriptionSegment[] | undefined = response.segments?.map((segment) => ({\n text: segment.text,\n ...(segment.start !== undefined ? { start: segment.start } : {}),\n ...(segment.end !== undefined ? { end: segment.end } : {}),\n }));\n\n const durationSeconds =\n response.duration ?? (response.usage?.type === \"duration\" ? response.usage.seconds : undefined);\n\n const usage =\n response.usage?.type === \"tokens\"\n ? {\n input: response.usage.input_tokens ?? 0,\n output: response.usage.output_tokens ?? 0,\n total: response.usage.total_tokens ?? 0,\n }\n : { input: 0, output: 0, total: 0 };\n\n return {\n text: response.text,\n ...(segments && segments.length > 0 ? { segments } : {}),\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n usage,\n };\n }\n}\n"],"mappings":";;;;;;;AAcA,MAAM,aAAa;;AAGnB,MAAM,+BAA+B;CAAC;CAAW;CAAqB;AAAwB;;AAG9F,SAAgB,2BAA2B,MAAuB;CAChE,OAAO,6BAA6B,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC;AAC9E;;;;;;;;;;;;;;;AA+BA,IAAa,2BAAb,MAA4E;CAQ1E,AAAO,YACL,QACA,QACA,WAAmB,UACnB;gBANgC;EAOhC,IAAI,CAAC,2BAA2B,OAAO,IAAI,GACzC,MAAM,IAAI,oBACR,IAAI,OAAO,KAAK,8JAElB;EAGF,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU,OAAO;CACxB;CAEA,MAAa,WACX,OACA,SACgC;EAChC,MAAM,YAAY,KAAK,KAAK,WAAW,SAAS;EAChD,MAAM,SAAS,SAAS,WAAW,YAAY,iBAAiB;EAEhE,MAAM,OAAO,MAAM,OAAO,OAAO,KAAK,MAAM,QAAQ,QAAQ,GAAG,MAAM,YAAY,SAAS,EACxF,MAAM,MAAM,UACd,CAAC;EAED,KAAK,OAAO,MAAM,YAAY,yBAAyB,+BAA+B;GACpF,OAAO,KAAK;GACZ;EACF,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,MAAM,MAAM,KAAK,OAAO,MAAM,eAAe,OAC3C;IACE,OAAO,KAAK;IACZ;IACA,iBAAiB;IACjB,GAAI,SAAS,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;IAC1D,GAAI,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GACtD,GACA,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,UAAU,gBAAgB,MAAM;GACtC,KAAK,OAAO,MAAM,YAAY,uBAAuB,QAAQ,SAAS;IACpE,MAAM,QAAQ;IACd,SAAS,QAAQ;GACnB,CAAC;GACD,MAAM;EACR;EAEA,MAAM,WAAW;EAEjB,MAAM,WAA+C,SAAS,UAAU,KAAK,aAAa;GACxF,MAAM,QAAQ;GACd,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;GAC9D,GAAI,QAAQ,QAAQ,SAAY,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1D,EAAE;EAEF,MAAM,kBACJ,SAAS,aAAa,SAAS,OAAO,SAAS,aAAa,SAAS,MAAM,UAAU;EAEvF,MAAM,QACJ,SAAS,OAAO,SAAS,WACrB;GACE,OAAO,SAAS,MAAM,gBAAgB;GACtC,QAAQ,SAAS,MAAM,iBAAiB;GACxC,OAAO,SAAS,MAAM,gBAAgB;EACxC,IACA;GAAE,OAAO;GAAG,QAAQ;GAAG,OAAO;EAAE;EAEtC,OAAO;GACL,MAAM,SAAS;GACf,GAAI,YAAY,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;GACtD,GAAI,oBAAoB,SAAY,EAAE,gBAAgB,IAAI,CAAC;GAC3D;EACF;CACF;AACF"}
1
+ {"version":3,"file":"transcription.mjs","names":[],"sources":["../../../../../../ai-openai/src/transcription.ts"],"sourcesContent":["import {\n type AudioInput,\n type TranscribeOptions,\n type TranscriptionModelContract,\n type TranscriptionModelPricing,\n type TranscriptionResponse,\n type TranscriptionSegment,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport OpenAI, { toFile } from \"openai\";\nimport type { OpenAITranscriptionConfig } from \"./config.type\";\nimport { wrapOpenAIError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/** Defensive view over the response, whose shape varies by `response_format`. */\ntype RawTranscription = {\n text: string;\n duration?: number;\n language?: string;\n segments?: Array<{ text: string; start?: number; end?: number }>;\n usage?: {\n type?: string;\n seconds?: number;\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n };\n};\n\n/**\n * OpenAI-backed implementation of `TranscriptionModelContract`\n * (speech-to-text) via `audio.transcriptions.create`. Consumed by the\n * `ai.transcribe()` verb.\n *\n * **No model-id validation.** `config.name` is forwarded to\n * `audio.transcriptions.create` exactly as given — the constructor\n * never inspects it. An unrecognized id fails at OpenAI (wrapped into\n * the typed `AIError` hierarchy by `transcribe()`), not here.\n *\n * **Response format.** Defaults to `verbose_json` for `whisper-1` (so\n * the run gets a `duration` + timestamped `segments`) and `json` for\n * the token-metered `gpt-4o-transcribe` family. Base64 audio is wrapped\n * in an uploadable via the SDK's `toFile`.\n *\n * @example\n * const stt = new OpenAITranscriptionModel(client, { name: \"whisper-1\" }, \"openai\");\n * const { text } = await stt.transcribe({ base64, mediaType: \"audio/mpeg\" });\n */\nexport class OpenAITranscriptionModel implements TranscriptionModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: TranscriptionModelPricing;\n\n private readonly client: OpenAI;\n private readonly logger: Logger = log;\n\n public constructor(\n client: OpenAI,\n config: OpenAITranscriptionConfig,\n provider: string = \"openai\",\n ) {\n this.client = client;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n }\n\n public async transcribe(\n audio: AudioInput,\n options?: TranscribeOptions,\n ): Promise<TranscriptionResponse> {\n const isWhisper = this.name.startsWith(\"whisper\");\n const format = options?.format ?? (isWhisper ? \"verbose_json\" : \"json\");\n\n const file = await toFile(Buffer.from(audio.base64, \"base64\"), audio.filename ?? \"audio\", {\n type: audio.mediaType,\n });\n\n this.logger.debug(LOG_MODULE, \"transcription.request\", \"audio.transcriptions.create\", {\n model: this.name,\n format,\n });\n\n let raw: unknown;\n\n try {\n raw = await this.client.audio.transcriptions.create(\n {\n model: this.name,\n file,\n response_format: format as OpenAI.Audio.TranscriptionCreateParams[\"response_format\"],\n ...(options?.language ? { language: options.language } : {}),\n ...(options?.prompt ? { prompt: options.prompt } : {}),\n } as OpenAI.Audio.TranscriptionCreateParamsNonStreaming,\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n this.logger.error(LOG_MODULE, \"transcription.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n throw wrapped;\n }\n\n const response = raw as RawTranscription;\n\n const segments: TranscriptionSegment[] | undefined = response.segments?.map((segment) => ({\n text: segment.text,\n ...(segment.start !== undefined ? { start: segment.start } : {}),\n ...(segment.end !== undefined ? { end: segment.end } : {}),\n }));\n\n const durationSeconds =\n response.duration ?? (response.usage?.type === \"duration\" ? response.usage.seconds : undefined);\n\n const usage =\n response.usage?.type === \"tokens\"\n ? {\n input: response.usage.input_tokens ?? 0,\n output: response.usage.output_tokens ?? 0,\n total: response.usage.total_tokens ?? 0,\n }\n : { input: 0, output: 0, total: 0 };\n\n return {\n text: response.text,\n ...(segments && segments.length > 0 ? { segments } : {}),\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n usage,\n };\n }\n}\n"],"mappings":";;;;;;AAaA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;AAoCnB,IAAa,2BAAb,MAA4E;CAQ1E,AAAO,YACL,QACA,QACA,WAAmB,UACnB;gBANgC;EAOhC,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU,OAAO;CACxB;CAEA,MAAa,WACX,OACA,SACgC;EAChC,MAAM,YAAY,KAAK,KAAK,WAAW,SAAS;EAChD,MAAM,SAAS,SAAS,WAAW,YAAY,iBAAiB;EAEhE,MAAM,OAAO,MAAM,OAAO,OAAO,KAAK,MAAM,QAAQ,QAAQ,GAAG,MAAM,YAAY,SAAS,EACxF,MAAM,MAAM,UACd,CAAC;EAED,KAAK,OAAO,MAAM,YAAY,yBAAyB,+BAA+B;GACpF,OAAO,KAAK;GACZ;EACF,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,MAAM,MAAM,KAAK,OAAO,MAAM,eAAe,OAC3C;IACE,OAAO,KAAK;IACZ;IACA,iBAAiB;IACjB,GAAI,SAAS,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;IAC1D,GAAI,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GACtD,GACA,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,UAAU,gBAAgB,MAAM;GACtC,KAAK,OAAO,MAAM,YAAY,uBAAuB,QAAQ,SAAS;IACpE,MAAM,QAAQ;IACd,SAAS,QAAQ;GACnB,CAAC;GACD,MAAM;EACR;EAEA,MAAM,WAAW;EAEjB,MAAM,WAA+C,SAAS,UAAU,KAAK,aAAa;GACxF,MAAM,QAAQ;GACd,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;GAC9D,GAAI,QAAQ,QAAQ,SAAY,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1D,EAAE;EAEF,MAAM,kBACJ,SAAS,aAAa,SAAS,OAAO,SAAS,aAAa,SAAS,MAAM,UAAU;EAEvF,MAAM,QACJ,SAAS,OAAO,SAAS,WACrB;GACE,OAAO,SAAS,MAAM,gBAAgB;GACtC,QAAQ,SAAS,MAAM,iBAAiB;GACxC,OAAO,SAAS,MAAM,gBAAgB;EACxC,IACA;GAAE,OAAO;GAAG,QAAQ;GAAG,OAAO;EAAE;EAEtC,OAAO;GACL,MAAM,SAAS;GACf,GAAI,YAAY,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;GACtD,GAAI,oBAAoB,SAAY,EAAE,gBAAgB,IAAI,CAAC;GAC3D;EACF;CACF;AACF"}
package/llms-full.txt CHANGED
@@ -97,7 +97,7 @@ const { data } = await ai.image({ model: gpt, prompt: "a red bicycle", size: "10
97
97
 
98
98
  - `gpt-image-*` is token-metered (price with `{ input, output }`) and always returns base64 bytes — the adapter never sends `response_format` (the API rejects it).
99
99
  - `dall-e-*` is per-image (price with `{ perImage }` / `perImageBySize`); defaults to base64, opt into a hosted URL with `options: { responseFormat: "url" }`.
100
- - A non-image model id (`openai.image({ name: "gpt-4o" })`) throws `InvalidRequestError` at construction. The verb surface (envelope, options, cost-truth) lives in [`@warlock.js/ai/generate-images/SKILL.md`](@warlock.js/ai/generate-images/SKILL.md).
100
+ - The model id is **not validated locally** — `openai.image({ name })` passes it straight to `client.images.generate`, so an id OpenAI does not serve fails at the provider (wrapped into the typed `AIError` hierarchy), not at construction. Same for `openai.speech({ name })` and `openai.transcribe({ name })`. There are no model-id helpers to consult: `isOpenAIImageModel()`, `OPENAI_IMAGE_MODEL_PREFIXES`, `isOpenAISpeechModel()` and `isOpenAITranscriptionModel()` were all removed in 4.13.0 — branch on your own id list if you need one. The verb surface (envelope, options, cost-truth) lives in [`@warlock.js/ai/generate-images/SKILL.md`](@warlock.js/ai/generate-images/SKILL.md).
101
101
 
102
102
  ## Streaming
103
103
 
package/package.json CHANGED
@@ -14,12 +14,12 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "openai": "^6.34.0",
17
- "@warlock.js/logger": "4.12.0"
17
+ "@warlock.js/logger": "4.14.0"
18
18
  },
19
19
  "peerDependencies": {
20
- "@warlock.js/ai": "4.12.0"
20
+ "@warlock.js/ai": "4.14.0"
21
21
  },
22
- "version": "4.12.0",
22
+ "version": "4.14.0",
23
23
  "main": "./cjs/index.cjs",
24
24
  "module": "./esm/index.mjs",
25
25
  "types": "./esm/index.d.mts",
@@ -89,7 +89,7 @@ const { data } = await ai.image({ model: gpt, prompt: "a red bicycle", size: "10
89
89
 
90
90
  - `gpt-image-*` is token-metered (price with `{ input, output }`) and always returns base64 bytes — the adapter never sends `response_format` (the API rejects it).
91
91
  - `dall-e-*` is per-image (price with `{ perImage }` / `perImageBySize`); defaults to base64, opt into a hosted URL with `options: { responseFormat: "url" }`.
92
- - A non-image model id (`openai.image({ name: "gpt-4o" })`) throws `InvalidRequestError` at construction. The verb surface (envelope, options, cost-truth) lives in [`@warlock.js/ai/generate-images/SKILL.md`](@warlock.js/ai/generate-images/SKILL.md).
92
+ - The model id is **not validated locally** — `openai.image({ name })` passes it straight to `client.images.generate`, so an id OpenAI does not serve fails at the provider (wrapped into the typed `AIError` hierarchy), not at construction. Same for `openai.speech({ name })` and `openai.transcribe({ name })`. There are no model-id helpers to consult: `isOpenAIImageModel()`, `OPENAI_IMAGE_MODEL_PREFIXES`, `isOpenAISpeechModel()` and `isOpenAITranscriptionModel()` were all removed in 4.13.0 — branch on your own id list if you need one. The verb surface (envelope, options, cost-truth) lives in [`@warlock.js/ai/generate-images/SKILL.md`](@warlock.js/ai/generate-images/SKILL.md).
93
93
 
94
94
  ## Streaming
95
95
 
@@ -1,30 +0,0 @@
1
- //#region ../ai-openai/src/known-image-models.d.ts
2
- /**
3
- * Model-id prefixes OpenAI exposes through the **Images** API
4
- * (`client.images.generate`). The two live families:
5
- *
6
- * - `gpt-image-*` — token-metered, always returns base64 bytes (no
7
- * `response_format` knob), supports `output_format` + `background`.
8
- * - `dall-e-*` — per-image-metered, returns a URL or base64 via
9
- * `response_format`.
10
- *
11
- * Used by {@link isOpenAIImageModel} for the construction-time guard so
12
- * `openai.image({ name: "gpt-4o" })` fails fast with a curated error
13
- * instead of a downstream 400 — mirroring the embedder/vision guards.
14
- */
15
- declare const OPENAI_IMAGE_MODEL_PREFIXES: readonly ["gpt-image", "dall-e"];
16
- /**
17
- * True when `name` is a recognized OpenAI image-generation model. A
18
- * prefix match (not an exact list) so dated snapshots
19
- * (`gpt-image-1-mini`, `dall-e-3`) are covered without a maintenance
20
- * burden every time OpenAI ships a point release.
21
- *
22
- * @example
23
- * isOpenAIImageModel("gpt-image-1"); // true
24
- * isOpenAIImageModel("dall-e-3"); // true
25
- * isOpenAIImageModel("gpt-4o"); // false
26
- */
27
- declare function isOpenAIImageModel(name: string): boolean;
28
- //#endregion
29
- export { OPENAI_IMAGE_MODEL_PREFIXES, isOpenAIImageModel };
30
- //# sourceMappingURL=known-image-models.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"known-image-models.d.mts","names":[],"sources":["../../../../../../ai-openai/src/known-image-models.ts"],"mappings":";;AAaA;;;;AAA2E;AAa3E;;;;AAA+C;;;cAblC,2BAAA;;;;;;;;;;;;iBAaG,kBAAA,CAAmB,IAAY"}
@@ -1,33 +0,0 @@
1
- //#region ../ai-openai/src/known-image-models.ts
2
- /**
3
- * Model-id prefixes OpenAI exposes through the **Images** API
4
- * (`client.images.generate`). The two live families:
5
- *
6
- * - `gpt-image-*` — token-metered, always returns base64 bytes (no
7
- * `response_format` knob), supports `output_format` + `background`.
8
- * - `dall-e-*` — per-image-metered, returns a URL or base64 via
9
- * `response_format`.
10
- *
11
- * Used by {@link isOpenAIImageModel} for the construction-time guard so
12
- * `openai.image({ name: "gpt-4o" })` fails fast with a curated error
13
- * instead of a downstream 400 — mirroring the embedder/vision guards.
14
- */
15
- const OPENAI_IMAGE_MODEL_PREFIXES = ["gpt-image", "dall-e"];
16
- /**
17
- * True when `name` is a recognized OpenAI image-generation model. A
18
- * prefix match (not an exact list) so dated snapshots
19
- * (`gpt-image-1-mini`, `dall-e-3`) are covered without a maintenance
20
- * burden every time OpenAI ships a point release.
21
- *
22
- * @example
23
- * isOpenAIImageModel("gpt-image-1"); // true
24
- * isOpenAIImageModel("dall-e-3"); // true
25
- * isOpenAIImageModel("gpt-4o"); // false
26
- */
27
- function isOpenAIImageModel(name) {
28
- return OPENAI_IMAGE_MODEL_PREFIXES.some((prefix) => name.startsWith(prefix));
29
- }
30
-
31
- //#endregion
32
- export { OPENAI_IMAGE_MODEL_PREFIXES, isOpenAIImageModel };
33
- //# sourceMappingURL=known-image-models.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"known-image-models.mjs","names":[],"sources":["../../../../../../ai-openai/src/known-image-models.ts"],"sourcesContent":["/**\n * Model-id prefixes OpenAI exposes through the **Images** API\n * (`client.images.generate`). The two live families:\n *\n * - `gpt-image-*` — token-metered, always returns base64 bytes (no\n * `response_format` knob), supports `output_format` + `background`.\n * - `dall-e-*` — per-image-metered, returns a URL or base64 via\n * `response_format`.\n *\n * Used by {@link isOpenAIImageModel} for the construction-time guard so\n * `openai.image({ name: \"gpt-4o\" })` fails fast with a curated error\n * instead of a downstream 400 — mirroring the embedder/vision guards.\n */\nexport const OPENAI_IMAGE_MODEL_PREFIXES = [\"gpt-image\", \"dall-e\"] as const;\n\n/**\n * True when `name` is a recognized OpenAI image-generation model. A\n * prefix match (not an exact list) so dated snapshots\n * (`gpt-image-1-mini`, `dall-e-3`) are covered without a maintenance\n * burden every time OpenAI ships a point release.\n *\n * @example\n * isOpenAIImageModel(\"gpt-image-1\"); // true\n * isOpenAIImageModel(\"dall-e-3\"); // true\n * isOpenAIImageModel(\"gpt-4o\"); // false\n */\nexport function isOpenAIImageModel(name: string): boolean {\n return OPENAI_IMAGE_MODEL_PREFIXES.some((prefix) => name.startsWith(prefix));\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,8BAA8B,CAAC,aAAa,QAAQ;;;;;;;;;;;;AAajE,SAAgB,mBAAmB,MAAuB;CACxD,OAAO,4BAA4B,MAAM,WAAW,KAAK,WAAW,MAAM,CAAC;AAC7E"}