@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.
- package/LICENSE +201 -0
- package/README.md +51 -0
- package/dist/composio/bounded-data.d.ts +36 -0
- package/dist/composio/bounded-data.d.ts.map +1 -0
- package/dist/composio/bounded-data.js +181 -0
- package/dist/composio/bounded-data.js.map +1 -0
- package/dist/composio/catalog.d.ts +147 -0
- package/dist/composio/catalog.d.ts.map +1 -0
- package/dist/composio/catalog.js +126 -0
- package/dist/composio/catalog.js.map +1 -0
- package/dist/composio/composio-config.d.ts +42 -0
- package/dist/composio/composio-config.d.ts.map +1 -0
- package/dist/composio/composio-config.js +145 -0
- package/dist/composio/composio-config.js.map +1 -0
- package/dist/composio/composio-descriptions.d.ts +16 -0
- package/dist/composio/composio-descriptions.d.ts.map +1 -0
- package/dist/composio/composio-descriptions.js +766 -0
- package/dist/composio/composio-descriptions.js.map +1 -0
- package/dist/composio/composio.d.ts +364 -0
- package/dist/composio/composio.d.ts.map +1 -0
- package/dist/composio/composio.js +1902 -0
- package/dist/composio/composio.js.map +1 -0
- package/dist/composio/errors.d.ts +15 -0
- package/dist/composio/errors.d.ts.map +1 -0
- package/dist/composio/errors.js +16 -0
- package/dist/composio/errors.js.map +1 -0
- package/dist/composio/file-lock.d.ts +13 -0
- package/dist/composio/file-lock.d.ts.map +1 -0
- package/dist/composio/file-lock.js +92 -0
- package/dist/composio/file-lock.js.map +1 -0
- package/dist/composio/index.d.ts +18 -0
- package/dist/composio/index.d.ts.map +1 -0
- package/dist/composio/index.js +7 -0
- package/dist/composio/index.js.map +1 -0
- package/dist/composio/json-schema.d.ts +93 -0
- package/dist/composio/json-schema.d.ts.map +1 -0
- package/dist/composio/json-schema.js +498 -0
- package/dist/composio/json-schema.js.map +1 -0
- package/dist/composio/output-protection.d.ts +32 -0
- package/dist/composio/output-protection.d.ts.map +1 -0
- package/dist/composio/output-protection.js +105 -0
- package/dist/composio/output-protection.js.map +1 -0
- package/dist/composio/service.d.ts +181 -0
- package/dist/composio/service.d.ts.map +1 -0
- package/dist/composio/service.js +758 -0
- package/dist/composio/service.js.map +1 -0
- package/dist/media-providers/capability-registry.d.ts +28 -0
- package/dist/media-providers/capability-registry.d.ts.map +1 -0
- package/dist/media-providers/capability-registry.js +27 -0
- package/dist/media-providers/capability-registry.js.map +1 -0
- package/dist/media-providers/catalog.d.ts +20 -0
- package/dist/media-providers/catalog.d.ts.map +1 -0
- package/dist/media-providers/catalog.js +20 -0
- package/dist/media-providers/catalog.js.map +1 -0
- package/dist/media-providers/dispatch/context.d.ts +21 -0
- package/dist/media-providers/dispatch/context.d.ts.map +1 -0
- package/dist/media-providers/dispatch/context.js +36 -0
- package/dist/media-providers/dispatch/context.js.map +1 -0
- package/dist/media-providers/dispatch/credentials.d.ts +4 -0
- package/dist/media-providers/dispatch/credentials.d.ts.map +1 -0
- package/dist/media-providers/dispatch/credentials.js +25 -0
- package/dist/media-providers/dispatch/credentials.js.map +1 -0
- package/dist/media-providers/dispatch/engine.d.ts +30 -0
- package/dist/media-providers/dispatch/engine.d.ts.map +1 -0
- package/dist/media-providers/dispatch/engine.js +208 -0
- package/dist/media-providers/dispatch/engine.js.map +1 -0
- package/dist/media-providers/dispatch/index.d.ts +25 -0
- package/dist/media-providers/dispatch/index.d.ts.map +1 -0
- package/dist/media-providers/dispatch/index.js +24 -0
- package/dist/media-providers/dispatch/index.js.map +1 -0
- package/dist/media-providers/dispatch/openai-compatible.d.ts +44 -0
- package/dist/media-providers/dispatch/openai-compatible.d.ts.map +1 -0
- package/dist/media-providers/dispatch/openai-compatible.js +214 -0
- package/dist/media-providers/dispatch/openai-compatible.js.map +1 -0
- package/dist/media-providers/dispatch/providers/aihubmix-shared.d.ts +72 -0
- package/dist/media-providers/dispatch/providers/aihubmix-shared.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/aihubmix-shared.js +131 -0
- package/dist/media-providers/dispatch/providers/aihubmix-shared.js.map +1 -0
- package/dist/media-providers/dispatch/providers/aihubmix.d.ts +4 -0
- package/dist/media-providers/dispatch/providers/aihubmix.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/aihubmix.js +205 -0
- package/dist/media-providers/dispatch/providers/aihubmix.js.map +1 -0
- package/dist/media-providers/dispatch/providers/custom-image.d.ts +6 -0
- package/dist/media-providers/dispatch/providers/custom-image.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/custom-image.js +94 -0
- package/dist/media-providers/dispatch/providers/custom-image.js.map +1 -0
- package/dist/media-providers/dispatch/providers/elevenlabs.d.ts +4 -0
- package/dist/media-providers/dispatch/providers/elevenlabs.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/elevenlabs.js +163 -0
- package/dist/media-providers/dispatch/providers/elevenlabs.js.map +1 -0
- package/dist/media-providers/dispatch/providers/fishaudio.d.ts +3 -0
- package/dist/media-providers/dispatch/providers/fishaudio.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/fishaudio.js +83 -0
- package/dist/media-providers/dispatch/providers/fishaudio.js.map +1 -0
- package/dist/media-providers/dispatch/providers/grok.d.ts +15 -0
- package/dist/media-providers/dispatch/providers/grok.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/grok.js +150 -0
- package/dist/media-providers/dispatch/providers/grok.js.map +1 -0
- package/dist/media-providers/dispatch/providers/imagerouter.d.ts +6 -0
- package/dist/media-providers/dispatch/providers/imagerouter.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/imagerouter.js +131 -0
- package/dist/media-providers/dispatch/providers/imagerouter.js.map +1 -0
- package/dist/media-providers/dispatch/providers/minimax.d.ts +3 -0
- package/dist/media-providers/dispatch/providers/minimax.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/minimax.js +96 -0
- package/dist/media-providers/dispatch/providers/minimax.js.map +1 -0
- package/dist/media-providers/dispatch/providers/nanobanana.d.ts +3 -0
- package/dist/media-providers/dispatch/providers/nanobanana.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/nanobanana.js +142 -0
- package/dist/media-providers/dispatch/providers/nanobanana.js.map +1 -0
- package/dist/media-providers/dispatch/providers/openai.d.ts +4 -0
- package/dist/media-providers/dispatch/providers/openai.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/openai.js +231 -0
- package/dist/media-providers/dispatch/providers/openai.js.map +1 -0
- package/dist/media-providers/dispatch/providers/openrouter.d.ts +13 -0
- package/dist/media-providers/dispatch/providers/openrouter.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/openrouter.js +152 -0
- package/dist/media-providers/dispatch/providers/openrouter.js.map +1 -0
- package/dist/media-providers/dispatch/providers/senseaudio.d.ts +4 -0
- package/dist/media-providers/dispatch/providers/senseaudio.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/senseaudio.js +190 -0
- package/dist/media-providers/dispatch/providers/senseaudio.js.map +1 -0
- package/dist/media-providers/dispatch/providers/volcengine.d.ts +3 -0
- package/dist/media-providers/dispatch/providers/volcengine.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/volcengine.js +95 -0
- package/dist/media-providers/dispatch/providers/volcengine.js.map +1 -0
- package/dist/media-providers/dispatch/response-parsers.d.ts +44 -0
- package/dist/media-providers/dispatch/response-parsers.d.ts.map +1 -0
- package/dist/media-providers/dispatch/response-parsers.js +103 -0
- package/dist/media-providers/dispatch/response-parsers.js.map +1 -0
- package/dist/media-providers/dispatch/ssrf-guard.d.ts +51 -0
- package/dist/media-providers/dispatch/ssrf-guard.d.ts.map +1 -0
- package/dist/media-providers/dispatch/ssrf-guard.js +215 -0
- package/dist/media-providers/dispatch/ssrf-guard.js.map +1 -0
- package/dist/media-providers/dispatch/stub.d.ts +5 -0
- package/dist/media-providers/dispatch/stub.d.ts.map +1 -0
- package/dist/media-providers/dispatch/stub.js +98 -0
- package/dist/media-providers/dispatch/stub.js.map +1 -0
- package/dist/media-providers/dispatch/types.d.ts +133 -0
- package/dist/media-providers/dispatch/types.d.ts.map +1 -0
- package/dist/media-providers/dispatch/types.js +2 -0
- package/dist/media-providers/dispatch/types.js.map +1 -0
- package/dist/media-providers/dispatch/vendor-adapter.d.ts +54 -0
- package/dist/media-providers/dispatch/vendor-adapter.d.ts.map +1 -0
- package/dist/media-providers/dispatch/vendor-adapter.js +68 -0
- package/dist/media-providers/dispatch/vendor-adapter.js.map +1 -0
- package/dist/media-providers/dispatch/vendor-registry.d.ts +36 -0
- package/dist/media-providers/dispatch/vendor-registry.d.ts.map +1 -0
- package/dist/media-providers/dispatch/vendor-registry.js +46 -0
- package/dist/media-providers/dispatch/vendor-registry.js.map +1 -0
- package/dist/media-providers/index.d.ts +22 -0
- package/dist/media-providers/index.d.ts.map +1 -0
- package/dist/media-providers/index.js +22 -0
- package/dist/media-providers/index.js.map +1 -0
- package/dist/media-providers/policy.d.ts +49 -0
- package/dist/media-providers/policy.d.ts.map +1 -0
- package/dist/media-providers/policy.js +39 -0
- package/dist/media-providers/policy.js.map +1 -0
- package/dist/media-providers/providers.d.ts +33 -0
- package/dist/media-providers/providers.d.ts.map +1 -0
- package/dist/media-providers/providers.js +200 -0
- package/dist/media-providers/providers.js.map +1 -0
- package/dist/media-providers/seed.d.ts +14 -0
- package/dist/media-providers/seed.d.ts.map +1 -0
- package/dist/media-providers/seed.js +109 -0
- package/dist/media-providers/seed.js.map +1 -0
- package/dist/media-providers/sqlite-task-store.d.ts +29 -0
- package/dist/media-providers/sqlite-task-store.d.ts.map +1 -0
- package/dist/media-providers/sqlite-task-store.js +234 -0
- package/dist/media-providers/sqlite-task-store.js.map +1 -0
- package/dist/media-providers/staging.d.ts +29 -0
- package/dist/media-providers/staging.d.ts.map +1 -0
- package/dist/media-providers/staging.js +141 -0
- package/dist/media-providers/staging.js.map +1 -0
- package/dist/media-providers/task-store.d.ts +94 -0
- package/dist/media-providers/task-store.d.ts.map +1 -0
- package/dist/media-providers/task-store.js +160 -0
- package/dist/media-providers/task-store.js.map +1 -0
- package/dist/media-providers/tokens.d.ts +7 -0
- package/dist/media-providers/tokens.d.ts.map +1 -0
- package/dist/media-providers/tokens.js +13 -0
- package/dist/media-providers/tokens.js.map +1 -0
- package/dist/media-providers/types.d.ts +123 -0
- package/dist/media-providers/types.d.ts.map +1 -0
- package/dist/media-providers/types.js +12 -0
- package/dist/media-providers/types.js.map +1 -0
- package/dist/media-providers/video-request.d.ts +62 -0
- package/dist/media-providers/video-request.d.ts.map +1 -0
- package/dist/media-providers/video-request.js +219 -0
- package/dist/media-providers/video-request.js.map +1 -0
- 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 @@
|
|
|
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"}
|