@jini-ai/integrations 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +51 -0
  3. package/dist/composio/bounded-data.d.ts +36 -0
  4. package/dist/composio/bounded-data.d.ts.map +1 -0
  5. package/dist/composio/bounded-data.js +181 -0
  6. package/dist/composio/bounded-data.js.map +1 -0
  7. package/dist/composio/catalog.d.ts +147 -0
  8. package/dist/composio/catalog.d.ts.map +1 -0
  9. package/dist/composio/catalog.js +126 -0
  10. package/dist/composio/catalog.js.map +1 -0
  11. package/dist/composio/composio-config.d.ts +42 -0
  12. package/dist/composio/composio-config.d.ts.map +1 -0
  13. package/dist/composio/composio-config.js +145 -0
  14. package/dist/composio/composio-config.js.map +1 -0
  15. package/dist/composio/composio-descriptions.d.ts +16 -0
  16. package/dist/composio/composio-descriptions.d.ts.map +1 -0
  17. package/dist/composio/composio-descriptions.js +766 -0
  18. package/dist/composio/composio-descriptions.js.map +1 -0
  19. package/dist/composio/composio.d.ts +364 -0
  20. package/dist/composio/composio.d.ts.map +1 -0
  21. package/dist/composio/composio.js +1902 -0
  22. package/dist/composio/composio.js.map +1 -0
  23. package/dist/composio/errors.d.ts +15 -0
  24. package/dist/composio/errors.d.ts.map +1 -0
  25. package/dist/composio/errors.js +16 -0
  26. package/dist/composio/errors.js.map +1 -0
  27. package/dist/composio/file-lock.d.ts +13 -0
  28. package/dist/composio/file-lock.d.ts.map +1 -0
  29. package/dist/composio/file-lock.js +92 -0
  30. package/dist/composio/file-lock.js.map +1 -0
  31. package/dist/composio/index.d.ts +18 -0
  32. package/dist/composio/index.d.ts.map +1 -0
  33. package/dist/composio/index.js +7 -0
  34. package/dist/composio/index.js.map +1 -0
  35. package/dist/composio/json-schema.d.ts +93 -0
  36. package/dist/composio/json-schema.d.ts.map +1 -0
  37. package/dist/composio/json-schema.js +498 -0
  38. package/dist/composio/json-schema.js.map +1 -0
  39. package/dist/composio/output-protection.d.ts +32 -0
  40. package/dist/composio/output-protection.d.ts.map +1 -0
  41. package/dist/composio/output-protection.js +105 -0
  42. package/dist/composio/output-protection.js.map +1 -0
  43. package/dist/composio/service.d.ts +181 -0
  44. package/dist/composio/service.d.ts.map +1 -0
  45. package/dist/composio/service.js +758 -0
  46. package/dist/composio/service.js.map +1 -0
  47. package/dist/media-providers/capability-registry.d.ts +28 -0
  48. package/dist/media-providers/capability-registry.d.ts.map +1 -0
  49. package/dist/media-providers/capability-registry.js +27 -0
  50. package/dist/media-providers/capability-registry.js.map +1 -0
  51. package/dist/media-providers/catalog.d.ts +20 -0
  52. package/dist/media-providers/catalog.d.ts.map +1 -0
  53. package/dist/media-providers/catalog.js +20 -0
  54. package/dist/media-providers/catalog.js.map +1 -0
  55. package/dist/media-providers/dispatch/context.d.ts +21 -0
  56. package/dist/media-providers/dispatch/context.d.ts.map +1 -0
  57. package/dist/media-providers/dispatch/context.js +36 -0
  58. package/dist/media-providers/dispatch/context.js.map +1 -0
  59. package/dist/media-providers/dispatch/credentials.d.ts +4 -0
  60. package/dist/media-providers/dispatch/credentials.d.ts.map +1 -0
  61. package/dist/media-providers/dispatch/credentials.js +25 -0
  62. package/dist/media-providers/dispatch/credentials.js.map +1 -0
  63. package/dist/media-providers/dispatch/engine.d.ts +30 -0
  64. package/dist/media-providers/dispatch/engine.d.ts.map +1 -0
  65. package/dist/media-providers/dispatch/engine.js +208 -0
  66. package/dist/media-providers/dispatch/engine.js.map +1 -0
  67. package/dist/media-providers/dispatch/index.d.ts +25 -0
  68. package/dist/media-providers/dispatch/index.d.ts.map +1 -0
  69. package/dist/media-providers/dispatch/index.js +24 -0
  70. package/dist/media-providers/dispatch/index.js.map +1 -0
  71. package/dist/media-providers/dispatch/openai-compatible.d.ts +44 -0
  72. package/dist/media-providers/dispatch/openai-compatible.d.ts.map +1 -0
  73. package/dist/media-providers/dispatch/openai-compatible.js +214 -0
  74. package/dist/media-providers/dispatch/openai-compatible.js.map +1 -0
  75. package/dist/media-providers/dispatch/providers/aihubmix-shared.d.ts +72 -0
  76. package/dist/media-providers/dispatch/providers/aihubmix-shared.d.ts.map +1 -0
  77. package/dist/media-providers/dispatch/providers/aihubmix-shared.js +131 -0
  78. package/dist/media-providers/dispatch/providers/aihubmix-shared.js.map +1 -0
  79. package/dist/media-providers/dispatch/providers/aihubmix.d.ts +4 -0
  80. package/dist/media-providers/dispatch/providers/aihubmix.d.ts.map +1 -0
  81. package/dist/media-providers/dispatch/providers/aihubmix.js +205 -0
  82. package/dist/media-providers/dispatch/providers/aihubmix.js.map +1 -0
  83. package/dist/media-providers/dispatch/providers/custom-image.d.ts +6 -0
  84. package/dist/media-providers/dispatch/providers/custom-image.d.ts.map +1 -0
  85. package/dist/media-providers/dispatch/providers/custom-image.js +94 -0
  86. package/dist/media-providers/dispatch/providers/custom-image.js.map +1 -0
  87. package/dist/media-providers/dispatch/providers/elevenlabs.d.ts +4 -0
  88. package/dist/media-providers/dispatch/providers/elevenlabs.d.ts.map +1 -0
  89. package/dist/media-providers/dispatch/providers/elevenlabs.js +163 -0
  90. package/dist/media-providers/dispatch/providers/elevenlabs.js.map +1 -0
  91. package/dist/media-providers/dispatch/providers/fishaudio.d.ts +3 -0
  92. package/dist/media-providers/dispatch/providers/fishaudio.d.ts.map +1 -0
  93. package/dist/media-providers/dispatch/providers/fishaudio.js +83 -0
  94. package/dist/media-providers/dispatch/providers/fishaudio.js.map +1 -0
  95. package/dist/media-providers/dispatch/providers/grok.d.ts +15 -0
  96. package/dist/media-providers/dispatch/providers/grok.d.ts.map +1 -0
  97. package/dist/media-providers/dispatch/providers/grok.js +150 -0
  98. package/dist/media-providers/dispatch/providers/grok.js.map +1 -0
  99. package/dist/media-providers/dispatch/providers/imagerouter.d.ts +6 -0
  100. package/dist/media-providers/dispatch/providers/imagerouter.d.ts.map +1 -0
  101. package/dist/media-providers/dispatch/providers/imagerouter.js +131 -0
  102. package/dist/media-providers/dispatch/providers/imagerouter.js.map +1 -0
  103. package/dist/media-providers/dispatch/providers/minimax.d.ts +3 -0
  104. package/dist/media-providers/dispatch/providers/minimax.d.ts.map +1 -0
  105. package/dist/media-providers/dispatch/providers/minimax.js +96 -0
  106. package/dist/media-providers/dispatch/providers/minimax.js.map +1 -0
  107. package/dist/media-providers/dispatch/providers/nanobanana.d.ts +3 -0
  108. package/dist/media-providers/dispatch/providers/nanobanana.d.ts.map +1 -0
  109. package/dist/media-providers/dispatch/providers/nanobanana.js +142 -0
  110. package/dist/media-providers/dispatch/providers/nanobanana.js.map +1 -0
  111. package/dist/media-providers/dispatch/providers/openai.d.ts +4 -0
  112. package/dist/media-providers/dispatch/providers/openai.d.ts.map +1 -0
  113. package/dist/media-providers/dispatch/providers/openai.js +231 -0
  114. package/dist/media-providers/dispatch/providers/openai.js.map +1 -0
  115. package/dist/media-providers/dispatch/providers/openrouter.d.ts +13 -0
  116. package/dist/media-providers/dispatch/providers/openrouter.d.ts.map +1 -0
  117. package/dist/media-providers/dispatch/providers/openrouter.js +152 -0
  118. package/dist/media-providers/dispatch/providers/openrouter.js.map +1 -0
  119. package/dist/media-providers/dispatch/providers/senseaudio.d.ts +4 -0
  120. package/dist/media-providers/dispatch/providers/senseaudio.d.ts.map +1 -0
  121. package/dist/media-providers/dispatch/providers/senseaudio.js +190 -0
  122. package/dist/media-providers/dispatch/providers/senseaudio.js.map +1 -0
  123. package/dist/media-providers/dispatch/providers/volcengine.d.ts +3 -0
  124. package/dist/media-providers/dispatch/providers/volcengine.d.ts.map +1 -0
  125. package/dist/media-providers/dispatch/providers/volcengine.js +95 -0
  126. package/dist/media-providers/dispatch/providers/volcengine.js.map +1 -0
  127. package/dist/media-providers/dispatch/response-parsers.d.ts +44 -0
  128. package/dist/media-providers/dispatch/response-parsers.d.ts.map +1 -0
  129. package/dist/media-providers/dispatch/response-parsers.js +103 -0
  130. package/dist/media-providers/dispatch/response-parsers.js.map +1 -0
  131. package/dist/media-providers/dispatch/ssrf-guard.d.ts +51 -0
  132. package/dist/media-providers/dispatch/ssrf-guard.d.ts.map +1 -0
  133. package/dist/media-providers/dispatch/ssrf-guard.js +215 -0
  134. package/dist/media-providers/dispatch/ssrf-guard.js.map +1 -0
  135. package/dist/media-providers/dispatch/stub.d.ts +5 -0
  136. package/dist/media-providers/dispatch/stub.d.ts.map +1 -0
  137. package/dist/media-providers/dispatch/stub.js +98 -0
  138. package/dist/media-providers/dispatch/stub.js.map +1 -0
  139. package/dist/media-providers/dispatch/types.d.ts +133 -0
  140. package/dist/media-providers/dispatch/types.d.ts.map +1 -0
  141. package/dist/media-providers/dispatch/types.js +2 -0
  142. package/dist/media-providers/dispatch/types.js.map +1 -0
  143. package/dist/media-providers/dispatch/vendor-adapter.d.ts +54 -0
  144. package/dist/media-providers/dispatch/vendor-adapter.d.ts.map +1 -0
  145. package/dist/media-providers/dispatch/vendor-adapter.js +68 -0
  146. package/dist/media-providers/dispatch/vendor-adapter.js.map +1 -0
  147. package/dist/media-providers/dispatch/vendor-registry.d.ts +36 -0
  148. package/dist/media-providers/dispatch/vendor-registry.d.ts.map +1 -0
  149. package/dist/media-providers/dispatch/vendor-registry.js +46 -0
  150. package/dist/media-providers/dispatch/vendor-registry.js.map +1 -0
  151. package/dist/media-providers/index.d.ts +22 -0
  152. package/dist/media-providers/index.d.ts.map +1 -0
  153. package/dist/media-providers/index.js +22 -0
  154. package/dist/media-providers/index.js.map +1 -0
  155. package/dist/media-providers/policy.d.ts +49 -0
  156. package/dist/media-providers/policy.d.ts.map +1 -0
  157. package/dist/media-providers/policy.js +39 -0
  158. package/dist/media-providers/policy.js.map +1 -0
  159. package/dist/media-providers/providers.d.ts +33 -0
  160. package/dist/media-providers/providers.d.ts.map +1 -0
  161. package/dist/media-providers/providers.js +200 -0
  162. package/dist/media-providers/providers.js.map +1 -0
  163. package/dist/media-providers/seed.d.ts +14 -0
  164. package/dist/media-providers/seed.d.ts.map +1 -0
  165. package/dist/media-providers/seed.js +109 -0
  166. package/dist/media-providers/seed.js.map +1 -0
  167. package/dist/media-providers/sqlite-task-store.d.ts +29 -0
  168. package/dist/media-providers/sqlite-task-store.d.ts.map +1 -0
  169. package/dist/media-providers/sqlite-task-store.js +234 -0
  170. package/dist/media-providers/sqlite-task-store.js.map +1 -0
  171. package/dist/media-providers/staging.d.ts +29 -0
  172. package/dist/media-providers/staging.d.ts.map +1 -0
  173. package/dist/media-providers/staging.js +141 -0
  174. package/dist/media-providers/staging.js.map +1 -0
  175. package/dist/media-providers/task-store.d.ts +94 -0
  176. package/dist/media-providers/task-store.d.ts.map +1 -0
  177. package/dist/media-providers/task-store.js +160 -0
  178. package/dist/media-providers/task-store.js.map +1 -0
  179. package/dist/media-providers/tokens.d.ts +7 -0
  180. package/dist/media-providers/tokens.d.ts.map +1 -0
  181. package/dist/media-providers/tokens.js +13 -0
  182. package/dist/media-providers/tokens.js.map +1 -0
  183. package/dist/media-providers/types.d.ts +123 -0
  184. package/dist/media-providers/types.d.ts.map +1 -0
  185. package/dist/media-providers/types.js +12 -0
  186. package/dist/media-providers/types.js.map +1 -0
  187. package/dist/media-providers/video-request.d.ts +62 -0
  188. package/dist/media-providers/video-request.d.ts.map +1 -0
  189. package/dist/media-providers/video-request.js +219 -0
  190. package/dist/media-providers/video-request.js.map +1 -0
  191. package/package.json +93 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Provider: AIHubMix — an OpenAI-wire-compatible aggregator gateway.
3
+ * `renderAIHubMixImage` (`POST /images/generations`, OpenAI shape) with an
4
+ * internal redirect to a Gemini-native path for gemini/imagen-family
5
+ * catalog ids, and `renderAIHubMixTTS` (`POST /audio/speech`, OpenAI
6
+ * shape). Ported near-verbatim from Open Design's
7
+ * `apps/daemon/src/media/index.ts` `renderAIHubMixImage`/
8
+ * `renderAIHubMixGeminiImage`/`renderAIHubMixTTS` — see `source-map.md`.
9
+ *
10
+ * The non-Gemini image path and the TTS path route through
11
+ * `openai-compatible.ts`'s shared helpers since AIHubMix's default wire
12
+ * shape is genuinely OpenAI-compatible (the whole point of the
13
+ * aggregator). The Gemini-family image path does not — see
14
+ * `aihubmix-shared.ts`'s `aihubmixGeminiImageBytes` doc comment.
15
+ *
16
+ * `renderAIHubMixImage`'s `entry.url` fallback is downloaded through
17
+ * `ssrf-guard.ts`'s `assertAndFetchExternalAsset`, matching the real
18
+ * origin exactly and `senseaudio.ts`'s identical reasoning: the URL is
19
+ * attacker-controllable inside an otherwise-successful response.
20
+ *
21
+ * Dropped from the origin's no-credential error messages (both exported
22
+ * functions): the `OD_`-prefixed override env var
23
+ * (`OD_AIHUBMIX_API_KEY`) — `providers.ts`'s
24
+ * `PROVIDER_CREDENTIAL_ENV_VARS.aihubmix` already de-branded this to plain
25
+ * `AIHUBMIX_API_KEY`.
26
+ *
27
+ * 2026-07-21: migrated onto the generic vendor-adapter dispatch engine
28
+ * (`vendor-adapter.ts`/`vendor-registry.ts`) — two adapters registered on
29
+ * the same `aihubmix` provider id (`image`/`audio:speech`). External
30
+ * behavior is unchanged (verified against `aihubmix.test.ts`, which
31
+ * asserts URL/body/header/error-message/providerNote shape and passes
32
+ * unmodified) — only the internal implementation moved.
33
+ *
34
+ * The image adapter's Gemini-vs-OpenAI branch is a genuine design decision
35
+ * this port had to make, not a mechanical translation: the origin decides
36
+ * which wire shape to use *before* any network call (a pure function of
37
+ * `wireModel` via `classifyAIHubMixModel`), so `buildRequest` makes that
38
+ * same decision up front and returns the ONE real request for whichever
39
+ * endpoint is correct — never the OpenAI endpoint for a Gemini model (that
40
+ * combination fails on AIHubMix's side, see `aihubmix-shared.ts`'s
41
+ * `aihubmixGeminiImageBytes` doc comment) — tagging the choice in `meta` so
42
+ * `parseResponse` decodes the one response that comes back. This is
43
+ * deliberately NOT `aihubmix-shared.ts`'s `aihubmixGeminiImageBytes`
44
+ * reused as a black box: that helper bundles its own `fetch` inside it,
45
+ * which would mean either a second, wasted real network call (fetching the
46
+ * OpenAI endpoint just to discard the response, then a real Gemini fetch)
47
+ * or reimplementing its URL/header building a second time just to satisfy
48
+ * an unused `doFetch` parameter — neither is a legitimate use of the
49
+ * generic harness's "one fetch" contract. Instead this file's `parseResponse`
50
+ * replicates `aihubmixGeminiImageBytes`'s response-decoding logic directly
51
+ * against the harness's already-fetched `Response` (same error messages,
52
+ * same candidate/part walk, verified line-for-line against that function) —
53
+ * `aihubmixGeminiImageBytes` itself stays exported and independently
54
+ * covered by `aihubmix-shared.test.ts`, just no longer called from here.
55
+ */
56
+ import { buildOpenAIImageUrl, buildOpenAISpeechUrl, openaiSizeFor, OPENAI_TTS_VOICES, resolveSpeechFormat, truncate, withRequestInit } from '../openai-compatible.js';
57
+ import { createRawBytesParser } from '../response-parsers.js';
58
+ import { assertAndFetchExternalAsset } from '../ssrf-guard.js';
59
+ import { dispatchVendorRequest, requireApiKey } from '../vendor-adapter.js';
60
+ import { mediaVendorRegistry } from '../vendor-registry.js';
61
+ import { aihubmixAppCodeHeader, aihubmixGeminiImageUrl, aihubmixHeaders, AIHUBMIX_DEFAULT_BASE_URL, aihubmixWireModel, classifyAIHubMixModel } from './aihubmix-shared.js';
62
+ const NO_CREDENTIAL_MESSAGE = 'no AIHubMix credential — configure an API key or set AIHUBMIX_API_KEY.';
63
+ const aihubmixImageAdapter = {
64
+ requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
65
+ buildRequest(ctx, credentials) {
66
+ const apiKey = credentials.apiKey; // requireCredential already validated this.
67
+ const baseUrl = credentials.baseUrl || AIHUBMIX_DEFAULT_BASE_URL;
68
+ const wireModel = aihubmixWireModel(credentials.model || ctx.wireModel);
69
+ if (classifyAIHubMixModel(wireModel) === 'gemini') {
70
+ const aspect = ctx.aspect || '1:1';
71
+ return {
72
+ url: aihubmixGeminiImageUrl(baseUrl, wireModel),
73
+ init: withRequestInit(ctx, {
74
+ method: 'POST',
75
+ redirect: 'error',
76
+ headers: {
77
+ 'content-type': 'application/json',
78
+ 'x-goog-api-key': apiKey,
79
+ ...aihubmixAppCodeHeader(),
80
+ },
81
+ body: JSON.stringify({
82
+ contents: [{ role: 'user', parts: [{ text: ctx.prompt || 'A high-quality reference image.' }] }],
83
+ generationConfig: {
84
+ responseModalities: ['TEXT', 'IMAGE'],
85
+ imageConfig: { aspectRatio: aspect },
86
+ },
87
+ }),
88
+ }),
89
+ meta: { kind: 'gemini', wireModel, aspect },
90
+ };
91
+ }
92
+ const body = {
93
+ model: wireModel,
94
+ prompt: ctx.prompt || 'A high-quality reference image.',
95
+ n: 1,
96
+ size: openaiSizeFor(wireModel, ctx.aspect),
97
+ };
98
+ if (wireModel.startsWith('dall-e-')) {
99
+ body.response_format = 'b64_json';
100
+ body.quality = wireModel === 'dall-e-3' ? 'hd' : 'standard';
101
+ }
102
+ else {
103
+ body.quality = 'high';
104
+ }
105
+ return {
106
+ url: buildOpenAIImageUrl(baseUrl, false),
107
+ init: withRequestInit(ctx, {
108
+ method: 'POST',
109
+ headers: { 'content-type': 'application/json', ...aihubmixHeaders(apiKey) },
110
+ body: JSON.stringify(body),
111
+ }),
112
+ meta: { kind: 'openai', wireModel },
113
+ };
114
+ },
115
+ async parseResponse(resp, ctx, request) {
116
+ if (request.meta.kind === 'gemini') {
117
+ const { wireModel, aspect } = request.meta;
118
+ if (!resp.ok) {
119
+ const text = await resp.text().catch(() => '');
120
+ throw new Error(`aihubmix image (gemini) ${resp.status}: ${text.slice(0, 240)}`);
121
+ }
122
+ const data = (await resp.json());
123
+ const parts = data.candidates?.[0]?.content?.parts ?? [];
124
+ const b64 = parts.map((p) => p.inlineData?.data ?? p.inline_data?.data).find((d) => typeof d === 'string' && d.length > 0);
125
+ if (!b64) {
126
+ throw new Error(`aihubmix gemini image response had no inline image data: ${JSON.stringify(data).slice(0, 200)}`);
127
+ }
128
+ const bytes = Buffer.from(b64, 'base64');
129
+ return {
130
+ bytes,
131
+ providerNote: `aihubmix/${wireModel} · ${aspect} · ${bytes.length} bytes (gemini-native)`,
132
+ suggestedExt: '.png',
133
+ };
134
+ }
135
+ const { wireModel } = request.meta;
136
+ const text = await resp.text();
137
+ if (!resp.ok) {
138
+ throw new Error(`aihubmix ${resp.status}: ${truncate(text, 240)}`);
139
+ }
140
+ let data;
141
+ try {
142
+ data = JSON.parse(text);
143
+ }
144
+ catch {
145
+ throw new Error(`aihubmix non-JSON response: ${truncate(text, 200)}`);
146
+ }
147
+ const entry = Array.isArray(data.data) ? data.data[0] : undefined;
148
+ if (!entry)
149
+ throw new Error('aihubmix response had no data[0]');
150
+ let bytes;
151
+ if (entry.b64_json) {
152
+ bytes = Buffer.from(entry.b64_json, 'base64');
153
+ }
154
+ else if (entry.url) {
155
+ const imgResp = await assertAndFetchExternalAsset(entry.url, withRequestInit(ctx));
156
+ if (!imgResp.ok)
157
+ throw new Error(`aihubmix image fetch ${imgResp.status}`);
158
+ bytes = Buffer.from(await imgResp.arrayBuffer());
159
+ }
160
+ else {
161
+ throw new Error('aihubmix response had neither b64_json nor url');
162
+ }
163
+ return {
164
+ bytes,
165
+ providerNote: `aihubmix/${wireModel} · ${ctx.aspect} · ${bytes.length} bytes`,
166
+ suggestedExt: '.png',
167
+ };
168
+ },
169
+ };
170
+ mediaVendorRegistry.register('aihubmix', 'image', aihubmixImageAdapter);
171
+ export async function renderAIHubMixImage(ctx, credentials) {
172
+ return dispatchVendorRequest(aihubmixImageAdapter, ctx, credentials);
173
+ }
174
+ const aihubmixTTSAdapter = {
175
+ requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
176
+ buildRequest(ctx, credentials) {
177
+ const apiKey = credentials.apiKey; // requireCredential already validated this.
178
+ const baseUrl = credentials.baseUrl || AIHUBMIX_DEFAULT_BASE_URL;
179
+ const wireModel = aihubmixWireModel(credentials.model || ctx.wireModel);
180
+ const format = resolveSpeechFormat(ctx.speechFormat);
181
+ const text = (ctx.prompt && ctx.prompt.trim()) || 'This is a test.';
182
+ const requestedVoice = (ctx.voice && ctx.voice.trim()) || '';
183
+ const voice = requestedVoice && OPENAI_TTS_VOICES.has(requestedVoice) ? requestedVoice : 'alloy';
184
+ return {
185
+ url: buildOpenAISpeechUrl(baseUrl, false),
186
+ init: withRequestInit(ctx, {
187
+ method: 'POST',
188
+ headers: { 'content-type': 'application/json', ...aihubmixHeaders(apiKey) },
189
+ body: JSON.stringify({ model: wireModel, input: text, voice, response_format: format }),
190
+ }),
191
+ meta: { wireModel, voice, format },
192
+ };
193
+ },
194
+ parseResponse: createRawBytesParser({
195
+ errorTag: 'aihubmix speech',
196
+ zeroBytesMessage: 'aihubmix speech returned zero bytes',
197
+ note: (bytes, meta) => `aihubmix/${meta.wireModel} · ${meta.voice} · ${meta.format} · ${bytes.length} bytes`,
198
+ suggestedExt: (_bytes, meta) => (meta.format === 'opus' ? '.ogg' : `.${meta.format}`),
199
+ }),
200
+ };
201
+ mediaVendorRegistry.register('aihubmix', 'audio:speech', aihubmixTTSAdapter);
202
+ export async function renderAIHubMixTTS(ctx, credentials) {
203
+ return dispatchVendorRequest(aihubmixTTSAdapter, ctx, credentials);
204
+ }
205
+ //# sourceMappingURL=aihubmix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aihubmix.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/aihubmix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ProviderCredentials, RenderContext, RenderResult } from '../types.js';
2
+ export declare const CUSTOM_IMAGE_MODEL_ID = "custom-image";
3
+ export declare function renderCustomOpenAIImage(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
4
+ /** Whether the caller's `custom-image` credentials should override an `openai`-provider request (same model configured on both). */
5
+ export declare function customImageOverridesOpenAIModel(ctx: RenderContext, credentials: ProviderCredentials | null): credentials is ProviderCredentials;
6
+ //# sourceMappingURL=custom-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-image.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/custom-image.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKpF,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AA8DpD,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAEzH;AAED,oIAAoI;AACpI,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,IAAI,GAAG,WAAW,IAAI,mBAAmB,CAK/I"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Provider: `custom-image` — a user-supplied OpenAI-compatible
3
+ * `/v1/images/generations` + `/v1/images/edits` endpoint. Ported
4
+ * near-verbatim from Open Design's `apps/daemon/src/media/index.ts`
5
+ * `renderCustomOpenAIImage`/`customImageOverridesOpenAIModel` — see
6
+ * `source-map.md`.
7
+ *
8
+ * 2026-07-21: migrated onto the generic vendor-adapter dispatch engine
9
+ * (`vendor-adapter.ts`/`vendor-registry.ts`). External behavior is
10
+ * unchanged (verified against `custom-image.test.ts`, which asserts
11
+ * URL/body/header/error-message/providerNote shape and passes unmodified)
12
+ * — only the internal implementation moved into a registered
13
+ * `VendorAdapter`. Unlike every other migrated vendor, `apiKey` is
14
+ * OPTIONAL here (a self-hosted gateway may need no auth at all), so this
15
+ * adapter has no `requireCredential` guard — its two required-field checks
16
+ * (`baseUrl`, resolved `wireModel`) instead live at the top of
17
+ * `buildRequest`, which throws synchronously before any request is built —
18
+ * the same "abort before the fetch" effect a `requireCredential` guard
19
+ * gets, just expressed as a plain throw since the check needs `ctx` (to
20
+ * resolve `wireModel`), which `requireCredential`'s signature doesn't
21
+ * receive.
22
+ *
23
+ * Not routed through `response-parsers.ts`'s factories — this vendor's
24
+ * `{ data: [...] }` JSON envelope isn't the "no envelope" raw-bytes shape
25
+ * either factory covers; it reuses `openai-compatible.ts`'s
26
+ * `parseOpenAICompatibleJson`/`bytesFromOpenAICompatibleData` helpers
27
+ * directly instead, same as it did before this pass.
28
+ */
29
+ import { buildOpenAIImageEditUrl, buildOpenAIImageUrl, bytesFromOpenAICompatibleData, openaiSizeFor, parseOpenAICompatibleJson, sniffImageExt, withRequestInit } from '../openai-compatible.js';
30
+ import { dispatchVendorRequest } from '../vendor-adapter.js';
31
+ import { mediaVendorRegistry } from '../vendor-registry.js';
32
+ export const CUSTOM_IMAGE_MODEL_ID = 'custom-image';
33
+ const customImageAdapter = {
34
+ buildRequest(ctx, credentials) {
35
+ const baseUrl = (credentials.baseUrl || '').trim();
36
+ if (!baseUrl) {
37
+ throw new Error('Custom Image API base URL required — configure an OpenAI-compatible /v1/images/generations or /v1/images/edits endpoint');
38
+ }
39
+ const wireModel = (credentials.model || (ctx.wireModel !== CUSTOM_IMAGE_MODEL_ID ? ctx.wireModel : '')).trim();
40
+ if (!wireModel) {
41
+ throw new Error('Custom Image API model required — configure the provider model');
42
+ }
43
+ const headers = {
44
+ 'content-type': 'application/json',
45
+ };
46
+ if (credentials.apiKey) {
47
+ headers.authorization = `Bearer ${credentials.apiKey}`;
48
+ }
49
+ const size = openaiSizeFor('gpt-image-1', ctx.aspect);
50
+ const body = {
51
+ prompt: ctx.prompt || 'A high-quality reference image.',
52
+ model: wireModel,
53
+ n: 1,
54
+ size,
55
+ };
56
+ let url = buildOpenAIImageUrl(baseUrl, false);
57
+ if (ctx.imageRef?.dataUrl) {
58
+ body.response_format = 'b64_json';
59
+ body.images = [{ image_url: ctx.imageRef.dataUrl }];
60
+ url = buildOpenAIImageEditUrl(baseUrl);
61
+ }
62
+ return {
63
+ url,
64
+ init: withRequestInit(ctx, {
65
+ method: 'POST',
66
+ headers,
67
+ body: JSON.stringify(body),
68
+ }),
69
+ meta: { wireModel, size },
70
+ };
71
+ },
72
+ async parseResponse(resp, ctx, request) {
73
+ const data = await parseOpenAICompatibleJson(resp, 'custom image');
74
+ const bytes = await bytesFromOpenAICompatibleData(data, 'custom image', ctx.requestInit);
75
+ return {
76
+ bytes,
77
+ providerNote: `custom-image/${request.meta.wireModel} · ${request.meta.size} · ${bytes.length} bytes`,
78
+ suggestedExt: sniffImageExt(bytes),
79
+ };
80
+ },
81
+ };
82
+ mediaVendorRegistry.register('custom-image', 'image', customImageAdapter);
83
+ export async function renderCustomOpenAIImage(ctx, credentials) {
84
+ return dispatchVendorRequest(customImageAdapter, ctx, credentials);
85
+ }
86
+ /** Whether the caller's `custom-image` credentials should override an `openai`-provider request (same model configured on both). */
87
+ export function customImageOverridesOpenAIModel(ctx, credentials) {
88
+ const baseUrl = credentials?.baseUrl?.trim();
89
+ const model = credentials?.model?.trim();
90
+ if (!baseUrl || !model)
91
+ return false;
92
+ return model === ctx.model || model === ctx.wireModel;
93
+ }
94
+ //# sourceMappingURL=custom-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-image.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/custom-image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ProviderCredentials, RenderContext, RenderResult } from '../types.js';
2
+ export declare function renderElevenLabsTTS(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
3
+ export declare function renderElevenLabsSfx(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
4
+ //# sourceMappingURL=elevenlabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elevenlabs.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/elevenlabs.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkGpF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAErH;AAmDD,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAErH"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Provider: ElevenLabs — v3 text-to-speech (`renderElevenLabsTTS`) and
3
+ * text-to-sound-effect (`renderElevenLabsSfx`), both synchronous. Ported
4
+ * near-verbatim from Open Design's `apps/daemon/src/media/index.ts`
5
+ * `renderElevenLabsTTS`/`renderElevenLabsSfx` — see `source-map.md`.
6
+ *
7
+ * Neither endpoint returns the OpenAI-images/audio wire shape (both are
8
+ * ElevenLabs-specific JSON-body-in, raw-audio-bytes-out POSTs), so this
9
+ * does not route through `openai-compatible.ts`'s OpenAI-shape helpers —
10
+ * only the generic `truncate`/`withRequestInit` are reused.
11
+ *
12
+ * Dropped from the origin's no-credential error message: the `OD_`-
13
+ * prefixed project-scoped override env var (`OD_ELEVENLABS_API_KEY`) —
14
+ * `providers.ts`'s `PROVIDER_CREDENTIAL_ENV_VARS.elevenlabs` already
15
+ * de-branded this to plain `ELEVENLABS_API_KEY`, matching every other
16
+ * ported provider.
17
+ *
18
+ * Reworded from the origin: both prompt-validation error messages
19
+ * referenced a `--prompt` CLI flag ("Pass --prompt before retrying" /
20
+ * "Shorten --prompt before retrying") — an assumption that the caller is
21
+ * OD's own `hermes` CLI. This package's `MediaGenerationRequest.prompt` is
22
+ * a plain request field with no CLI-flag concept attached to it for every
23
+ * possible host, so both messages were reworded to describe the
24
+ * constraint without assuming how the caller sets it. The underlying
25
+ * validation (non-empty prompt; SFX prompt <= 450 chars) is unchanged.
26
+ *
27
+ * 2026-07-21: migrated onto the generic vendor-adapter dispatch engine
28
+ * (`vendor-adapter.ts`/`vendor-registry.ts`) — two adapters registered on
29
+ * the same `elevenlabs` provider id (`audio:speech`/`audio:sfx`). External
30
+ * behavior is unchanged (verified against `elevenlabs.test.ts`, which
31
+ * asserts URL/body/header/error-message/providerNote shape and passes
32
+ * unmodified) — only the internal implementation moved. Both prompt-
33
+ * validation checks (non-empty; SFX length cap) run inside `buildRequest`,
34
+ * which throws synchronously before any request is built — the same
35
+ * "abort before the fetch" effect as `openai.ts`'s explicit no-credential
36
+ * throw. Both responses are raw bytes with no envelope, the same shape
37
+ * `fishaudio.ts`/`openai.ts`'s speech renderer return, so both now share
38
+ * `response-parsers.ts`'s `createRawBytesParser`.
39
+ */
40
+ import { withRequestInit } from '../openai-compatible.js';
41
+ import { createRawBytesParser } from '../response-parsers.js';
42
+ import { dispatchVendorRequest, requireApiKey } from '../vendor-adapter.js';
43
+ import { mediaVendorRegistry } from '../vendor-registry.js';
44
+ const ELEVENLABS_DEFAULT_BASE_URL = 'https://api.elevenlabs.io';
45
+ const ELEVENLABS_DEFAULT_VOICE_ID = '21m00Tcm4TlvDq8ikWAM';
46
+ const ELEVENLABS_TTS_MODEL_MAP = {
47
+ 'elevenlabs-v3': 'eleven_v3',
48
+ };
49
+ const ELEVENLABS_SFX_MODEL_MAP = {
50
+ 'elevenlabs-sfx': 'eleven_text_to_sound_v2',
51
+ };
52
+ const ELEVENLABS_SFX_MAX_PROMPT_CHARS = 450;
53
+ const ELEVENLABS_SFX_DEFAULT_PROMPT_INFLUENCE = 0.3;
54
+ const NO_CREDENTIAL_MESSAGE = 'no ElevenLabs credential — configure an API key or set ELEVENLABS_API_KEY.';
55
+ function clampElevenLabsSfxDuration(value) {
56
+ if (typeof value !== 'number' || !Number.isFinite(value))
57
+ return 5;
58
+ return Math.min(30, Math.max(0.5, value));
59
+ }
60
+ function clampElevenLabsSfxPromptInfluence(value) {
61
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
62
+ return ELEVENLABS_SFX_DEFAULT_PROMPT_INFLUENCE;
63
+ }
64
+ return Math.min(1, Math.max(0, value));
65
+ }
66
+ function requireElevenLabsPrompt(text, kind) {
67
+ const trimmed = text.trim();
68
+ if (!trimmed) {
69
+ throw new Error(`ElevenLabs ${kind} prompt must not be empty.`);
70
+ }
71
+ return trimmed;
72
+ }
73
+ function assertElevenLabsSfxPromptLength(text) {
74
+ const promptChars = Array.from(text).length;
75
+ if (promptChars > ELEVENLABS_SFX_MAX_PROMPT_CHARS) {
76
+ throw new Error(`ElevenLabs SFX prompt exceeds ${ELEVENLABS_SFX_MAX_PROMPT_CHARS} characters (${promptChars}). Shorten the prompt before retrying.`);
77
+ }
78
+ }
79
+ const elevenLabsTTSAdapter = {
80
+ requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
81
+ buildRequest(ctx, credentials) {
82
+ const apiKey = credentials.apiKey; // requireCredential already validated this.
83
+ const baseUrl = (credentials.baseUrl || ELEVENLABS_DEFAULT_BASE_URL).replace(/\/$/, '');
84
+ const wireModel = ELEVENLABS_TTS_MODEL_MAP[ctx.model] || ctx.model;
85
+ const text = requireElevenLabsPrompt(ctx.prompt, 'TTS');
86
+ const voiceId = (ctx.voice && ctx.voice.trim()) || ELEVENLABS_DEFAULT_VOICE_ID;
87
+ const body = {
88
+ text,
89
+ model_id: wireModel,
90
+ voice_settings: {
91
+ stability: 1,
92
+ similarity_boost: 1,
93
+ style: 0,
94
+ speed: 1,
95
+ use_speaker_boost: true,
96
+ },
97
+ };
98
+ return {
99
+ url: `${baseUrl}/v1/text-to-speech/${encodeURIComponent(voiceId)}?output_format=mp3_44100_128`,
100
+ init: withRequestInit(ctx, {
101
+ method: 'POST',
102
+ headers: {
103
+ 'xi-api-key': apiKey,
104
+ 'content-type': 'application/json',
105
+ },
106
+ body: JSON.stringify(body),
107
+ }),
108
+ meta: { wireModel, voiceId },
109
+ };
110
+ },
111
+ parseResponse: createRawBytesParser({
112
+ errorTag: 'elevenlabs tts',
113
+ zeroBytesMessage: 'elevenlabs tts returned zero bytes',
114
+ note: (bytes, meta) => `elevenlabs/${meta.wireModel} · ${meta.voiceId} · ${bytes.length} bytes`,
115
+ suggestedExt: '.mp3',
116
+ }),
117
+ };
118
+ mediaVendorRegistry.register('elevenlabs', 'audio:speech', elevenLabsTTSAdapter);
119
+ export async function renderElevenLabsTTS(ctx, credentials) {
120
+ return dispatchVendorRequest(elevenLabsTTSAdapter, ctx, credentials);
121
+ }
122
+ const elevenLabsSfxAdapter = {
123
+ requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
124
+ buildRequest(ctx, credentials) {
125
+ const apiKey = credentials.apiKey; // requireCredential already validated this.
126
+ const baseUrl = (credentials.baseUrl || ELEVENLABS_DEFAULT_BASE_URL).replace(/\/$/, '');
127
+ const wireModel = ELEVENLABS_SFX_MODEL_MAP[ctx.model] || ctx.model;
128
+ const text = requireElevenLabsPrompt(ctx.prompt, 'SFX');
129
+ assertElevenLabsSfxPromptLength(text);
130
+ const durationSeconds = clampElevenLabsSfxDuration(ctx.duration);
131
+ const promptInfluence = clampElevenLabsSfxPromptInfluence(ctx.promptInfluence);
132
+ const body = {
133
+ text,
134
+ duration_seconds: durationSeconds,
135
+ prompt_influence: promptInfluence,
136
+ ...(ctx.loop ? { loop: true } : {}),
137
+ model_id: wireModel,
138
+ };
139
+ return {
140
+ url: `${baseUrl}/v1/sound-generation?output_format=mp3_44100_128`,
141
+ init: withRequestInit(ctx, {
142
+ method: 'POST',
143
+ headers: {
144
+ 'xi-api-key': apiKey,
145
+ 'content-type': 'application/json',
146
+ },
147
+ body: JSON.stringify(body),
148
+ }),
149
+ meta: { wireModel, durationSeconds, loop: ctx.loop },
150
+ };
151
+ },
152
+ parseResponse: createRawBytesParser({
153
+ errorTag: 'elevenlabs sfx',
154
+ zeroBytesMessage: 'elevenlabs sfx returned zero bytes',
155
+ note: (bytes, meta) => `elevenlabs/${meta.wireModel} · ${meta.durationSeconds}s${meta.loop ? ' · loop' : ''} · ${bytes.length} bytes`,
156
+ suggestedExt: '.mp3',
157
+ }),
158
+ };
159
+ mediaVendorRegistry.register('elevenlabs', 'audio:sfx', elevenLabsSfxAdapter);
160
+ export async function renderElevenLabsSfx(ctx, credentials) {
161
+ return dispatchVendorRequest(elevenLabsSfxAdapter, ctx, credentials);
162
+ }
163
+ //# sourceMappingURL=elevenlabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elevenlabs.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/elevenlabs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ProviderCredentials, RenderContext, RenderResult } from '../types.js';
2
+ export declare function renderFishAudioTTS(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
3
+ //# sourceMappingURL=fishaudio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fishaudio.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/fishaudio.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqEpF,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAEpH"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Provider: FishAudio — Speech-1.x family text-to-speech (synchronous,
3
+ * `POST /v1/tts`). Ported near-verbatim from Open Design's
4
+ * `apps/daemon/src/media/index.ts` `renderFishAudioTTS` — see
5
+ * `source-map.md`.
6
+ *
7
+ * 2026-07-21: migrated onto the generic vendor-adapter dispatch engine
8
+ * (`vendor-adapter.ts`/`vendor-registry.ts`). External behavior (URL,
9
+ * body, headers, every error message, `providerNote` format) is unchanged
10
+ * — only the internal implementation moved into a registered
11
+ * `VendorAdapter`. Its response is raw bytes with no envelope, the same
12
+ * shape `openai.ts`'s `renderOpenAISpeech` returns, so both now share
13
+ * `response-parsers.ts`'s `createRawBytesParser`.
14
+ *
15
+ * Returns raw audio bytes directly (not wrapped in JSON, unlike
16
+ * MiniMax/SenseAudio's hex-in-JSON shape, and not the OpenAI
17
+ * `/audio/speech` shape either), so this reuses only the generic
18
+ * `withRequestInit` helper from `openai-compatible.ts`.
19
+ *
20
+ * Dropped from the origin's no-credential error message: the `OD_`-
21
+ * prefixed override env var (`OD_FISHAUDIO_API_KEY`) — `providers.ts`'s
22
+ * `PROVIDER_CREDENTIAL_ENV_VARS.fishaudio` already de-branded this to
23
+ * plain `FISH_AUDIO_API_KEY`.
24
+ */
25
+ import { withRequestInit } from '../openai-compatible.js';
26
+ import { createRawBytesParser } from '../response-parsers.js';
27
+ import { dispatchVendorRequest, requireApiKey } from '../vendor-adapter.js';
28
+ import { mediaVendorRegistry } from '../vendor-registry.js';
29
+ const FISHAUDIO_DEFAULT_BASE_URL = 'https://api.fish.audio';
30
+ const FISHAUDIO_TTS_MODEL_MAP = {
31
+ 'fish-speech-2': 'speech-1.6',
32
+ };
33
+ const NO_CREDENTIAL_MESSAGE = 'no FishAudio credential — configure an API key or set FISH_AUDIO_API_KEY.';
34
+ const fishAudioTTSAdapter = {
35
+ requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
36
+ buildRequest(ctx, credentials) {
37
+ const apiKey = credentials.apiKey; // requireCredential already validated this.
38
+ const baseUrl = (credentials.baseUrl || FISHAUDIO_DEFAULT_BASE_URL).replace(/\/$/, '');
39
+ // Same precedence as minimax.ts's renderMinimaxTTS: an explicit caller
40
+ // alias (ctx.wireModel set to something other than the catalog id) wins
41
+ // over the project's hardcoded FishAudio rename map, which in turn wins
42
+ // over passing the catalog id through unchanged.
43
+ const wireModel = ctx.wireModel !== ctx.model ? ctx.wireModel : FISHAUDIO_TTS_MODEL_MAP[ctx.model] || ctx.model;
44
+ const text = (ctx.prompt && ctx.prompt.trim()) || 'This is a test.';
45
+ const body = {
46
+ text,
47
+ format: 'mp3',
48
+ mp3_bitrate: 128,
49
+ model: wireModel,
50
+ normalize: true,
51
+ latency: 'normal',
52
+ };
53
+ // FishAudio's `reference_id` slot pins which voice the synth uses;
54
+ // empty/absent means FishAudio falls back to its own default voice for
55
+ // the chosen model.
56
+ if (ctx.voice && ctx.voice.trim()) {
57
+ body.reference_id = ctx.voice.trim();
58
+ }
59
+ return {
60
+ url: `${baseUrl}/v1/tts`,
61
+ init: withRequestInit(ctx, {
62
+ method: 'POST',
63
+ headers: {
64
+ authorization: `Bearer ${apiKey}`,
65
+ 'content-type': 'application/json',
66
+ },
67
+ body: JSON.stringify(body),
68
+ }),
69
+ meta: { wireModel },
70
+ };
71
+ },
72
+ parseResponse: createRawBytesParser({
73
+ errorTag: 'fishaudio tts',
74
+ zeroBytesMessage: 'fishaudio tts returned zero bytes',
75
+ note: (bytes, meta) => `fishaudio/${meta.wireModel} · ${bytes.length} bytes`,
76
+ suggestedExt: '.mp3',
77
+ }),
78
+ };
79
+ mediaVendorRegistry.register('fishaudio', 'audio:speech', fishAudioTTSAdapter);
80
+ export async function renderFishAudioTTS(ctx, credentials) {
81
+ return dispatchVendorRequest(fishAudioTTSAdapter, ctx, credentials);
82
+ }
83
+ //# sourceMappingURL=fishaudio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fishaudio.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/fishaudio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,0BAA0B,GAAG,wBAAwB,CAAC;AAE5D,MAAM,uBAAuB,GAA2B;IACtD,eAAe,EAAE,YAAY;CAC9B,CAAC;AAEF,MAAM,qBAAqB,GAAG,2EAA2E,CAAC;AAM1G,MAAM,mBAAmB,GAAoC;IAC3D,iBAAiB,EAAE,aAAa,CAAC,qBAAqB,CAAC;IAEvD,YAAY,CAAC,GAAkB,EAAE,WAAgC;QAC/D,MAAM,MAAM,GAAG,WAAW,CAAC,MAAO,CAAC,CAAC,4CAA4C;QAChF,MAAM,OAAO,GAAG,CAAC,WAAW,CAAC,OAAO,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvF,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,iDAAiD;QACjD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC;QAChH,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,iBAAiB,CAAC;QAEpE,MAAM,IAAI,GAA4B;YACpC,IAAI;YACJ,MAAM,EAAE,KAAK;YACb,WAAW,EAAE,GAAG;YAChB,KAAK,EAAE,SAAS;YAChB,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,QAAQ;SAClB,CAAC;QACF,mEAAmE;QACnE,uEAAuE;QACvE,oBAAoB;QACpB,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACvC,CAAC;QAED,OAAO;YACL,GAAG,EAAE,GAAG,OAAO,SAAS;YACxB,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE;gBACzB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,MAAM,EAAE;oBACjC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC;YACF,IAAI,EAAE,EAAE,SAAS,EAAE;SACpB,CAAC;IACJ,CAAC;IAED,aAAa,EAAE,oBAAoB,CAAmB;QACpD,QAAQ,EAAE,eAAe;QACzB,gBAAgB,EAAE,mCAAmC;QACrD,IAAI,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,aAAa,IAAI,CAAC,SAAS,MAAM,KAAK,CAAC,MAAM,QAAQ;QAC5E,YAAY,EAAE,MAAM;KACrB,CAAC;CACH,CAAC;AAEF,mBAAmB,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAc,EAAE,mBAAmB,CAAC,CAAC;AAE/E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAkB,EAAE,WAAgC;IAC3F,OAAO,qBAAqB,CAAC,mBAAmB,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ProviderCredentials, RenderContext, RenderResult } from '../types.js';
2
+ /**
3
+ * xAI's Imagine API accepts a wide list of aspect ratios (1:1, 16:9, 9:16,
4
+ * 4:3, 3:4, 3:2, 2:3, 2:1, 1:2, 19.5:9, 9:19.5, 20:9, 9:20, auto) —
5
+ * `providers.ts`'s `MEDIA_ASPECTS` is a strict subset, so a recognized
6
+ * value passes through and anything else defaults to 16:9. Written as
7
+ * independent `if` returns (rather than the origin's one `||`-chained
8
+ * condition) to match this package's established aspect-mapping style
9
+ * (see `imagerouter.ts`'s `imageRouterSizeFor`) — same input -> output
10
+ * mapping for every case, not a behavior change.
11
+ */
12
+ export declare function grokAspectFor(aspect: string | undefined): string;
13
+ export declare function renderGrokImage(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
14
+ export declare function renderXAITTS(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
15
+ //# sourceMappingURL=grok.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grok.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/grok.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKpF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAOhE;AAmDD,wBAAsB,eAAe,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAEjH;AAsDD,wBAAsB,YAAY,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAE9G"}