@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,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider: OpenAI Images API (gpt-image-2, gpt-image-1.5, dall-e-3, ...)
|
|
3
|
+
* and text-to-speech via `/v1/audio/speech`. Ported near-verbatim from
|
|
4
|
+
* Open Design's `apps/daemon/src/media/index.ts` `renderOpenAIImage`/
|
|
5
|
+
* `renderOpenAISpeech` — see `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 is
|
|
9
|
+
* unchanged (verified against `openai.test.ts`, which asserts URL/body/
|
|
10
|
+
* header/error-message/providerNote shape and passes unmodified) — only
|
|
11
|
+
* the internal implementation moved into two registered `VendorAdapter`s.
|
|
12
|
+
* `renderOpenAISpeech`'s response (raw bytes, no envelope) is the same
|
|
13
|
+
* shape `fishaudio.ts` returns, so it now shares
|
|
14
|
+
* `response-parsers.ts`'s `createRawBytesParser`. `renderOpenAIImage`
|
|
15
|
+
* keeps a custom `parseResponse`, deliberately NOT routed through
|
|
16
|
+
* `openai-compatible.ts`'s `parseOpenAICompatibleJson`/
|
|
17
|
+
* `bytesFromOpenAICompatibleData` helpers despite the superficially
|
|
18
|
+
* matching `{ data: [...] }` shape: the origin's non-JSON-parse-failure
|
|
19
|
+
* error message is the literal string `"openai non-JSON response: ..."`
|
|
20
|
+
* regardless of whether the request was Azure-routed (unlike its
|
|
21
|
+
* non-OK-status error, which IS azure-tag-aware) — a real, tested
|
|
22
|
+
* asymmetry in the origin (`openai.test.ts` has no azure-tagged non-JSON
|
|
23
|
+
* assertion, and the origin's own catch block never references the azure
|
|
24
|
+
* tag at all). Forcing this through the shared helper (which always tags
|
|
25
|
+
* both messages the same way) would have silently changed that specific
|
|
26
|
+
* corner's message text — exactly the kind of behavior drift this port's
|
|
27
|
+
* "no scope cuts" constraint forbids, so it stays a bespoke parser
|
|
28
|
+
* instead.
|
|
29
|
+
*
|
|
30
|
+
* Supports both the canonical OpenAI endpoint AND Azure-hosted OpenAI
|
|
31
|
+
* deployments behind the same provider slot — Azure is detected from the
|
|
32
|
+
* base URL (`*.azure.com` host or a `/deployments/<name>` path segment).
|
|
33
|
+
* For Azure this additionally:
|
|
34
|
+
* - appends `?api-version=...` (default 2024-02-01, unless the caller
|
|
35
|
+
* already encoded one into the base URL),
|
|
36
|
+
* - sends the `api-key` header in addition to `Authorization` (Azure
|
|
37
|
+
* accepts either; some deployments only honor `api-key`),
|
|
38
|
+
* - drops the `model` field from the body, since the deployment in the
|
|
39
|
+
* path already names the model.
|
|
40
|
+
*
|
|
41
|
+
* Dropped from the origin: the local Codex-CLI-subscription image
|
|
42
|
+
* fallback (`codex-gpt-image-2` etc.) — an OD-specific local-CLI-login
|
|
43
|
+
* integration already excluded from `providers.ts`'s catalogue, not a REST
|
|
44
|
+
* credential shape any other Jini consumer could use.
|
|
45
|
+
*/
|
|
46
|
+
import { FETCH_TIMEOUT_MS, fetchWithTimeout } from '@jini-ai/platform';
|
|
47
|
+
import { Agent as UndiciAgent } from 'undici';
|
|
48
|
+
import { buildOpenAIImageUrl, buildOpenAISpeechUrl, detectAzureEndpoint, openaiSizeFor, OPENAI_TTS_VOICES, truncate, withRequestInit, } from '../openai-compatible.js';
|
|
49
|
+
import { createRawBytesParser } from '../response-parsers.js';
|
|
50
|
+
import { dispatchVendorRequest, requireApiKey } from '../vendor-adapter.js';
|
|
51
|
+
import { mediaVendorRegistry } from '../vendor-registry.js';
|
|
52
|
+
// Image generation can legitimately take minutes (gpt-image-2 4K renders
|
|
53
|
+
// especially). A generous default headers/body timeout dispatcher is
|
|
54
|
+
// applied so a caller-configured global fetch default doesn't cut a
|
|
55
|
+
// legitimately-slow-but-succeeding generation short; a caller can still
|
|
56
|
+
// override via `requestInit.dispatcher`.
|
|
57
|
+
const OPENAI_IMAGE_HEADERS_TIMEOUT_MS = 10 * 60 * 1000;
|
|
58
|
+
const OPENAI_IMAGE_BODY_TIMEOUT_MS = 10 * 60 * 1000;
|
|
59
|
+
const openAIImageDispatcher = new UndiciAgent({
|
|
60
|
+
headersTimeout: OPENAI_IMAGE_HEADERS_TIMEOUT_MS,
|
|
61
|
+
bodyTimeout: OPENAI_IMAGE_BODY_TIMEOUT_MS,
|
|
62
|
+
});
|
|
63
|
+
const OPENAI_IMAGE_NO_CREDENTIAL_MESSAGE = 'no OpenAI credential — configure an API key or set OPENAI_API_KEY.';
|
|
64
|
+
const OPENAI_SPEECH_NO_CREDENTIAL_MESSAGE = 'no OpenAI credential — configure an API key or set OPENAI_API_KEY';
|
|
65
|
+
const openAIImageAdapter = {
|
|
66
|
+
requireCredential: requireApiKey(OPENAI_IMAGE_NO_CREDENTIAL_MESSAGE),
|
|
67
|
+
buildRequest(ctx, credentials) {
|
|
68
|
+
const apiKey = credentials.apiKey; // requireCredential already validated this.
|
|
69
|
+
const rawBase = credentials.baseUrl || 'https://api.openai.com/v1';
|
|
70
|
+
const azure = detectAzureEndpoint(rawBase);
|
|
71
|
+
const url = buildOpenAIImageUrl(rawBase, azure);
|
|
72
|
+
const body = {
|
|
73
|
+
prompt: ctx.prompt || 'A high-quality reference image.',
|
|
74
|
+
n: 1,
|
|
75
|
+
size: openaiSizeFor(ctx.model, ctx.aspect),
|
|
76
|
+
};
|
|
77
|
+
// For non-Azure calls, include `model` in the body. Azure infers it from
|
|
78
|
+
// the deployment in the path so omitting it keeps payloads compatible
|
|
79
|
+
// across both flavors. The wire-name (post-alias) goes on the body so a
|
|
80
|
+
// host's model-aliasing layer, if any, reaches the API.
|
|
81
|
+
if (!azure) {
|
|
82
|
+
body.model = ctx.wireModel;
|
|
83
|
+
}
|
|
84
|
+
// Capability branches key off the CATALOG id (not the wire alias) so a
|
|
85
|
+
// host that aliased `dall-e-3` to a custom Azure/proxy deployment still
|
|
86
|
+
// gets the DALL-E-specific quality + response_format flags.
|
|
87
|
+
if (ctx.model.startsWith('dall-e-')) {
|
|
88
|
+
body.response_format = 'b64_json';
|
|
89
|
+
body.quality = ctx.model === 'dall-e-3' ? 'hd' : 'standard';
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// gpt-image-* accepts quality 'high' | 'medium' | 'low'.
|
|
93
|
+
body.quality = 'high';
|
|
94
|
+
}
|
|
95
|
+
const headers = {
|
|
96
|
+
authorization: `Bearer ${apiKey}`,
|
|
97
|
+
'content-type': 'application/json',
|
|
98
|
+
};
|
|
99
|
+
if (azure) {
|
|
100
|
+
// Azure's canonical auth header. Some deployments accept Bearer too,
|
|
101
|
+
// but api-key is what their docs document, so both are sent. OpenAI
|
|
102
|
+
// ignores unknown headers, so this is harmless on the standard
|
|
103
|
+
// endpoint too.
|
|
104
|
+
headers['api-key'] = apiKey;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
url,
|
|
108
|
+
init: withRequestInit(ctx, {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers,
|
|
111
|
+
body: JSON.stringify(body),
|
|
112
|
+
dispatcher: ctx.requestInit.dispatcher ?? openAIImageDispatcher,
|
|
113
|
+
signal: AbortSignal.timeout(Math.max(OPENAI_IMAGE_HEADERS_TIMEOUT_MS, OPENAI_IMAGE_BODY_TIMEOUT_MS)),
|
|
114
|
+
}),
|
|
115
|
+
meta: { tag: azure ? 'azure-openai' : 'openai', wireModel: ctx.wireModel },
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
async parseResponse(resp, ctx, request) {
|
|
119
|
+
const text = await resp.text();
|
|
120
|
+
if (!resp.ok) {
|
|
121
|
+
throw new Error(`${request.meta.tag} ${resp.status}: ${truncate(text, 240)}`);
|
|
122
|
+
}
|
|
123
|
+
let data;
|
|
124
|
+
try {
|
|
125
|
+
data = JSON.parse(text);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Deliberately not azure-tag-aware — matches the origin exactly, see
|
|
129
|
+
// this module's doc comment for why.
|
|
130
|
+
throw new Error(`openai non-JSON response: ${truncate(text, 200)}`);
|
|
131
|
+
}
|
|
132
|
+
const entry = Array.isArray(data.data) ? data.data[0] : null;
|
|
133
|
+
if (!entry)
|
|
134
|
+
throw new Error('openai response had no data[0]');
|
|
135
|
+
let bytes;
|
|
136
|
+
if (entry.b64_json) {
|
|
137
|
+
bytes = Buffer.from(entry.b64_json, 'base64');
|
|
138
|
+
}
|
|
139
|
+
else if (entry.url) {
|
|
140
|
+
// Downloading already-generated bytes, not waiting on generation — UPLOAD-class (2min), not
|
|
141
|
+
// the 10-minute GENERATE-class timeout the generation call itself uses above.
|
|
142
|
+
const imgResp = await fetchWithTimeout(entry.url, withRequestInit(ctx), { timeoutMs: FETCH_TIMEOUT_MS.UPLOAD });
|
|
143
|
+
if (!imgResp.ok)
|
|
144
|
+
throw new Error(`openai image fetch ${imgResp.status}`);
|
|
145
|
+
const arr = await imgResp.arrayBuffer();
|
|
146
|
+
bytes = Buffer.from(arr);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
throw new Error('openai response had neither b64_json nor url');
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
bytes,
|
|
153
|
+
providerNote: `${request.meta.tag}/${request.meta.wireModel} · ${ctx.aspect} · ${bytes.length} bytes`,
|
|
154
|
+
suggestedExt: '.png',
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
mediaVendorRegistry.register('openai', 'image', openAIImageAdapter);
|
|
159
|
+
export async function renderOpenAIImage(ctx, credentials) {
|
|
160
|
+
return dispatchVendorRequest(openAIImageAdapter, ctx, credentials);
|
|
161
|
+
}
|
|
162
|
+
const openAISpeechAdapter = {
|
|
163
|
+
requireCredential: requireApiKey(OPENAI_SPEECH_NO_CREDENTIAL_MESSAGE),
|
|
164
|
+
buildRequest(ctx, credentials) {
|
|
165
|
+
const apiKey = credentials.apiKey; // requireCredential already validated this.
|
|
166
|
+
const rawBase = credentials.baseUrl || 'https://api.openai.com/v1';
|
|
167
|
+
const azure = detectAzureEndpoint(rawBase);
|
|
168
|
+
const url = buildOpenAISpeechUrl(rawBase, azure);
|
|
169
|
+
const format = ctx.speechFormat;
|
|
170
|
+
const text = (ctx.prompt && ctx.prompt.trim()) || 'This is a test.';
|
|
171
|
+
let voiceId = 'alloy';
|
|
172
|
+
let instructions = '';
|
|
173
|
+
const requestedVoice = (ctx.voice && ctx.voice.trim()) || '';
|
|
174
|
+
if (requestedVoice) {
|
|
175
|
+
if (OPENAI_TTS_VOICES.has(requestedVoice)) {
|
|
176
|
+
voiceId = requestedVoice;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// gpt-4o-mini-tts accepts free-form speaking-style instructions. If
|
|
180
|
+
// the caller passed prose rather than a concrete voice id, preserve
|
|
181
|
+
// it here instead of surfacing a provider error.
|
|
182
|
+
instructions = requestedVoice;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const body = {
|
|
186
|
+
input: text,
|
|
187
|
+
voice: voiceId,
|
|
188
|
+
response_format: format,
|
|
189
|
+
};
|
|
190
|
+
if (!azure) {
|
|
191
|
+
body.model = ctx.wireModel;
|
|
192
|
+
}
|
|
193
|
+
if (instructions && ctx.model === 'gpt-4o-mini-tts') {
|
|
194
|
+
body.instructions = instructions;
|
|
195
|
+
}
|
|
196
|
+
const headers = {
|
|
197
|
+
authorization: `Bearer ${apiKey}`,
|
|
198
|
+
'content-type': 'application/json',
|
|
199
|
+
};
|
|
200
|
+
if (azure) {
|
|
201
|
+
headers['api-key'] = apiKey;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
url,
|
|
205
|
+
init: withRequestInit(ctx, {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers,
|
|
208
|
+
body: JSON.stringify(body),
|
|
209
|
+
}),
|
|
210
|
+
meta: { tag: azure ? 'azure-openai' : 'openai', wireModel: ctx.wireModel, voiceId, format, instructions },
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
parseResponse: createRawBytesParser({
|
|
214
|
+
errorTag: (meta) => `${meta.tag} speech`,
|
|
215
|
+
// Deliberately not azure-tag-aware — matches the origin exactly (see
|
|
216
|
+
// this module's doc comment).
|
|
217
|
+
zeroBytesMessage: 'openai speech returned zero bytes',
|
|
218
|
+
note: (bytes, meta) => {
|
|
219
|
+
const noteBits = [`${meta.tag}/${meta.wireModel}`, meta.voiceId, meta.format, `${bytes.length} bytes`];
|
|
220
|
+
if (meta.instructions)
|
|
221
|
+
noteBits.splice(2, 0, 'styled');
|
|
222
|
+
return noteBits.join(' · ');
|
|
223
|
+
},
|
|
224
|
+
suggestedExt: (_bytes, meta) => (meta.format === 'opus' ? '.ogg' : `.${meta.format}`),
|
|
225
|
+
}),
|
|
226
|
+
};
|
|
227
|
+
mediaVendorRegistry.register('openai', 'audio:speech', openAISpeechAdapter);
|
|
228
|
+
export async function renderOpenAISpeech(ctx, credentials) {
|
|
229
|
+
return dispatchVendorRequest(openAISpeechAdapter, ctx, credentials);
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProviderCredentials, RenderContext, RenderResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* OpenRouter normalizes aspect ratios across the providers it fronts;
|
|
4
|
+
* `providers.ts`'s `MEDIA_ASPECTS` are all natively supported, default to
|
|
5
|
+
* 16:9 for anything else. Written as independent `if` returns (rather than
|
|
6
|
+
* the origin's one `||`-chained condition) to match this package's
|
|
7
|
+
* established aspect-mapping style (see `imagerouter.ts`'s
|
|
8
|
+
* `imageRouterSizeFor`) — same input -> output mapping for every case, not
|
|
9
|
+
* a behavior change.
|
|
10
|
+
*/
|
|
11
|
+
export declare function openRouterAspectFor(aspect: string | undefined): string;
|
|
12
|
+
export declare function renderOpenRouterImage(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
|
|
13
|
+
//# sourceMappingURL=openrouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/openrouter.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAYpF;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAOtE;AAmGD,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAEvH"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider: OpenRouter — unified multi-vendor image generation via the
|
|
3
|
+
* Chat Completions API (`POST /chat/completions` with
|
|
4
|
+
* `modalities: ["image"]`), not the dedicated `/images/generations` shape
|
|
5
|
+
* OpenRouter's own *video* generation uses. Ported near-verbatim from Open
|
|
6
|
+
* Design's `apps/daemon/src/media/index.ts` `renderOpenRouterImage` — see
|
|
7
|
+
* `source-map.md`.
|
|
8
|
+
*
|
|
9
|
+
* Not OpenAI-images-wire-compatible (chat-completions request shape;
|
|
10
|
+
* `choices[0].message.images[].image_url.url` response shape), so this
|
|
11
|
+
* does not route through `openai-compatible.ts`'s OpenAI-images helpers —
|
|
12
|
+
* only its vendor-agnostic utilities (`truncate`, `sniffImageExt`,
|
|
13
|
+
* `withRequestInit`) are reused.
|
|
14
|
+
*
|
|
15
|
+
* Dropped from the origin: the `HTTP-Referer: https://opendesign.dev` /
|
|
16
|
+
* `X-Title: Open Design` request headers — these are OpenRouter's optional
|
|
17
|
+
* app-attribution headers (used for their model leaderboard, not required
|
|
18
|
+
* for the request to succeed) and both values are OD product identity, out
|
|
19
|
+
* of scope per AGENTS.md's no-product-identity-strings boundary. This
|
|
20
|
+
* package has no host-identity concept to substitute one with; a host that
|
|
21
|
+
* wants attribution would need its own request-building layer.
|
|
22
|
+
*
|
|
23
|
+
* 2026-07-21: migrated onto the generic vendor-adapter dispatch engine
|
|
24
|
+
* (`vendor-adapter.ts`/`vendor-registry.ts`). External behavior is
|
|
25
|
+
* unchanged (verified against `openrouter.test.ts`, which asserts URL/
|
|
26
|
+
* body/header/error-message/providerNote shape and passes unmodified) —
|
|
27
|
+
* only the internal implementation moved into a registered
|
|
28
|
+
* `VendorAdapter`. Not routed through `response-parsers.ts`'s factories —
|
|
29
|
+
* the three-way image-decode (`data:` URI / http(s) download / bare
|
|
30
|
+
* base64) has no other current consumer, so it correctly stays a bespoke
|
|
31
|
+
* `parseResponse`.
|
|
32
|
+
*/
|
|
33
|
+
import { FETCH_TIMEOUT_MS, fetchWithTimeout } from '@jini-ai/platform';
|
|
34
|
+
import { sniffImageExt, truncate, withRequestInit } from '../openai-compatible.js';
|
|
35
|
+
import { dispatchVendorRequest, requireApiKey } from '../vendor-adapter.js';
|
|
36
|
+
import { mediaVendorRegistry } from '../vendor-registry.js';
|
|
37
|
+
// Chat-completions image generation can take a while for some routed
|
|
38
|
+
// models; matches the same 10-minute ceiling `openai.ts`'s image dispatcher
|
|
39
|
+
// uses for the same reason (see `OPENAI_IMAGE_HEADERS_TIMEOUT_MS`/
|
|
40
|
+
// `OPENAI_IMAGE_BODY_TIMEOUT_MS` there — this vendor has no separate
|
|
41
|
+
// headers/body distinction, so a single `AbortSignal.timeout` suffices).
|
|
42
|
+
const OPENROUTER_IMAGE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
43
|
+
/**
|
|
44
|
+
* OpenRouter normalizes aspect ratios across the providers it fronts;
|
|
45
|
+
* `providers.ts`'s `MEDIA_ASPECTS` are all natively supported, default to
|
|
46
|
+
* 16:9 for anything else. Written as independent `if` returns (rather than
|
|
47
|
+
* the origin's one `||`-chained condition) to match this package's
|
|
48
|
+
* established aspect-mapping style (see `imagerouter.ts`'s
|
|
49
|
+
* `imageRouterSizeFor`) — same input -> output mapping for every case, not
|
|
50
|
+
* a behavior change.
|
|
51
|
+
*/
|
|
52
|
+
export function openRouterAspectFor(aspect) {
|
|
53
|
+
if (aspect === '1:1')
|
|
54
|
+
return '1:1';
|
|
55
|
+
if (aspect === '16:9')
|
|
56
|
+
return '16:9';
|
|
57
|
+
if (aspect === '9:16')
|
|
58
|
+
return '9:16';
|
|
59
|
+
if (aspect === '4:3')
|
|
60
|
+
return '4:3';
|
|
61
|
+
if (aspect === '3:4')
|
|
62
|
+
return '3:4';
|
|
63
|
+
return '16:9';
|
|
64
|
+
}
|
|
65
|
+
const NO_CREDENTIAL_MESSAGE = 'no OpenRouter API key — configure it or set OPENROUTER_API_KEY';
|
|
66
|
+
const openRouterImageAdapter = {
|
|
67
|
+
requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
|
|
68
|
+
buildRequest(ctx, credentials) {
|
|
69
|
+
const apiKey = credentials.apiKey; // requireCredential already validated this.
|
|
70
|
+
const baseUrl = (credentials.baseUrl || 'https://openrouter.ai/api/v1').replace(/\/$/, '');
|
|
71
|
+
// credentials.model (a host's own model-aliasing layer) wins over
|
|
72
|
+
// ctx.wireModel; strip the catalogue's `openrouter/` prefix so the wire
|
|
73
|
+
// model name matches OpenRouter's canonical slug.
|
|
74
|
+
const resolved = (credentials.model || ctx.wireModel).trim();
|
|
75
|
+
const wireModel = resolved.startsWith('openrouter/') ? resolved.slice('openrouter/'.length) : resolved;
|
|
76
|
+
// Multi-modal models (Gemini variants) accept both image and text
|
|
77
|
+
// output; image-only models (Flux, Recraft, Sourceful) only accept
|
|
78
|
+
// ["image"]. A simple heuristic on the slug, matching the origin.
|
|
79
|
+
const modalities = wireModel.includes('gemini') ? ['image', 'text'] : ['image'];
|
|
80
|
+
const aspectRatio = openRouterAspectFor(ctx.aspect);
|
|
81
|
+
const body = {
|
|
82
|
+
model: wireModel,
|
|
83
|
+
messages: [{ role: 'user', content: ctx.prompt || 'A high-quality reference image.' }],
|
|
84
|
+
modalities,
|
|
85
|
+
stream: false,
|
|
86
|
+
image_config: { aspect_ratio: aspectRatio, image_size: '1K' },
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
url: `${baseUrl}/chat/completions`,
|
|
90
|
+
init: withRequestInit(ctx, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
authorization: `Bearer ${apiKey}`,
|
|
94
|
+
'content-type': 'application/json',
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify(body),
|
|
97
|
+
signal: AbortSignal.timeout(OPENROUTER_IMAGE_TIMEOUT_MS),
|
|
98
|
+
}),
|
|
99
|
+
meta: { wireModel, aspectRatio },
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
async parseResponse(resp, ctx, request) {
|
|
103
|
+
const { wireModel, aspectRatio } = request.meta;
|
|
104
|
+
const text = await resp.text();
|
|
105
|
+
if (!resp.ok) {
|
|
106
|
+
throw new Error(`openrouter image ${resp.status}: ${truncate(text, 240)}`);
|
|
107
|
+
}
|
|
108
|
+
let data;
|
|
109
|
+
try {
|
|
110
|
+
data = JSON.parse(text);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
throw new Error(`openrouter image non-JSON response: ${truncate(text, 200)}`);
|
|
114
|
+
}
|
|
115
|
+
const images = data?.choices?.[0]?.message?.images;
|
|
116
|
+
if (!Array.isArray(images) || images.length === 0) {
|
|
117
|
+
throw new Error(`openrouter image response contained no images for model ${wireModel}: ${truncate(text, 200)}`);
|
|
118
|
+
}
|
|
119
|
+
const dataUrl = images[0]?.image_url?.url;
|
|
120
|
+
if (typeof dataUrl !== 'string' || !dataUrl) {
|
|
121
|
+
throw new Error(`openrouter image response missing image_url.url: ${truncate(text, 200)}`);
|
|
122
|
+
}
|
|
123
|
+
// Strip a "data:image/...;base64," prefix and decode; fall back to
|
|
124
|
+
// downloading a plain URL, or treating the string as raw base64.
|
|
125
|
+
const b64Match = dataUrl.match(/^data:image\/[^;]+;base64,(.+)$/s);
|
|
126
|
+
let bytes;
|
|
127
|
+
if (b64Match) {
|
|
128
|
+
bytes = Buffer.from(b64Match[1], 'base64');
|
|
129
|
+
}
|
|
130
|
+
else if (dataUrl.startsWith('http')) {
|
|
131
|
+
// Downloading already-generated bytes, not waiting on generation — UPLOAD-class (2min), not
|
|
132
|
+
// the 10-minute GENERATE-class timeout the chat-completions call itself uses above.
|
|
133
|
+
const imgResp = await fetchWithTimeout(dataUrl, withRequestInit(ctx), { timeoutMs: FETCH_TIMEOUT_MS.UPLOAD });
|
|
134
|
+
if (!imgResp.ok)
|
|
135
|
+
throw new Error(`openrouter image download ${imgResp.status}`);
|
|
136
|
+
bytes = Buffer.from(await imgResp.arrayBuffer());
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
bytes = Buffer.from(dataUrl, 'base64');
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
bytes,
|
|
143
|
+
providerNote: `openrouter/${wireModel} · ${aspectRatio} · ${bytes.length} bytes`,
|
|
144
|
+
suggestedExt: sniffImageExt(bytes),
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
mediaVendorRegistry.register('openrouter', 'image', openRouterImageAdapter);
|
|
149
|
+
export async function renderOpenRouterImage(ctx, credentials) {
|
|
150
|
+
return dispatchVendorRequest(openRouterImageAdapter, ctx, credentials);
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/openrouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEnF,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qEAAqE;AACrE,4EAA4E;AAC5E,mEAAmE;AACnE,qEAAqE;AACrE,yEAAyE;AACzE,MAAM,2BAA2B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA0B;IAC5D,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACrC,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACrC,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,qBAAqB,GAAG,gEAAgE,CAAC;AAO/F,MAAM,sBAAsB,GAAuC;IACjE,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,8BAA8B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAE3F,kEAAkE;QAClE,wEAAwE;QACxE,kDAAkD;QAClD,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,KAAK,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAEvG,kEAAkE;QAClE,mEAAmE;QACnE,kEAAkE;QAClE,MAAM,UAAU,GAAsB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACnG,MAAM,WAAW,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpD,MAAM,IAAI,GAA4B;YACpC,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,IAAI,iCAAiC,EAAE,CAAC;YACtF,UAAU;YACV,MAAM,EAAE,KAAK;YACb,YAAY,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE;SAC9D,CAAC;QAEF,OAAO;YACL,GAAG,EAAE,GAAG,OAAO,mBAAmB;YAClC,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;gBAC1B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,2BAA2B,CAAC;aACzD,CAAC;YACF,IAAI,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE;SACjC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAAc,EAAE,GAAkB,EAAE,OAA2C;QACjG,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,uCAAuC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,MAAM,GAAI,IAAuE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC;QACvH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,2DAA2D,SAAS,KAAK,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAClH,CAAC;QACD,MAAM,OAAO,GAAI,MAAM,CAAC,CAAC,CAA8C,EAAE,SAAS,EAAE,GAAG,CAAC;QACxF,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,oDAAoD,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7F,CAAC;QAED,mEAAmE;QACnE,iEAAiE;QACjE,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACnE,IAAI,KAAa,CAAC;QAClB,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,4FAA4F;YAC5F,oFAAoF;YACpF,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9G,IAAI,CAAC,OAAO,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAChF,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;QAED,OAAO;YACL,KAAK;YACL,YAAY,EAAE,cAAc,SAAS,MAAM,WAAW,MAAM,KAAK,CAAC,MAAM,QAAQ;YAChF,YAAY,EAAE,aAAa,CAAC,KAAK,CAAC;SACnC,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,mBAAmB,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;AAE5E,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAkB,EAAE,WAAgC;IAC9F,OAAO,qBAAqB,CAAC,sBAAsB,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderCredentials, RenderContext, RenderResult } from '../types.js';
|
|
2
|
+
export declare function renderSenseAudioTTS(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
|
|
3
|
+
export declare function renderSenseAudioImage(ctx: RenderContext, credentials: ProviderCredentials): Promise<RenderResult>;
|
|
4
|
+
//# sourceMappingURL=senseaudio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"senseaudio.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/senseaudio.ts"],"names":[],"mappings":"AAiDA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8DpF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAErH;AA0GD,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAEvH"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider: SenseAudio — `senseaudio-tts-1.5` text-to-speech
|
|
3
|
+
* (`renderSenseAudioTTS`, `POST /v1/t2a_v2`) and text-to-image
|
|
4
|
+
* (`renderSenseAudioImage`, `POST /v1/image/sync`), both synchronous.
|
|
5
|
+
* Ported near-verbatim from Open Design's
|
|
6
|
+
* `apps/daemon/src/media/index.ts` `renderSenseAudioTTS`/
|
|
7
|
+
* `renderSenseAudioImage` — see `source-map.md`.
|
|
8
|
+
*
|
|
9
|
+
* 2026-07-21: migrated onto the generic vendor-adapter dispatch engine
|
|
10
|
+
* (`vendor-adapter.ts`/`vendor-registry.ts`). External behavior (URLs,
|
|
11
|
+
* bodies, headers, every error message, `providerNote` format, the SSRF
|
|
12
|
+
* guard on the image download) is unchanged; only the internal
|
|
13
|
+
* implementation moved into two registered `VendorAdapter`s. TTS's
|
|
14
|
+
* response envelope is identical to `minimax.ts`'s, so both now share
|
|
15
|
+
* `response-parsers.ts`'s `createHexEnvelopeAudioParser`. Image's response
|
|
16
|
+
* (`{ url }`, downloaded through an SSRF-guarded second fetch, with two
|
|
17
|
+
* independent failure-signaling paths — `base_resp` AND a separate
|
|
18
|
+
* `error_message`) has no equivalent elsewhere in this package, so it
|
|
19
|
+
* keeps a custom `parseResponse` — the generic engine's `parseResponse`
|
|
20
|
+
* hook is a full function precisely so a second network call mid-parse
|
|
21
|
+
* (the SSRF-guarded download) stays possible; see `vendor-adapter.ts`'s
|
|
22
|
+
* module doc.
|
|
23
|
+
*
|
|
24
|
+
* Neither endpoint is OpenAI-wire-compatible: TTS mirrors MiniMax's own
|
|
25
|
+
* `voice_setting`/`audio_setting` body shape and `base_resp` response
|
|
26
|
+
* envelope (an HTTP 200 can still be a logical failure), and image returns
|
|
27
|
+
* `{ url }` rather than `{ data: [...] }`, so only the generic
|
|
28
|
+
* `withRequestInit` helper is reused from `openai-compatible.ts` (matching
|
|
29
|
+
* `minimax.ts`'s reasoning).
|
|
30
|
+
*
|
|
31
|
+
* `renderSenseAudioImage`'s `url` is fetched through `ssrf-guard.ts`'s
|
|
32
|
+
* `assertAndFetchExternalAsset` — same as the real origin — because it is
|
|
33
|
+
* attacker-controllable inside an otherwise-successful response (a
|
|
34
|
+
* compromised/misconfigured gateway could point it at internal
|
|
35
|
+
* infrastructure); `assertAndFetchExternalAsset` DNS-resolves the host,
|
|
36
|
+
* rejects loopback/RFC1918/link-local/CGNAT/metadata-service addresses,
|
|
37
|
+
* and pins `redirect: 'error'` so a validated public URL can't 302 into
|
|
38
|
+
* private space either.
|
|
39
|
+
*
|
|
40
|
+
* Dropped from the origin's no-credential error messages (both
|
|
41
|
+
* functions): the `OD_`-prefixed override env var
|
|
42
|
+
* (`OD_SENSEAUDIO_API_KEY`) — `providers.ts`'s
|
|
43
|
+
* `PROVIDER_CREDENTIAL_ENV_VARS.senseaudio` already de-branded this to
|
|
44
|
+
* plain `SENSEAUDIO_API_KEY`.
|
|
45
|
+
*/
|
|
46
|
+
import { truncate, withRequestInit } from '../openai-compatible.js';
|
|
47
|
+
import { createHexEnvelopeAudioParser } from '../response-parsers.js';
|
|
48
|
+
import { assertAndFetchExternalAsset } from '../ssrf-guard.js';
|
|
49
|
+
import { dispatchVendorRequest, requireApiKey } from '../vendor-adapter.js';
|
|
50
|
+
import { mediaVendorRegistry } from '../vendor-registry.js';
|
|
51
|
+
const SENSEAUDIO_DEFAULT_BASE_URL = 'https://api.senseaudio.cn';
|
|
52
|
+
const SENSEAUDIO_DEFAULT_VOICE_ID = 'female_0033_b';
|
|
53
|
+
const SENSEAUDIO_IMAGE_PROMPT_LIMIT = 2000;
|
|
54
|
+
const SENSEAUDIO_TTS_MODEL_MAP = {
|
|
55
|
+
'senseaudio-tts': 'senseaudio-tts-1.5-260319',
|
|
56
|
+
};
|
|
57
|
+
const NO_CREDENTIAL_MESSAGE = 'no SenseAudio credential — configure an API key or set SENSEAUDIO_API_KEY.';
|
|
58
|
+
const senseAudioTTSAdapter = {
|
|
59
|
+
requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
|
|
60
|
+
buildRequest(ctx, credentials) {
|
|
61
|
+
const apiKey = credentials.apiKey; // requireCredential already validated this.
|
|
62
|
+
const baseUrl = (credentials.baseUrl || SENSEAUDIO_DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
63
|
+
const wireModel = SENSEAUDIO_TTS_MODEL_MAP[ctx.model] || ctx.model;
|
|
64
|
+
const text = (ctx.prompt && ctx.prompt.trim()) || 'This is a test.';
|
|
65
|
+
const voiceId = (ctx.voice && ctx.voice.trim()) || SENSEAUDIO_DEFAULT_VOICE_ID;
|
|
66
|
+
const body = {
|
|
67
|
+
model: wireModel,
|
|
68
|
+
text,
|
|
69
|
+
stream: false,
|
|
70
|
+
voice_setting: {
|
|
71
|
+
voice_id: voiceId,
|
|
72
|
+
speed: 1,
|
|
73
|
+
vol: 1,
|
|
74
|
+
pitch: 0,
|
|
75
|
+
},
|
|
76
|
+
audio_setting: {
|
|
77
|
+
format: 'mp3',
|
|
78
|
+
sample_rate: 32000,
|
|
79
|
+
bitrate: 128000,
|
|
80
|
+
channel: 2,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
url: `${baseUrl}/v1/t2a_v2`,
|
|
85
|
+
init: withRequestInit(ctx, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: {
|
|
88
|
+
authorization: `Bearer ${apiKey}`,
|
|
89
|
+
'content-type': 'application/json',
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify(body),
|
|
92
|
+
}),
|
|
93
|
+
meta: { wireModel, voiceId },
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
parseResponse: createHexEnvelopeAudioParser({ errorTag: 'senseaudio tts', providerId: 'senseaudio' }),
|
|
97
|
+
};
|
|
98
|
+
mediaVendorRegistry.register('senseaudio', 'audio:speech', senseAudioTTSAdapter);
|
|
99
|
+
export async function renderSenseAudioTTS(ctx, credentials) {
|
|
100
|
+
return dispatchVendorRequest(senseAudioTTSAdapter, ctx, credentials);
|
|
101
|
+
}
|
|
102
|
+
/** SenseAudio's image gateway rejects non-standard pixel sizes with a 400; keep this mapping in sync with any other SenseAudio-image call site a future host adds. */
|
|
103
|
+
function senseAudioImageSize(aspect) {
|
|
104
|
+
if (aspect === '16:9')
|
|
105
|
+
return '1280x720';
|
|
106
|
+
if (aspect === '9:16')
|
|
107
|
+
return '720x1280';
|
|
108
|
+
if (aspect === '4:3')
|
|
109
|
+
return '1024x768';
|
|
110
|
+
if (aspect === '3:4')
|
|
111
|
+
return '768x1024';
|
|
112
|
+
return '1024x1024';
|
|
113
|
+
}
|
|
114
|
+
const senseAudioImageAdapter = {
|
|
115
|
+
requireCredential: requireApiKey(NO_CREDENTIAL_MESSAGE),
|
|
116
|
+
buildRequest(ctx, credentials) {
|
|
117
|
+
const apiKey = credentials.apiKey; // requireCredential already validated this.
|
|
118
|
+
const baseUrl = (credentials.baseUrl || SENSEAUDIO_DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
119
|
+
const promptRaw = (ctx.prompt && ctx.prompt.trim()) || 'A high-quality reference image.';
|
|
120
|
+
// SenseAudio rejects prompts over 2000 chars with a 4xx; trim
|
|
121
|
+
// defensively so a verbose caller doesn't dead-end the generation.
|
|
122
|
+
const prompt = promptRaw.length > SENSEAUDIO_IMAGE_PROMPT_LIMIT ? promptRaw.slice(0, SENSEAUDIO_IMAGE_PROMPT_LIMIT) : promptRaw;
|
|
123
|
+
const size = senseAudioImageSize(ctx.aspect);
|
|
124
|
+
const reference = ctx.imageRef?.dataUrl;
|
|
125
|
+
const body = {
|
|
126
|
+
model: ctx.wireModel,
|
|
127
|
+
prompt,
|
|
128
|
+
size,
|
|
129
|
+
};
|
|
130
|
+
if (reference) {
|
|
131
|
+
// The API documents `size` as optional when a reference image is
|
|
132
|
+
// supplied; it is still sent so output dimensions stay deterministic
|
|
133
|
+
// across t2i/i2i calls for the same model.
|
|
134
|
+
body.reference = reference;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
url: `${baseUrl}/v1/image/sync`,
|
|
138
|
+
init: withRequestInit(ctx, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: {
|
|
141
|
+
authorization: `Bearer ${apiKey}`,
|
|
142
|
+
'content-type': 'application/json',
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify(body),
|
|
145
|
+
}),
|
|
146
|
+
meta: { size, hasReference: Boolean(reference) },
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
async parseResponse(resp, ctx, request) {
|
|
150
|
+
const respText = await resp.text();
|
|
151
|
+
if (!resp.ok) {
|
|
152
|
+
throw new Error(`senseaudio image ${resp.status}: ${truncate(respText, 240)}`);
|
|
153
|
+
}
|
|
154
|
+
let data;
|
|
155
|
+
try {
|
|
156
|
+
data = JSON.parse(respText);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
throw new Error(`senseaudio image non-JSON: ${truncate(respText, 200)}`);
|
|
160
|
+
}
|
|
161
|
+
if (data.base_resp && data.base_resp.status_code !== 0) {
|
|
162
|
+
throw new Error(`senseaudio image api error ${data.base_resp.status_code}: ${data.base_resp.status_msg || 'unknown'}`);
|
|
163
|
+
}
|
|
164
|
+
if (typeof data.error_message === 'string' && data.error_message) {
|
|
165
|
+
throw new Error(`senseaudio image api error: ${data.error_message}`);
|
|
166
|
+
}
|
|
167
|
+
const url = typeof data.url === 'string' ? data.url : '';
|
|
168
|
+
if (!url) {
|
|
169
|
+
throw new Error('senseaudio image response missing url');
|
|
170
|
+
}
|
|
171
|
+
const imgResp = await assertAndFetchExternalAsset(url, withRequestInit(ctx));
|
|
172
|
+
if (!imgResp.ok) {
|
|
173
|
+
throw new Error(`senseaudio image fetch ${imgResp.status}`);
|
|
174
|
+
}
|
|
175
|
+
const bytes = Buffer.from(await imgResp.arrayBuffer());
|
|
176
|
+
if (bytes.length === 0) {
|
|
177
|
+
throw new Error('senseaudio image fetch returned zero bytes');
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
bytes,
|
|
181
|
+
providerNote: `senseaudio/${ctx.wireModel} · ${request.meta.size}${request.meta.hasReference ? ' · i2i' : ''} · ${bytes.length} bytes`,
|
|
182
|
+
suggestedExt: '.png',
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
mediaVendorRegistry.register('senseaudio', 'image', senseAudioImageAdapter);
|
|
187
|
+
export async function renderSenseAudioImage(ctx, credentials) {
|
|
188
|
+
return dispatchVendorRequest(senseAudioImageAdapter, ctx, credentials);
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=senseaudio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"senseaudio.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/senseaudio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volcengine.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/volcengine.ts"],"names":[],"mappings":"AAiDA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmDpF,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAEvH"}
|