dsh-image-gen 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/lib/index.js ADDED
@@ -0,0 +1,525 @@
1
+ import { credentialRef } from "@deepseek-ai/dsh-credentials";
2
+ import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
3
+ import { defineTool } from "@deepseek-ai/dsh-tools";
4
+ import z from "@deepseek-ai/schemastery";
5
+ //#region lib/types/shared.js
6
+ /** Values shared by the Host and browser Bundle faces. */
7
+ /** Browser route used by the generated-image card. */
8
+ const IMAGE_ROUTE = "/plugins/dsh-image-gen/image";
9
+ /** Namespace persisted through DSH Settings. */
10
+ const IMAGE_GENERATION_NAMESPACE = "image-generation";
11
+ /** Supported providers. */
12
+ const IMAGE_PROVIDERS = [
13
+ "google",
14
+ "openai",
15
+ "seedream"
16
+ ];
17
+ /** Default endpoints and base URLs. */
18
+ const DEFAULT_GOOGLE_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/interactions";
19
+ const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
20
+ const DEFAULT_SEEDREAM_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
21
+ /** Default model names. */
22
+ const DEFAULT_GOOGLE_MODEL = "gemini-3.1-flash-image";
23
+ const DEFAULT_OPENAI_MODEL = "gpt-image-2";
24
+ const DEFAULT_SEEDREAM_MODEL = "doubao-seedream-5-0-260128";
25
+ //#endregion
26
+ //#region lib/types/config.js
27
+ /** User-facing configuration for supported image providers. */
28
+ /** Google API credential reference. */
29
+ const GOOGLE_API_KEY_ENV = "GEMINI_API_KEY";
30
+ /** OpenAI Platform or compatible relay credential reference. */
31
+ const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
32
+ /** Volcengine Ark credential reference. */
33
+ const SEEDREAM_API_KEY_ENV = "ARK_API_KEY";
34
+ /** Cordis configuration schema. */
35
+ const Config = z.object({
36
+ provider: z.union(IMAGE_PROVIDERS).default("google"),
37
+ googleModel: z.string().default(DEFAULT_GOOGLE_MODEL),
38
+ googleEndpoint: z.string().default(DEFAULT_GOOGLE_ENDPOINT),
39
+ openaiBaseURL: z.string().default(DEFAULT_OPENAI_BASE_URL),
40
+ openaiModel: z.string().default(DEFAULT_OPENAI_MODEL),
41
+ seedreamBaseURL: z.string().default(DEFAULT_SEEDREAM_BASE_URL),
42
+ seedreamModel: z.string().default(DEFAULT_SEEDREAM_MODEL)
43
+ });
44
+ /** Resolve exactly one provider profile for a tool call. */
45
+ function resolveProvider(config) {
46
+ switch (config.provider ?? "google") {
47
+ case "openai": return {
48
+ provider: "openai",
49
+ apiKeyEnv: OPENAI_API_KEY_ENV,
50
+ model: config.openaiModel ?? "gpt-image-2",
51
+ baseURL: config.openaiBaseURL ?? "https://api.openai.com/v1",
52
+ imageSize: "1024x1024"
53
+ };
54
+ case "seedream": return {
55
+ provider: "seedream",
56
+ apiKeyEnv: SEEDREAM_API_KEY_ENV,
57
+ model: config.seedreamModel ?? "doubao-seedream-5-0-260128",
58
+ baseURL: config.seedreamBaseURL ?? "https://ark.cn-beijing.volces.com/api/v3",
59
+ imageSize: "2K"
60
+ };
61
+ case "google": return {
62
+ provider: "google",
63
+ apiKeyEnv: GOOGLE_API_KEY_ENV,
64
+ model: config.googleModel ?? "gemini-3.1-flash-image",
65
+ endpoint: config.googleEndpoint ?? "https://generativelanguage.googleapis.com/v1beta/interactions",
66
+ aspectRatio: "1:1",
67
+ imageSize: "1K"
68
+ };
69
+ }
70
+ }
71
+ //#endregion
72
+ //#region lib/types/google.js
73
+ const ERROR_LIMIT$1 = 4096;
74
+ const REQUESTED_MEDIA_TYPE = "image/jpeg";
75
+ /** Send one native Google image request and validate its inline image response. */
76
+ async function generateGoogleImage(input) {
77
+ const response = await fetch(input.endpoint, {
78
+ method: "POST",
79
+ redirect: "error",
80
+ signal: input.signal,
81
+ headers: {
82
+ "content-type": "application/json",
83
+ "x-goog-api-key": input.apiKey
84
+ },
85
+ body: JSON.stringify({
86
+ model: input.model,
87
+ input: input.prompt,
88
+ response_format: {
89
+ type: "image",
90
+ mime_type: REQUESTED_MEDIA_TYPE,
91
+ aspect_ratio: input.aspectRatio,
92
+ image_size: input.imageSize
93
+ }
94
+ })
95
+ });
96
+ const text = await readBoundedText$1(response, Math.ceil(input.maxBytes * 1.4) + ERROR_LIMIT$1);
97
+ if (!response.ok) throw new Error(`Google image generation failed (${response.status}): ${text.slice(0, ERROR_LIMIT$1)}`);
98
+ let payload;
99
+ try {
100
+ payload = JSON.parse(text);
101
+ } catch {
102
+ throw new Error("Google image generation returned invalid JSON");
103
+ }
104
+ const image = outputImage(payload);
105
+ if (image === void 0) throw new Error(`Google image generation returned no image: ${text.slice(0, ERROR_LIMIT$1)}`);
106
+ const mediaType = mediaTypeOf(image.mime_type ?? REQUESTED_MEDIA_TYPE);
107
+ if (mediaType === void 0) throw new Error(`Google image generation returned unsupported media type ${JSON.stringify(image.mime_type)}`);
108
+ const data = decodeBase64$1(image.data);
109
+ if (data.byteLength > input.maxBytes) throw new Error(`Google image generation exceeded the ${String(input.maxBytes)} byte image limit`);
110
+ return {
111
+ data,
112
+ mediaType
113
+ };
114
+ }
115
+ function outputImage(value) {
116
+ const interaction = record$1(value);
117
+ if (interaction === void 0) return void 0;
118
+ const direct = imageContent(interaction.output_image, false);
119
+ if (direct !== void 0) return direct;
120
+ if (!Array.isArray(interaction.steps)) return void 0;
121
+ for (const step of interaction.steps) {
122
+ const modelOutput = record$1(step);
123
+ if (modelOutput?.type !== "model_output" || !Array.isArray(modelOutput.content)) continue;
124
+ for (const content of modelOutput.content) {
125
+ const image = imageContent(content, true);
126
+ if (image !== void 0) return image;
127
+ }
128
+ }
129
+ }
130
+ function imageContent(value, requiresImageType) {
131
+ const image = record$1(value);
132
+ if (image === void 0 || requiresImageType && image.type !== "image" || typeof image.data !== "string") return void 0;
133
+ return {
134
+ data: image.data,
135
+ mime_type: image.mime_type
136
+ };
137
+ }
138
+ function record$1(value) {
139
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
140
+ }
141
+ function mediaTypeOf(value) {
142
+ return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif" ? value : void 0;
143
+ }
144
+ function decodeBase64$1(data) {
145
+ const clean = data.replace(/\s+/g, "");
146
+ if (clean.length === 0) throw new Error("Google image generation returned invalid base64 image data");
147
+ const decoded = Buffer.from(clean, "base64");
148
+ if (decoded.length === 0) throw new Error("Google image generation returned invalid base64 image data");
149
+ return new Uint8Array(decoded);
150
+ }
151
+ async function readBoundedText$1(response, maxBytes) {
152
+ if (response.body === null) return "";
153
+ const reader = response.body.getReader();
154
+ const chunks = [];
155
+ let bytes = 0;
156
+ try {
157
+ for (;;) {
158
+ const next = await reader.read();
159
+ if (next.done) break;
160
+ bytes += next.value.byteLength;
161
+ if (bytes > maxBytes) throw new Error(`Google image generation response exceeded the ${String(maxBytes)} byte limit`);
162
+ chunks.push(next.value);
163
+ }
164
+ } finally {
165
+ reader.releaseLock();
166
+ }
167
+ const joined = new Uint8Array(bytes);
168
+ let offset = 0;
169
+ for (const chunk of chunks) {
170
+ joined.set(chunk, offset);
171
+ offset += chunk.byteLength;
172
+ }
173
+ return new TextDecoder().decode(joined);
174
+ }
175
+ //#endregion
176
+ //#region lib/types/image-route.js
177
+ const MAX_BODY_BYTES = 4096;
178
+ /** Serve one verified durable image reference to a same-origin browser request. */
179
+ async function serveImage(req, res, deps) {
180
+ if (req.method !== "POST") return jsonError(res, 405, "method-not-allowed");
181
+ if (!(req.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) return jsonError(res, 415, "json-required");
182
+ const origin = req.headers.origin;
183
+ const host = req.headers.host;
184
+ if (origin !== void 0 && host !== void 0 && origin !== `http://${host}` && origin !== `https://${host}`) return jsonError(res, 403, "origin-rejected");
185
+ let body;
186
+ try {
187
+ body = JSON.parse(await readBody(req));
188
+ } catch {
189
+ return jsonError(res, 400, "invalid-request");
190
+ }
191
+ const attachment = attachmentFromRequest(body);
192
+ if (attachment === void 0) return jsonError(res, 400, "invalid-attachment");
193
+ try {
194
+ const stored = await deps.readImage(attachment);
195
+ res.writeHead(200, {
196
+ "content-type": stored.ref.mediaType,
197
+ "content-length": String(stored.data.byteLength),
198
+ "cache-control": "private, no-store",
199
+ "x-content-type-options": "nosniff"
200
+ });
201
+ res.end(stored.data);
202
+ } catch {
203
+ jsonError(res, 404, "image-unavailable");
204
+ }
205
+ }
206
+ /** Validate the persisted reference carried by a tool presentation. */
207
+ function imageAttachmentFromMeta(meta) {
208
+ const value = record(meta);
209
+ if (value?.kind !== "dsh-image-gen") return void 0;
210
+ return imageAttachment(value.attachment);
211
+ }
212
+ function attachmentFromRequest(value) {
213
+ return imageAttachment(record(value)?.attachment);
214
+ }
215
+ function imageAttachment(value) {
216
+ const ref = record(value);
217
+ if (ref === void 0) return void 0;
218
+ if (typeof ref.attachmentId !== "string" || !mediaType(ref.mediaType) || typeof ref.bytes !== "number" || typeof ref.width !== "number" || typeof ref.height !== "number") return void 0;
219
+ if (ref.name !== void 0 && typeof ref.name !== "string") return void 0;
220
+ return ref;
221
+ }
222
+ function mediaType(value) {
223
+ return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
224
+ }
225
+ function record(value) {
226
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
227
+ }
228
+ async function readBody(req) {
229
+ const chunks = [];
230
+ let bytes = 0;
231
+ for await (const chunk of req) {
232
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
233
+ bytes += buffer.byteLength;
234
+ if (bytes > MAX_BODY_BYTES) throw new Error("request too large");
235
+ chunks.push(buffer);
236
+ }
237
+ return Buffer.concat(chunks).toString("utf8");
238
+ }
239
+ function jsonError(res, status, code) {
240
+ res.writeHead(status, {
241
+ "content-type": "application/json",
242
+ "cache-control": "no-store"
243
+ });
244
+ res.end(JSON.stringify({ error: code }));
245
+ }
246
+ //#endregion
247
+ //#region lib/types/openai-compatible.js
248
+ const ERROR_LIMIT = 4096;
249
+ /** Generate an image from an OpenAI-compatible endpoint. */
250
+ async function generateOpenAICompatibleImage(input) {
251
+ const response = await fetch(imageEndpoint(input.baseURL), {
252
+ method: "POST",
253
+ redirect: "error",
254
+ signal: input.signal,
255
+ headers: {
256
+ authorization: `Bearer ${input.apiKey}`,
257
+ "content-type": "application/json"
258
+ },
259
+ body: JSON.stringify({
260
+ model: input.model,
261
+ prompt: input.prompt,
262
+ size: input.size,
263
+ ...input.provider === "seedream" ? { response_format: "url" } : {}
264
+ })
265
+ });
266
+ const text = await readBoundedText(response, Math.ceil(input.maxBytes * 1.4) + ERROR_LIMIT);
267
+ if (!response.ok) throw new Error(`${input.provider} image generation failed (${response.status}): ${text.slice(0, ERROR_LIMIT)}`);
268
+ let payload;
269
+ try {
270
+ payload = JSON.parse(text);
271
+ } catch {
272
+ throw new Error(`${input.provider} image generation returned invalid JSON`);
273
+ }
274
+ const image = firstImage(payload);
275
+ if (image === void 0) throw new Error(`${input.provider} image generation returned no image: ${text.slice(0, ERROR_LIMIT)}`);
276
+ if (image.b64_json !== void 0) return {
277
+ data: decodeBase64(image.b64_json, input.provider),
278
+ mediaType: "image/png"
279
+ };
280
+ return downloadImage(image.url, input);
281
+ }
282
+ function imageEndpoint(baseURL) {
283
+ try {
284
+ return new URL("images/generations", baseURL.endsWith("/") ? baseURL : `${baseURL}/`).toString();
285
+ } catch {
286
+ throw new Error("Image generation endpoint must be an absolute URL");
287
+ }
288
+ }
289
+ function firstImage(value) {
290
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
291
+ const data = value.data;
292
+ if (!Array.isArray(data) || data.length === 0) return void 0;
293
+ const candidate = data[0];
294
+ if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) return void 0;
295
+ const item = candidate;
296
+ return typeof item.b64_json === "string" ? { b64_json: item.b64_json } : typeof item.url === "string" ? { url: item.url } : void 0;
297
+ }
298
+ async function downloadImage(url, input) {
299
+ if (url === void 0) throw new Error(`${input.provider} image generation returned no image data`);
300
+ const response = await fetch(url, {
301
+ redirect: "follow",
302
+ signal: input.signal
303
+ });
304
+ if (!response.ok) throw new Error(`${input.provider} image download failed (${response.status})`);
305
+ const mediaType = imageMediaType(response.headers.get("content-type"));
306
+ if (mediaType === void 0) throw new Error(`${input.provider} image download returned unsupported content type`);
307
+ return {
308
+ data: await readBoundedBytes(response, input.maxBytes),
309
+ mediaType
310
+ };
311
+ }
312
+ function decodeBase64(value, provider) {
313
+ const clean = value.replace(/\s+/g, "");
314
+ if (clean.length === 0) throw new Error(`${provider} image generation returned invalid base64 image data`);
315
+ const decoded = Buffer.from(clean, "base64");
316
+ if (decoded.length === 0) throw new Error(`${provider} image generation returned invalid base64 image data`);
317
+ return new Uint8Array(decoded);
318
+ }
319
+ function imageMediaType(value) {
320
+ const mediaType = value?.split(";", 1)[0]?.trim().toLowerCase();
321
+ return mediaType === "image/png" || mediaType === "image/jpeg" || mediaType === "image/webp" || mediaType === "image/gif" ? mediaType : void 0;
322
+ }
323
+ async function readBoundedText(response, maxBytes) {
324
+ return new TextDecoder().decode(await readBoundedBytes(response, maxBytes));
325
+ }
326
+ async function readBoundedBytes(response, maxBytes) {
327
+ if (response.body === null) return /* @__PURE__ */ new Uint8Array();
328
+ const reader = response.body.getReader();
329
+ const chunks = [];
330
+ let bytes = 0;
331
+ try {
332
+ for (;;) {
333
+ const next = await reader.read();
334
+ if (next.done) break;
335
+ bytes += next.value.byteLength;
336
+ if (bytes > maxBytes) throw new Error(`Image response exceeded the ${String(maxBytes)} byte limit`);
337
+ chunks.push(next.value);
338
+ }
339
+ } finally {
340
+ reader.releaseLock();
341
+ }
342
+ const joined = new Uint8Array(bytes);
343
+ let offset = 0;
344
+ for (const chunk of chunks) {
345
+ joined.set(chunk, offset);
346
+ offset += chunk.byteLength;
347
+ }
348
+ return joined;
349
+ }
350
+ //#endregion
351
+ //#region lib/types/index.js
352
+ /** Cordis plugin name. */
353
+ const name = "dsh-image-gen";
354
+ /** Host services required by the Bundle. */
355
+ const inject = [
356
+ "tools",
357
+ "attachments",
358
+ "credentials",
359
+ "webServer"
360
+ ];
361
+ /** Register settings, the image route, and the model-callable tool. */
362
+ function apply(ctx, config = {}) {
363
+ let current = () => config;
364
+ installSettingsSection(ctx, settingsNamespace(IMAGE_GENERATION_NAMESPACE), Config, config, {
365
+ setSource: (source) => {
366
+ current = source;
367
+ },
368
+ onChange: () => {}
369
+ });
370
+ ctx.effect(() => ctx.webServer.register({
371
+ kind: "exact",
372
+ path: IMAGE_ROUTE,
373
+ handler: (req, res) => serveImage(req, res, { readImage: (ref) => ctx.attachments.readImage(ref) })
374
+ }), "dsh-image-gen: image route");
375
+ ctx.tools.register(defineTool({
376
+ name: "generate_image",
377
+ description: "Generate one image with the configured image provider. Use when the user explicitly asks to create or draw an image. Give a complete visual prompt including subject, composition, style, lighting, and any exact text that should appear. A successful image is already attached directly to the conversation and has no local file path; do not call read, glob, or other tools to locate or verify it.",
378
+ parameters: {
379
+ prompt: {
380
+ type: "string",
381
+ required: true,
382
+ description: "Complete description of the image to generate."
383
+ },
384
+ aspect_ratio: {
385
+ type: "string",
386
+ enum: [
387
+ "1:1",
388
+ "3:2",
389
+ "2:3",
390
+ "4:3",
391
+ "3:4",
392
+ "16:9",
393
+ "9:16"
394
+ ],
395
+ description: "Optional output aspect ratio for Google Gemini."
396
+ },
397
+ image_size: {
398
+ type: "string",
399
+ enum: [
400
+ "1K",
401
+ "2K",
402
+ "4K"
403
+ ],
404
+ description: "Optional output resolution for Google Gemini."
405
+ },
406
+ size: {
407
+ type: "string",
408
+ description: "Optional dimensions or size tier for OpenAI or Seedream."
409
+ }
410
+ },
411
+ output: {
412
+ schema: {
413
+ type: "object",
414
+ additionalProperties: false,
415
+ properties: {
416
+ attachment: {
417
+ type: "object",
418
+ required: true,
419
+ additionalProperties: false,
420
+ properties: {
421
+ attachmentId: {
422
+ type: "string",
423
+ required: true
424
+ },
425
+ mediaType: {
426
+ type: "string",
427
+ required: true
428
+ },
429
+ bytes: {
430
+ type: "integer",
431
+ required: true
432
+ },
433
+ width: {
434
+ type: "integer",
435
+ required: true
436
+ },
437
+ height: {
438
+ type: "integer",
439
+ required: true
440
+ },
441
+ name: { type: "string" }
442
+ }
443
+ },
444
+ provider: {
445
+ type: "string",
446
+ required: true
447
+ },
448
+ model: {
449
+ type: "string",
450
+ required: true
451
+ },
452
+ output: {
453
+ type: "string",
454
+ required: true
455
+ }
456
+ }
457
+ },
458
+ render: (_args, value) => [{
459
+ type: "text",
460
+ text: `Generated one image with ${value.provider}/${value.model} (${value.output}). It is already attached to the conversation with no local file path; respond to the user without reading or searching for it.`
461
+ }],
462
+ presentationMeta: (_args, value) => ({
463
+ kind: "dsh-image-gen",
464
+ attachment: value.attachment
465
+ })
466
+ },
467
+ async execute(args, exec) {
468
+ const active = resolveProvider(current());
469
+ const credential = await ctx.credentials.resolve(credentialRef(active.apiKeyEnv));
470
+ if (credential === void 0 || credential.value.length === 0) throw new Error(`generate_image requires the ${active.apiKeyEnv} credential; configure it in Settings > Plugins > Image generation.`);
471
+ if (active.provider === "google") {
472
+ const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
473
+ const imageSize = args.image_size ?? active.imageSize;
474
+ return saveGenerated(ctx, await generateGoogleImage({
475
+ apiKey: credential.value,
476
+ endpoint: active.endpoint,
477
+ model: active.model,
478
+ prompt: args.prompt,
479
+ aspectRatio,
480
+ imageSize,
481
+ maxBytes: ctx.attachments.imageLimits.maxImageBytes,
482
+ signal: exec.signal
483
+ }), active.provider, active.model, `${aspectRatio}, ${imageSize}`);
484
+ }
485
+ const size = args.size ?? active.imageSize;
486
+ return saveGenerated(ctx, await generateOpenAICompatibleImage({
487
+ provider: active.provider,
488
+ apiKey: credential.value,
489
+ baseURL: active.baseURL,
490
+ model: active.model,
491
+ prompt: args.prompt,
492
+ size,
493
+ maxBytes: ctx.attachments.imageLimits.maxImageBytes,
494
+ signal: exec.signal
495
+ }), active.provider, active.model, size);
496
+ },
497
+ presentResult: (_args, result) => imagePresentation(result)
498
+ }));
499
+ }
500
+ async function saveGenerated(ctx, generated, provider, model, output) {
501
+ if (!ctx.attachments.imageLimits.mediaTypes.includes(generated.mediaType)) throw new Error(`This DSH deployment does not accept ${generated.mediaType} generated images`);
502
+ return {
503
+ attachment: await ctx.attachments.saveImage({
504
+ data: generated.data,
505
+ mediaType: generated.mediaType,
506
+ name: "generated-image"
507
+ }),
508
+ provider,
509
+ model,
510
+ output
511
+ };
512
+ }
513
+ function imagePresentation(result) {
514
+ const attachment = imageAttachmentFromMeta(result.meta);
515
+ return attachment === void 0 ? void 0 : {
516
+ card: "generic",
517
+ title: "Generated image",
518
+ content: [{
519
+ type: "image",
520
+ attachment
521
+ }]
522
+ };
523
+ }
524
+ //#endregion
525
+ export { Config, IMAGE_ROUTE, apply, imageAttachmentFromMeta, inject, name };
@@ -0,0 +1,30 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client';
3
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
4
+ import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
5
+ import { type ImageProvider } from '../shared.js';
6
+ type Provider = ImageProvider;
7
+ interface ImageSettings {
8
+ provider?: Provider;
9
+ googleModel?: string;
10
+ googleEndpoint?: string;
11
+ openaiBaseURL?: string;
12
+ openaiModel?: string;
13
+ seedreamBaseURL?: string;
14
+ seedreamModel?: string;
15
+ }
16
+ interface SettingsFace {
17
+ scope: SettingsScope<ImageSettings>;
18
+ credentials: ConnectionHandle['api']['credentials'];
19
+ }
20
+ type SettingsCardProps = PropsRuntime<'settings.plugin.item'> & InjectFace<SettingsFace>;
21
+ type ImageCardProps = PropsRuntime<'tool.call.toolview'>;
22
+ /** Required browser services. */
23
+ export declare const inject: string[];
24
+ /** Mount the settings card and generated-image card. */
25
+ export declare function apply(ctx: Context): void;
26
+ /** Edit provider settings and its write-only API credential. */
27
+ export declare function ImageGenerationSettingsCard(props: SettingsCardProps): import("react").JSX.Element;
28
+ /** Render the durable attachment referenced by a completed image tool call. */
29
+ export declare function GeneratedImageCard(props: ImageCardProps): import("react").JSX.Element;
30
+ export {};
@@ -0,0 +1,48 @@
1
+ /** User-facing configuration for supported image providers. */
2
+ import z from '@deepseek-ai/schemastery';
3
+ import { DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, IMAGE_PROVIDERS, type ImageProvider } from './shared.js';
4
+ export { DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, IMAGE_PROVIDERS, type ImageProvider, };
5
+ /** Google API credential reference. */
6
+ export declare const GOOGLE_API_KEY_ENV = "GEMINI_API_KEY";
7
+ /** OpenAI Platform or compatible relay credential reference. */
8
+ export declare const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
9
+ /** Volcengine Ark credential reference. */
10
+ export declare const SEEDREAM_API_KEY_ENV = "ARK_API_KEY";
11
+ /** Google tool-level controls. */
12
+ export declare const ASPECT_RATIOS: readonly ["1:1", "3:2", "2:3", "4:3", "3:4", "16:9", "9:16"];
13
+ export declare const IMAGE_SIZES: readonly ["1K", "2K", "4K"];
14
+ export type AspectRatio = typeof ASPECT_RATIOS[number];
15
+ export type ImageSize = typeof IMAGE_SIZES[number];
16
+ /** Bundle configuration from the profile patch and the Web settings page. */
17
+ export interface Config {
18
+ provider?: ImageProvider;
19
+ googleModel?: string;
20
+ googleEndpoint?: string;
21
+ openaiBaseURL?: string;
22
+ openaiModel?: string;
23
+ seedreamBaseURL?: string;
24
+ seedreamModel?: string;
25
+ }
26
+ /** Cordis configuration schema. */
27
+ export declare const Config: z<Config>;
28
+ /** Resolve exactly one provider profile for a tool call. */
29
+ export declare function resolveProvider(config: Config): {
30
+ provider: 'google';
31
+ apiKeyEnv: string;
32
+ model: string;
33
+ endpoint: string;
34
+ aspectRatio: AspectRatio;
35
+ imageSize: ImageSize;
36
+ } | {
37
+ provider: 'openai';
38
+ apiKeyEnv: string;
39
+ model: string;
40
+ baseURL: string;
41
+ imageSize: string;
42
+ } | {
43
+ provider: 'seedream';
44
+ apiKeyEnv: string;
45
+ model: string;
46
+ baseURL: string;
47
+ imageSize: string;
48
+ };
@@ -0,0 +1,19 @@
1
+ /** Google Gemini Interactions API adapter. */
2
+ import type { ImageMediaType } from '@deepseek-ai/dsh-attachment';
3
+ import type { AspectRatio, ImageSize } from './config.js';
4
+ /** Image bytes returned before Attachment persistence. */
5
+ export interface GeneratedImage {
6
+ data: Uint8Array;
7
+ mediaType: ImageMediaType;
8
+ }
9
+ /** Send one native Google image request and validate its inline image response. */
10
+ export declare function generateGoogleImage(input: {
11
+ apiKey: string;
12
+ endpoint: string;
13
+ model: string;
14
+ prompt: string;
15
+ aspectRatio: AspectRatio;
16
+ imageSize: ImageSize;
17
+ maxBytes: number;
18
+ signal: AbortSignal;
19
+ }): Promise<GeneratedImage>;
@@ -0,0 +1,12 @@
1
+ /** Same-origin HTTP bridge from the Web result card to the Attachment service. */
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+ import type { ImageAttachmentRef, StoredImageAttachment } from '@deepseek-ai/dsh-attachment';
4
+ export { IMAGE_ROUTE } from './shared.js';
5
+ /** Dependencies required by the image route. */
6
+ export interface ImageRouteDeps {
7
+ readImage(ref: ImageAttachmentRef): Promise<StoredImageAttachment>;
8
+ }
9
+ /** Serve one verified durable image reference to a same-origin browser request. */
10
+ export declare function serveImage(req: IncomingMessage, res: ServerResponse, deps: ImageRouteDeps): Promise<void>;
11
+ /** Validate the persisted reference carried by a tool presentation. */
12
+ export declare function imageAttachmentFromMeta(meta: unknown): ImageAttachmentRef | undefined;
@@ -0,0 +1,11 @@
1
+ /** Multi-provider image-generation Bundle for DeepSeek Harness. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import { Config } from './config.js';
4
+ export { Config } from './config.js';
5
+ export { IMAGE_ROUTE, imageAttachmentFromMeta } from './image-route.js';
6
+ /** Cordis plugin name. */
7
+ export declare const name = "dsh-image-gen";
8
+ /** Host services required by the Bundle. */
9
+ export declare const inject: string[];
10
+ /** Register settings, the image route, and the model-callable tool. */
11
+ export declare function apply(ctx: Context, config?: Config): void;
@@ -0,0 +1,18 @@
1
+ /** OpenAI Images API and Volcengine Ark/Seedream response adapter. */
2
+ import type { ImageMediaType } from '@deepseek-ai/dsh-attachment';
3
+ /** One generated raster normalized before Attachment persistence. */
4
+ export interface GeneratedCompatibleImage {
5
+ data: Uint8Array;
6
+ mediaType: ImageMediaType;
7
+ }
8
+ /** Generate an image from an OpenAI-compatible endpoint. */
9
+ export declare function generateOpenAICompatibleImage(input: {
10
+ provider: 'openai' | 'seedream';
11
+ apiKey: string;
12
+ baseURL: string;
13
+ model: string;
14
+ prompt: string;
15
+ size: string;
16
+ maxBytes: number;
17
+ signal: AbortSignal;
18
+ }): Promise<GeneratedCompatibleImage>;
@@ -0,0 +1,18 @@
1
+ /** Values shared by the Host and browser Bundle faces. */
2
+ /** Browser route used by the generated-image card. */
3
+ export declare const IMAGE_ROUTE = "/plugins/dsh-image-gen/image";
4
+ /** Namespace persisted through DSH Settings. */
5
+ export declare const IMAGE_GENERATION_NAMESPACE = "image-generation";
6
+ /** Supported providers. */
7
+ export declare const IMAGE_PROVIDERS: readonly ["google", "openai", "seedream"];
8
+ export type ImageProvider = typeof IMAGE_PROVIDERS[number];
9
+ /** Default endpoints and base URLs. */
10
+ export declare const DEFAULT_GOOGLE_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/interactions";
11
+ export declare const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
12
+ export declare const DEFAULT_SEEDREAM_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
13
+ /** Default model names. */
14
+ export declare const DEFAULT_GOOGLE_MODEL = "gemini-3.1-flash-image";
15
+ export declare const DEFAULT_OPENAI_MODEL = "gpt-image-2";
16
+ export declare const DEFAULT_SEEDREAM_MODEL = "doubao-seedream-5-0-260128";
17
+ export declare const DEFAULT_MODELS: Record<ImageProvider, string>;
18
+ export declare const DEFAULT_BASE_URLS: Record<ImageProvider, string>;