@dickpy/dsh-imagegen 1.4.0 → 1.5.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 -201
- package/README.md +270 -124
- package/cordis.patch.yml +8 -8
- package/docs/images/ecommerce-mode.png +0 -0
- package/docs/images/image-generation-studio-three-column.png +0 -0
- package/docs/images/imagegen-overview.png +0 -0
- package/docs/videos/agent-chat-edit.gif +0 -0
- package/docs/videos/agent-chat-edit.mp4 +0 -0
- package/lib/client.js +1859 -420
- package/lib/client.js.map +1 -1
- package/lib/index.js +327 -112
- package/package.json +69 -68
- package/src/agent-image-tools.ts +447 -418
- package/src/client/ImageGenPanel.tsx +1242 -348
- package/src/client/SettingsCard.tsx +936 -936
- package/src/client/TemplateLibrary.tsx +336 -336
- package/src/client/api.ts +203 -193
- package/src/client/channels-form.ts +263 -263
- package/src/client/controller.ts +46 -46
- package/src/client/conversation-sync.ts +14 -14
- package/src/client/css-modules.d.ts +5 -5
- package/src/client/helpers.ts +33 -33
- package/src/client/image-toolview.module.css +73 -73
- package/src/client/image-toolview.tsx +18 -18
- package/src/client/index.ts +22 -22
- package/src/client/locales.ts +156 -28
- package/src/client/mount.tsx +117 -117
- package/src/client/panel.module.css +1243 -455
- package/src/client/settings-card.module.css +1023 -1023
- package/src/client/settings-form.ts +336 -336
- package/src/client/settings-scope.ts +298 -298
- package/src/client/sidebar-entry.ts +190 -190
- package/src/client/templates.module.css +453 -453
- package/src/edit-image-command.ts +110 -0
- package/src/engine.ts +520 -520
- package/src/gallery-store.ts +306 -286
- package/src/generation-runtime.ts +84 -79
- package/src/history-store.ts +270 -250
- package/src/image-format.ts +11 -11
- package/src/image-models.ts +19 -19
- package/src/index.ts +337 -318
- package/src/model-catalog.ts +115 -115
- package/src/presets.ts +71 -71
- package/src/prompt-enhancer.ts +137 -137
- package/src/protocol.ts +380 -338
- package/src/routes.ts +964 -916
- package/src/task-queue.ts +113 -113
- package/src/templates/cases.json +10196 -10196
- package/src/templates-store.ts +278 -278
- package/src/updater.ts +117 -117
- package/docs/images/agent-chat-edit.png +0 -0
- package/docs/images/agent-chat-generate.png +0 -0
- package/docs/images/agent-chat-poster-workflow.png +0 -0
package/lib/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
|
16
16
|
/** Settings namespace this plugin owns (host settings seam + bridge). */
|
|
17
17
|
const IMAGEGEN_SETTINGS_NAMESPACE = "dsh-imagegen";
|
|
18
18
|
/** Published package version shared by the host updater and the client UI. */
|
|
19
|
-
const PLUGIN_VERSION = "1.
|
|
19
|
+
const PLUGIN_VERSION = "1.5.0";
|
|
20
20
|
/** Same-origin route family (loopback-only, mirroring the dsh-ssh fence). */
|
|
21
21
|
const SETTINGS_API = {
|
|
22
22
|
describe: "/api/dsh-imagegen/settings/describe",
|
|
@@ -35,6 +35,8 @@ const IMAGE_MODEL_API = { models: "/api/dsh-imagegen/image-models" };
|
|
|
35
35
|
const PRESETS_API = "/api/dsh-imagegen/presets";
|
|
36
36
|
/** Loopback-only image reader for Agent tool-result previews. */
|
|
37
37
|
const AGENT_IMAGE_API = "/api/dsh-imagegen/agent-image";
|
|
38
|
+
/** Store the current composer image for the direct edit_image command. */
|
|
39
|
+
const CONVERSATION_IMAGE_API = "/api/dsh-imagegen/conversation-image";
|
|
38
40
|
/**
|
|
39
41
|
* Host-computed per-channel usage counters (generation-count badges in the
|
|
40
42
|
* settings card): entries are tallied from the persisted history and gallery
|
|
@@ -811,7 +813,7 @@ async function writeIndex$1(entries) {
|
|
|
811
813
|
function isStoredEntry$1(value) {
|
|
812
814
|
if (value === null || typeof value !== "object") return false;
|
|
813
815
|
const entry = value;
|
|
814
|
-
return typeof entry.id === "string" && typeof entry.createdAt === "number" && (entry.mode === "text" || entry.mode === "edit") && typeof entry.prompt === "string" && Array.isArray(entry.images) && entry.images.every((image) => {
|
|
816
|
+
return typeof entry.id === "string" && typeof entry.createdAt === "number" && (entry.mode === "text" || entry.mode === "edit") && (entry.workflow === void 0 || entry.workflow === "ecommerce") && (entry.projectId === void 0 || typeof entry.projectId === "string") && (entry.projectName === void 0 || typeof entry.projectName === "string") && (entry.slotKey === void 0 || typeof entry.slotKey === "string") && (entry.slotLabel === void 0 || typeof entry.slotLabel === "string") && typeof entry.prompt === "string" && Array.isArray(entry.images) && entry.images.every((image) => {
|
|
815
817
|
if (image === null || typeof image !== "object") return false;
|
|
816
818
|
const record = image;
|
|
817
819
|
return typeof record.file === "string" && typeof record.mime === "string";
|
|
@@ -844,7 +846,12 @@ function toWire$1(entry) {
|
|
|
844
846
|
...entry.channel === void 0 ? {} : { channel: entry.channel },
|
|
845
847
|
...entry.channelId === void 0 ? {} : { channelId: entry.channelId },
|
|
846
848
|
...entry.comparisonId === void 0 ? {} : { comparisonId: entry.comparisonId },
|
|
847
|
-
...entry.comparisonModels === void 0 ? {} : { comparisonModels: entry.comparisonModels }
|
|
849
|
+
...entry.comparisonModels === void 0 ? {} : { comparisonModels: entry.comparisonModels },
|
|
850
|
+
...entry.workflow === void 0 ? {} : { workflow: entry.workflow },
|
|
851
|
+
...entry.projectId === void 0 ? {} : { projectId: entry.projectId },
|
|
852
|
+
...entry.projectName === void 0 ? {} : { projectName: entry.projectName },
|
|
853
|
+
...entry.slotKey === void 0 ? {} : { slotKey: entry.slotKey },
|
|
854
|
+
...entry.slotLabel === void 0 ? {} : { slotLabel: entry.slotLabel }
|
|
848
855
|
};
|
|
849
856
|
}
|
|
850
857
|
/** List the persisted history, newest first, as wire entries. */
|
|
@@ -887,7 +894,12 @@ async function appendHistory(input) {
|
|
|
887
894
|
...input.channelId === void 0 ? {} : { channelId: input.channelId },
|
|
888
895
|
...input.channel === void 0 ? {} : { channel: input.channel },
|
|
889
896
|
...input.comparisonId === void 0 ? {} : { comparisonId: input.comparisonId },
|
|
890
|
-
...input.comparisonModels === void 0 ? {} : { comparisonModels: input.comparisonModels }
|
|
897
|
+
...input.comparisonModels === void 0 ? {} : { comparisonModels: input.comparisonModels },
|
|
898
|
+
...input.workflow === void 0 ? {} : { workflow: input.workflow },
|
|
899
|
+
...input.projectId === void 0 ? {} : { projectId: input.projectId },
|
|
900
|
+
...input.projectName === void 0 ? {} : { projectName: input.projectName },
|
|
901
|
+
...input.slotKey === void 0 ? {} : { slotKey: input.slotKey },
|
|
902
|
+
...input.slotLabel === void 0 ? {} : { slotLabel: input.slotLabel }
|
|
891
903
|
}, ...await readIndex$1()];
|
|
892
904
|
const kept = merged.slice(0, 50);
|
|
893
905
|
for (const dropped of merged.slice(50)) await removeEntryFiles$1(dropped);
|
|
@@ -1075,7 +1087,12 @@ var ImageGenerationRuntime = class {
|
|
|
1075
1087
|
...request.channelId === void 0 ? {} : { channelId: request.channelId },
|
|
1076
1088
|
...request.channel === void 0 ? {} : { channel: request.channel },
|
|
1077
1089
|
...request.comparisonId === void 0 ? {} : { comparisonId: request.comparisonId },
|
|
1078
|
-
...request.comparisonModels === void 0 ? {} : { comparisonModels: request.comparisonModels }
|
|
1090
|
+
...request.comparisonModels === void 0 ? {} : { comparisonModels: request.comparisonModels },
|
|
1091
|
+
...request.workflow === void 0 ? {} : { workflow: request.workflow },
|
|
1092
|
+
...request.projectId === void 0 ? {} : { projectId: request.projectId },
|
|
1093
|
+
...request.projectName === void 0 ? {} : { projectName: request.projectName },
|
|
1094
|
+
...request.slotKey === void 0 ? {} : { slotKey: request.slotKey },
|
|
1095
|
+
...request.slotLabel === void 0 ? {} : { slotLabel: request.slotLabel }
|
|
1079
1096
|
});
|
|
1080
1097
|
return {
|
|
1081
1098
|
...result,
|
|
@@ -1169,7 +1186,7 @@ async function writeIndex(entries) {
|
|
|
1169
1186
|
function isStoredEntry(value) {
|
|
1170
1187
|
if (value === null || typeof value !== "object") return false;
|
|
1171
1188
|
const entry = value;
|
|
1172
|
-
return typeof entry.id === "string" && typeof entry.createdAt === "number" && (entry.mode === "text" || entry.mode === "edit") && typeof entry.prompt === "string" && Array.isArray(entry.images) && entry.images.every((image) => {
|
|
1189
|
+
return typeof entry.id === "string" && typeof entry.createdAt === "number" && (entry.mode === "text" || entry.mode === "edit") && (entry.workflow === void 0 || entry.workflow === "ecommerce") && (entry.projectId === void 0 || typeof entry.projectId === "string") && (entry.projectName === void 0 || typeof entry.projectName === "string") && (entry.slotKey === void 0 || typeof entry.slotKey === "string") && (entry.slotLabel === void 0 || typeof entry.slotLabel === "string") && typeof entry.prompt === "string" && Array.isArray(entry.images) && entry.images.every((image) => {
|
|
1173
1190
|
if (image === null || typeof image !== "object") return false;
|
|
1174
1191
|
const record = image;
|
|
1175
1192
|
return typeof record.file === "string" && typeof record.mime === "string";
|
|
@@ -1201,7 +1218,12 @@ function toWire(entry) {
|
|
|
1201
1218
|
...entry.refName === void 0 ? {} : { refName: entry.refName },
|
|
1202
1219
|
...entry.tags === void 0 ? {} : { tags: entry.tags },
|
|
1203
1220
|
...entry.channel === void 0 ? {} : { channel: entry.channel },
|
|
1204
|
-
...entry.channelId === void 0 ? {} : { channelId: entry.channelId }
|
|
1221
|
+
...entry.channelId === void 0 ? {} : { channelId: entry.channelId },
|
|
1222
|
+
...entry.workflow === void 0 ? {} : { workflow: entry.workflow },
|
|
1223
|
+
...entry.projectId === void 0 ? {} : { projectId: entry.projectId },
|
|
1224
|
+
...entry.projectName === void 0 ? {} : { projectName: entry.projectName },
|
|
1225
|
+
...entry.slotKey === void 0 ? {} : { slotKey: entry.slotKey },
|
|
1226
|
+
...entry.slotLabel === void 0 ? {} : { slotLabel: entry.slotLabel }
|
|
1205
1227
|
};
|
|
1206
1228
|
}
|
|
1207
1229
|
/** List the persisted gallery, newest first, as wire entries. */
|
|
@@ -1253,7 +1275,12 @@ async function appendGallery(input) {
|
|
|
1253
1275
|
...hash === void 0 ? {} : { hash },
|
|
1254
1276
|
...input.refName === void 0 ? {} : { refName: input.refName },
|
|
1255
1277
|
...input.channelId === void 0 ? {} : { channelId: input.channelId },
|
|
1256
|
-
...input.channel === void 0 ? {} : { channel: input.channel }
|
|
1278
|
+
...input.channel === void 0 ? {} : { channel: input.channel },
|
|
1279
|
+
...input.workflow === void 0 ? {} : { workflow: input.workflow },
|
|
1280
|
+
...input.projectId === void 0 ? {} : { projectId: input.projectId },
|
|
1281
|
+
...input.projectName === void 0 ? {} : { projectName: input.projectName },
|
|
1282
|
+
...input.slotKey === void 0 ? {} : { slotKey: input.slotKey },
|
|
1283
|
+
...input.slotLabel === void 0 ? {} : { slotLabel: input.slotLabel }
|
|
1257
1284
|
}, ...await readIndex()];
|
|
1258
1285
|
await writeIndex(merged);
|
|
1259
1286
|
return {
|
|
@@ -1806,7 +1833,12 @@ function parseGenerateRequest(body) {
|
|
|
1806
1833
|
...typeof body.refName === "string" && body.refName !== "" ? { refName: body.refName } : {},
|
|
1807
1834
|
...typeof body.channelId === "string" && body.channelId !== "" ? { channelId: body.channelId } : {},
|
|
1808
1835
|
...typeof body.comparisonId === "string" && body.comparisonId !== "" ? { comparisonId: body.comparisonId } : {},
|
|
1809
|
-
...comparisonModels.length > 1 ? { comparisonModels } : {}
|
|
1836
|
+
...comparisonModels.length > 1 ? { comparisonModels } : {},
|
|
1837
|
+
...body.workflow === "ecommerce" ? { workflow: "ecommerce" } : {},
|
|
1838
|
+
...typeof body.projectId === "string" && body.projectId !== "" ? { projectId: body.projectId } : {},
|
|
1839
|
+
...typeof body.projectName === "string" && body.projectName !== "" ? { projectName: body.projectName } : {},
|
|
1840
|
+
...typeof body.slotKey === "string" && body.slotKey !== "" ? { slotKey: body.slotKey } : {},
|
|
1841
|
+
...typeof body.slotLabel === "string" && body.slotLabel !== "" ? { slotLabel: body.slotLabel } : {}
|
|
1810
1842
|
};
|
|
1811
1843
|
}
|
|
1812
1844
|
/** Validate a submitted history entry (images carry base64). */
|
|
@@ -1847,7 +1879,12 @@ function parseHistoryEntryInput(body) {
|
|
|
1847
1879
|
...typeof entry.channelId === "string" ? { channelId: entry.channelId } : {},
|
|
1848
1880
|
...typeof entry.channel === "string" ? { channel: entry.channel } : {},
|
|
1849
1881
|
...typeof entry.comparisonId === "string" ? { comparisonId: entry.comparisonId } : {},
|
|
1850
|
-
...comparisonModels.length > 1 ? { comparisonModels } : {}
|
|
1882
|
+
...comparisonModels.length > 1 ? { comparisonModels } : {},
|
|
1883
|
+
...entry.workflow === "ecommerce" ? { workflow: "ecommerce" } : {},
|
|
1884
|
+
...typeof entry.projectId === "string" ? { projectId: entry.projectId } : {},
|
|
1885
|
+
...typeof entry.projectName === "string" ? { projectName: entry.projectName } : {},
|
|
1886
|
+
...typeof entry.slotKey === "string" ? { slotKey: entry.slotKey } : {},
|
|
1887
|
+
...typeof entry.slotLabel === "string" ? { slotLabel: entry.slotLabel } : {}
|
|
1851
1888
|
};
|
|
1852
1889
|
}
|
|
1853
1890
|
/** Extract the image file name from a history-image request URL. */
|
|
@@ -1889,6 +1926,15 @@ function agentImageRefFrom(rawUrl) {
|
|
|
1889
1926
|
function isImageMediaType(value) {
|
|
1890
1927
|
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
1891
1928
|
}
|
|
1929
|
+
function imageDataUrl$1(value) {
|
|
1930
|
+
const match = /^data:(image\/(?:png|jpeg|webp|gif));base64,(.*)$/su.exec(value.trim());
|
|
1931
|
+
if (match === null || match[1] === void 0 || match[2] === void 0) return void 0;
|
|
1932
|
+
const data = Buffer.from(match[2], "base64");
|
|
1933
|
+
return data.byteLength === 0 ? void 0 : {
|
|
1934
|
+
mediaType: match[1],
|
|
1935
|
+
data
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1892
1938
|
/** Project one settings descriptor onto the bridge wire view. */
|
|
1893
1939
|
function toView(descriptor) {
|
|
1894
1940
|
return {
|
|
@@ -2040,6 +2086,39 @@ function makeRoutes(deps) {
|
|
|
2040
2086
|
return true;
|
|
2041
2087
|
};
|
|
2042
2088
|
return [
|
|
2089
|
+
...deps.attachments?.saveImage === void 0 || deps.pendingConversationImages === void 0 ? [] : [{
|
|
2090
|
+
kind: "exact",
|
|
2091
|
+
path: CONVERSATION_IMAGE_API,
|
|
2092
|
+
handler: async (req, res) => {
|
|
2093
|
+
if (!guard(req, res, "POST")) return;
|
|
2094
|
+
const body = await readJsonBody(req, MAX_JSON_BODY_BYTES);
|
|
2095
|
+
const sessionId = typeof body?.sessionId === "string" ? body.sessionId.trim() : "";
|
|
2096
|
+
const dataUrl = typeof body?.dataUrl === "string" ? imageDataUrl$1(body.dataUrl) : void 0;
|
|
2097
|
+
if (sessionId === "" || dataUrl === void 0) {
|
|
2098
|
+
writeJson(res, 200, {
|
|
2099
|
+
ok: false,
|
|
2100
|
+
code: "bad-request",
|
|
2101
|
+
message: "sessionId and image data are required"
|
|
2102
|
+
});
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
try {
|
|
2106
|
+
const ref = await deps.attachments.saveImage({
|
|
2107
|
+
data: dataUrl.data,
|
|
2108
|
+
mediaType: dataUrl.mediaType,
|
|
2109
|
+
...typeof body?.name === "string" && body.name.trim() !== "" ? { name: body.name.trim() } : {}
|
|
2110
|
+
});
|
|
2111
|
+
deps.pendingConversationImages.set(sessionId, ref);
|
|
2112
|
+
writeJson(res, 200, { ok: true });
|
|
2113
|
+
} catch (error) {
|
|
2114
|
+
writeJson(res, 200, {
|
|
2115
|
+
ok: false,
|
|
2116
|
+
code: "image-save-failed",
|
|
2117
|
+
message: messageOf(error)
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}],
|
|
2043
2122
|
...deps.attachments === void 0 ? [] : [{
|
|
2044
2123
|
kind: "prefix",
|
|
2045
2124
|
path: AGENT_IMAGE_API,
|
|
@@ -2863,6 +2942,113 @@ const taskResultSchema = {
|
|
|
2863
2942
|
};
|
|
2864
2943
|
/** Agent calls stay pending until the provider and history write settle. */
|
|
2865
2944
|
const AGENT_GENERATION_TIMEOUT_MS = 3e5;
|
|
2945
|
+
function ensureAgentImageConfigured(config) {
|
|
2946
|
+
if (!config.enabled) throw new ImageGenError("AI image generation is disabled. Open Settings > Plugins > AI Image and enable it.", "plugin-disabled");
|
|
2947
|
+
if (!config.allowAgentImageGeneration) throw new ImageGenError("Agent image generation is disabled in Settings > Plugins > AI Image.", "agent-generation-disabled");
|
|
2948
|
+
if (!config.channels.some((channel) => channel.apiUrl.trim() !== "" && channel.apiKey.trim() !== "")) throw new ImageGenError("Image API credentials are not configured. Open Settings > Plugins > AI Image, add a channel and fill in its API URL and API key.", "image-api-not-configured");
|
|
2949
|
+
}
|
|
2950
|
+
/** Resolve a configured image alias and its owning channel. */
|
|
2951
|
+
function resolveAgentImageModel(config, requested) {
|
|
2952
|
+
const entries = config.channels.flatMap((channel) => channel.models.map((model) => ({
|
|
2953
|
+
channel,
|
|
2954
|
+
alias: model.alias,
|
|
2955
|
+
upstream: model.id
|
|
2956
|
+
})));
|
|
2957
|
+
if (entries.length === 0) throw new ImageGenError("No image models are configured. Open Settings > Plugins > AI Image and add a channel with at least one model.", "no-models-configured");
|
|
2958
|
+
const wanted = typeof requested === "string" && requested.trim() !== "" ? requested.trim() : "";
|
|
2959
|
+
if (wanted === "") {
|
|
2960
|
+
if (entries.length === 1) return entries[0];
|
|
2961
|
+
throw new ImageGenError(`Multiple image models are available — ask the user which channel and model to use, then call this tool again with that exact model name. Options: ${config.channels.flatMap((channel) => channel.models.map((model) => `"${channel.name} · ${model.alias}"`)).join(", ")}.`, "model-choice-required");
|
|
2962
|
+
}
|
|
2963
|
+
const hosting = entries.filter((entry) => entry.alias === wanted);
|
|
2964
|
+
if (hosting.length === 0) throw new ImageGenError(`Image model "${wanted}" is not configured in any channel. Choose one of: ${[...new Set(entries.map((entry) => entry.alias))].join(", ")}.`, "image-model-not-configured");
|
|
2965
|
+
return hosting.find((entry) => entry.channel.id === config.defaultChannelId) ?? hosting[0];
|
|
2966
|
+
}
|
|
2967
|
+
function findAgentImageTask(runtime, id) {
|
|
2968
|
+
const task = runtime.queue.list().find((candidate) => candidate.id === id);
|
|
2969
|
+
if (task === void 0) throw new ImageGenError(`Image generation task ${id} was not found.`, "task-not-found");
|
|
2970
|
+
return task;
|
|
2971
|
+
}
|
|
2972
|
+
/** Wait for a queue task without sending anything through the chat model. */
|
|
2973
|
+
function waitForAgentImageTask(runtime, id, signal) {
|
|
2974
|
+
return new Promise((resolveTask, rejectTask) => {
|
|
2975
|
+
let settled = false;
|
|
2976
|
+
let dispose = () => {};
|
|
2977
|
+
let timer;
|
|
2978
|
+
let abort = () => {};
|
|
2979
|
+
const cleanup = () => {
|
|
2980
|
+
dispose();
|
|
2981
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2982
|
+
signal?.removeEventListener("abort", abort);
|
|
2983
|
+
};
|
|
2984
|
+
const resolve = (task) => {
|
|
2985
|
+
if (settled) return;
|
|
2986
|
+
settled = true;
|
|
2987
|
+
cleanup();
|
|
2988
|
+
resolveTask(task);
|
|
2989
|
+
};
|
|
2990
|
+
const reject = (error) => {
|
|
2991
|
+
if (settled) return;
|
|
2992
|
+
settled = true;
|
|
2993
|
+
cleanup();
|
|
2994
|
+
rejectTask(error);
|
|
2995
|
+
};
|
|
2996
|
+
abort = () => {
|
|
2997
|
+
if (settled) return;
|
|
2998
|
+
const reason = signal?.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Image generation was cancelled.");
|
|
2999
|
+
settled = true;
|
|
3000
|
+
cleanup();
|
|
3001
|
+
runtime.queue.cancel(id);
|
|
3002
|
+
rejectTask(reason);
|
|
3003
|
+
};
|
|
3004
|
+
const onChange = (updated) => {
|
|
3005
|
+
if (updated.id === id && isFinalTask(updated)) resolve(updated);
|
|
3006
|
+
};
|
|
3007
|
+
if (signal?.aborted === true) {
|
|
3008
|
+
abort();
|
|
3009
|
+
return;
|
|
3010
|
+
}
|
|
3011
|
+
dispose = runtime.queue.subscribe(onChange);
|
|
3012
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3013
|
+
timer = setTimeout(() => {
|
|
3014
|
+
if (settled) return;
|
|
3015
|
+
const timeout = new ImageGenError(`Image generation task ${id} timed out after ${AGENT_GENERATION_TIMEOUT_MS / 1e3} seconds.`, "generation-timeout");
|
|
3016
|
+
settled = true;
|
|
3017
|
+
cleanup();
|
|
3018
|
+
runtime.queue.cancel(id);
|
|
3019
|
+
rejectTask(timeout);
|
|
3020
|
+
}, AGENT_GENERATION_TIMEOUT_MS);
|
|
3021
|
+
let current;
|
|
3022
|
+
try {
|
|
3023
|
+
current = findAgentImageTask(runtime, id);
|
|
3024
|
+
} catch (error) {
|
|
3025
|
+
reject(error);
|
|
3026
|
+
return;
|
|
3027
|
+
}
|
|
3028
|
+
if (isFinalTask(current)) resolve(current);
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
/** Submit the same image-edit request used by the Agent tool. */
|
|
3032
|
+
async function submitAgentImageEdit(attachments, runtime, resolve, input) {
|
|
3033
|
+
const config = resolve();
|
|
3034
|
+
ensureAgentImageConfigured(config);
|
|
3035
|
+
const reference = await attachments.readImage(input.sourceImage, input.signal);
|
|
3036
|
+
const picked = resolveAgentImageModel(config, (config.channels.find((channel) => channel.id === config.defaultChannelId) ?? config.channels[0])?.models[0]?.alias ?? config.channels.flatMap((channel) => channel.models)[0]?.alias);
|
|
3037
|
+
return waitForAgentImageTask(runtime, runtime.queue.submit({
|
|
3038
|
+
mode: "edit",
|
|
3039
|
+
model: picked.alias,
|
|
3040
|
+
upstream: picked.upstream,
|
|
3041
|
+
channelId: picked.channel.id,
|
|
3042
|
+
channel: picked.channel.name,
|
|
3043
|
+
prompt: input.prompt.trim(),
|
|
3044
|
+
size: "auto",
|
|
3045
|
+
quality: "auto",
|
|
3046
|
+
n: 1,
|
|
3047
|
+
detail: "",
|
|
3048
|
+
image: imageDataUrl(reference),
|
|
3049
|
+
...reference.ref.name === void 0 ? {} : { refName: reference.ref.name }
|
|
3050
|
+
}).id, input.signal);
|
|
3051
|
+
}
|
|
2866
3052
|
function acceptedMediaType(value) {
|
|
2867
3053
|
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
2868
3054
|
}
|
|
@@ -2948,36 +3134,9 @@ function presentImageResult(_args, result) {
|
|
|
2948
3134
|
function registerAgentImageTools(ctx, runtime, resolve) {
|
|
2949
3135
|
const attachmentRefs = /* @__PURE__ */ new Map();
|
|
2950
3136
|
const ensureConfigured = () => {
|
|
2951
|
-
|
|
2952
|
-
if (!config.enabled) throw new ImageGenError("AI image generation is disabled. Open Settings > Plugins > AI Image and enable it.", "plugin-disabled");
|
|
2953
|
-
if (!config.allowAgentImageGeneration) throw new ImageGenError("Agent image generation is disabled in Settings > Plugins > AI Image.", "agent-generation-disabled");
|
|
2954
|
-
if (!config.channels.some((channel) => channel.apiUrl.trim() !== "" && channel.apiKey.trim() !== "")) throw new ImageGenError("Image API credentials are not configured. Open Settings > Plugins > AI Image, add a channel and fill in its API URL and API key.", "image-api-not-configured");
|
|
2955
|
-
};
|
|
2956
|
-
/**
|
|
2957
|
-
* Resolve the requested model alias onto a channel. Rules:
|
|
2958
|
-
* - a named alias must exist in some channel's catalog (several channels
|
|
2959
|
-
* may host it; the default channel wins);
|
|
2960
|
-
* - with no alias, a single configured model is used directly, while
|
|
2961
|
-
* multiple models require the Agent to ask the user first.
|
|
2962
|
-
* @returns the channel plus the alias and its upstream id.
|
|
2963
|
-
*/
|
|
2964
|
-
const resolveModel = (requested) => {
|
|
2965
|
-
const config = resolve();
|
|
2966
|
-
const entries = config.channels.flatMap((channel) => channel.models.map((model) => ({
|
|
2967
|
-
channel,
|
|
2968
|
-
alias: model.alias,
|
|
2969
|
-
upstream: model.id
|
|
2970
|
-
})));
|
|
2971
|
-
if (entries.length === 0) throw new ImageGenError("No image models are configured. Open Settings > Plugins > AI Image and add a channel with at least one model.", "no-models-configured");
|
|
2972
|
-
const wanted = typeof requested === "string" && requested.trim() !== "" ? requested.trim() : "";
|
|
2973
|
-
if (wanted === "") {
|
|
2974
|
-
if (entries.length === 1) return entries[0];
|
|
2975
|
-
throw new ImageGenError(`Multiple image models are available — ask the user which channel and model to use, then call this tool again with that exact model name. Options: ${config.channels.flatMap((channel) => channel.models.map((model) => `"${channel.name} · ${model.alias}"`)).join(", ")}.`, "model-choice-required");
|
|
2976
|
-
}
|
|
2977
|
-
const hosting = entries.filter((entry) => entry.alias === wanted);
|
|
2978
|
-
if (hosting.length === 0) throw new ImageGenError(`Image model "${wanted}" is not configured in any channel. Choose one of: ${[...new Set(entries.map((entry) => entry.alias))].join(", ")}.`, "image-model-not-configured");
|
|
2979
|
-
return hosting.find((entry) => entry.channel.id === config.defaultChannelId) ?? hosting[0];
|
|
3137
|
+
ensureAgentImageConfigured(resolve());
|
|
2980
3138
|
};
|
|
3139
|
+
const resolveModel = (requested) => resolveAgentImageModel(resolve(), requested);
|
|
2981
3140
|
const materializeTaskImages = (task) => {
|
|
2982
3141
|
if (task.status !== "completed") return Promise.resolve([]);
|
|
2983
3142
|
const existing = attachmentRefs.get(task.id);
|
|
@@ -2999,67 +3158,8 @@ function registerAgentImageTools(ctx, runtime, resolve) {
|
|
|
2999
3158
|
images
|
|
3000
3159
|
};
|
|
3001
3160
|
};
|
|
3002
|
-
const findTask = (id) =>
|
|
3003
|
-
|
|
3004
|
-
if (task === void 0) throw new ImageGenError(`Image generation task ${id} was not found.`, "task-not-found");
|
|
3005
|
-
return task;
|
|
3006
|
-
};
|
|
3007
|
-
const waitForTask = (id, signal) => new Promise((resolveTask, rejectTask) => {
|
|
3008
|
-
let settled = false;
|
|
3009
|
-
let dispose = () => {};
|
|
3010
|
-
let timer;
|
|
3011
|
-
let abort = () => {};
|
|
3012
|
-
const cleanup = () => {
|
|
3013
|
-
dispose();
|
|
3014
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3015
|
-
signal?.removeEventListener("abort", abort);
|
|
3016
|
-
};
|
|
3017
|
-
const resolve = (task) => {
|
|
3018
|
-
if (settled) return;
|
|
3019
|
-
settled = true;
|
|
3020
|
-
cleanup();
|
|
3021
|
-
resolveTask(task);
|
|
3022
|
-
};
|
|
3023
|
-
const reject = (error) => {
|
|
3024
|
-
if (settled) return;
|
|
3025
|
-
settled = true;
|
|
3026
|
-
cleanup();
|
|
3027
|
-
rejectTask(error);
|
|
3028
|
-
};
|
|
3029
|
-
abort = () => {
|
|
3030
|
-
if (settled) return;
|
|
3031
|
-
const reason = signal?.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Image generation was cancelled.");
|
|
3032
|
-
settled = true;
|
|
3033
|
-
cleanup();
|
|
3034
|
-
runtime.queue.cancel(id);
|
|
3035
|
-
rejectTask(reason);
|
|
3036
|
-
};
|
|
3037
|
-
const onChange = (updated) => {
|
|
3038
|
-
if (updated.id === id && isFinalTask(updated)) resolve(updated);
|
|
3039
|
-
};
|
|
3040
|
-
if (signal?.aborted === true) {
|
|
3041
|
-
abort();
|
|
3042
|
-
return;
|
|
3043
|
-
}
|
|
3044
|
-
dispose = runtime.queue.subscribe(onChange);
|
|
3045
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
3046
|
-
timer = setTimeout(() => {
|
|
3047
|
-
if (settled) return;
|
|
3048
|
-
const timeout = new ImageGenError(`Image generation task ${id} timed out after ${AGENT_GENERATION_TIMEOUT_MS / 1e3} seconds.`, "generation-timeout");
|
|
3049
|
-
settled = true;
|
|
3050
|
-
cleanup();
|
|
3051
|
-
runtime.queue.cancel(id);
|
|
3052
|
-
rejectTask(timeout);
|
|
3053
|
-
}, AGENT_GENERATION_TIMEOUT_MS);
|
|
3054
|
-
let current;
|
|
3055
|
-
try {
|
|
3056
|
-
current = findTask(id);
|
|
3057
|
-
} catch (error) {
|
|
3058
|
-
reject(error);
|
|
3059
|
-
return;
|
|
3060
|
-
}
|
|
3061
|
-
if (isFinalTask(current)) resolve(current);
|
|
3062
|
-
});
|
|
3161
|
+
const findTask = (id) => findAgentImageTask(runtime, id);
|
|
3162
|
+
const waitForTask = (id, signal) => waitForAgentImageTask(runtime, id, signal);
|
|
3063
3163
|
const disposers = [
|
|
3064
3164
|
ctx.tools.register(defineTool({
|
|
3065
3165
|
name: "generate_image",
|
|
@@ -3244,11 +3344,107 @@ function toSaveImage(image, taskId, index) {
|
|
|
3244
3344
|
};
|
|
3245
3345
|
}
|
|
3246
3346
|
//#endregion
|
|
3347
|
+
//#region src/edit-image-command.ts
|
|
3348
|
+
function isImageReference(value) {
|
|
3349
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
3350
|
+
const ref = value;
|
|
3351
|
+
return typeof ref.attachmentId === "string" && (ref.mediaType === "image/png" || ref.mediaType === "image/jpeg" || ref.mediaType === "image/webp" || ref.mediaType === "image/gif") && Number.isInteger(ref.bytes) && ref.bytes > 0 && Number.isInteger(ref.width) && ref.width > 0 && Number.isInteger(ref.height) && ref.height > 0;
|
|
3352
|
+
}
|
|
3353
|
+
function imageInContent(value) {
|
|
3354
|
+
if (!Array.isArray(value)) return void 0;
|
|
3355
|
+
for (let index = value.length - 1; index >= 0; index -= 1) {
|
|
3356
|
+
const block = value[index];
|
|
3357
|
+
if (typeof block !== "object" || block === null || Array.isArray(block)) continue;
|
|
3358
|
+
const raw = block;
|
|
3359
|
+
if (raw.type === "image" && isImageReference(raw.attachment)) return raw.attachment;
|
|
3360
|
+
if (raw.type === "tool-result") {
|
|
3361
|
+
const nested = imageInContent(raw.content);
|
|
3362
|
+
if (nested !== void 0) return nested;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
/** Pick the newest image explicitly attached to this command invocation. */
|
|
3367
|
+
function imageInInvocation(value) {
|
|
3368
|
+
if (!Array.isArray(value)) return void 0;
|
|
3369
|
+
for (let index = value.length - 1; index >= 0; index -= 1) {
|
|
3370
|
+
const block = value[index];
|
|
3371
|
+
if (typeof block !== "object" || block === null || Array.isArray(block)) continue;
|
|
3372
|
+
const raw = block;
|
|
3373
|
+
if (raw.type === "image" && isImageReference(raw.attachment)) return raw.attachment;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
/** Find the newest durable image reference, including nested tool results. */
|
|
3377
|
+
function latestSessionImage(messages) {
|
|
3378
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
3379
|
+
const message = messages[index];
|
|
3380
|
+
if (typeof message !== "object" || message === null || Array.isArray(message)) continue;
|
|
3381
|
+
const image = imageInContent(message.content);
|
|
3382
|
+
if (image !== void 0) return image;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
function commandError(error) {
|
|
3386
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
3387
|
+
return {
|
|
3388
|
+
kind: "error",
|
|
3389
|
+
text: text.trim() === "" ? "图片编辑失败。" : text
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
/** Register the host-side command; it never sends the command line to a chat model. */
|
|
3393
|
+
function registerEditImageCommand(ctx, runtime, resolve, pendingImages) {
|
|
3394
|
+
return ctx.commands.register({
|
|
3395
|
+
name: "edit_image",
|
|
3396
|
+
description: "Edit the latest image in this conversation with the plugin image model",
|
|
3397
|
+
input: {
|
|
3398
|
+
hint: "Describe how to modify the latest image",
|
|
3399
|
+
images: true
|
|
3400
|
+
},
|
|
3401
|
+
async handler(invocation) {
|
|
3402
|
+
const prompt = invocation.rawInput.trim();
|
|
3403
|
+
if (prompt === "") return {
|
|
3404
|
+
kind: "error",
|
|
3405
|
+
text: "请提供图片修改描述,例如:/edit_image 把背景改成夜景"
|
|
3406
|
+
};
|
|
3407
|
+
const invocationImage = imageInInvocation(invocation.attachments);
|
|
3408
|
+
const pendingImage = pendingImages?.get(String(invocation.agent.id));
|
|
3409
|
+
const durableImage = latestSessionImage(invocation.agent.session.deriveMessages());
|
|
3410
|
+
const sourceImage = invocationImage ?? pendingImage ?? durableImage;
|
|
3411
|
+
if (sourceImage === void 0) return {
|
|
3412
|
+
kind: "error",
|
|
3413
|
+
text: "当前对话没有可用图片,请先上传图片或把画廊图片加入对话。"
|
|
3414
|
+
};
|
|
3415
|
+
try {
|
|
3416
|
+
const task = await submitAgentImageEdit(ctx.attachments, runtime, resolve, {
|
|
3417
|
+
prompt,
|
|
3418
|
+
sourceImage,
|
|
3419
|
+
signal: invocation.signal
|
|
3420
|
+
});
|
|
3421
|
+
if (task.status === "completed") {
|
|
3422
|
+
if (pendingImage !== void 0) pendingImages?.consume(String(invocation.agent.id), pendingImage);
|
|
3423
|
+
return {
|
|
3424
|
+
kind: "success",
|
|
3425
|
+
text: "图片编辑已完成,可在 AI 生图面板查看结果。"
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
return {
|
|
3429
|
+
kind: "error",
|
|
3430
|
+
text: task.error ?? `图片编辑${task.status === "cancelled" ? "已取消" : "失败"}。`
|
|
3431
|
+
};
|
|
3432
|
+
} catch (error) {
|
|
3433
|
+
return commandError(error);
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
});
|
|
3437
|
+
}
|
|
3438
|
+
//#endregion
|
|
3247
3439
|
//#region src/index.ts
|
|
3248
3440
|
/** Stable cordis plugin name. */
|
|
3249
3441
|
const name = "imagegen";
|
|
3250
3442
|
/** Services required before the surfaces can mount. */
|
|
3251
|
-
const inject = [
|
|
3443
|
+
const inject = [
|
|
3444
|
+
"webServer",
|
|
3445
|
+
"systemPrompt",
|
|
3446
|
+
"commands"
|
|
3447
|
+
];
|
|
3252
3448
|
/** The branded settings namespace of this plugin (the card edits it). */
|
|
3253
3449
|
const ImageGenSettingsNamespace = settingsNamespace(IMAGEGEN_SETTINGS_NAMESPACE);
|
|
3254
3450
|
const Config = z.object({
|
|
@@ -3281,7 +3477,7 @@ const DEFAULT_ALLOW_AGENT_IMAGE_GENERATION = true;
|
|
|
3281
3477
|
/** Order of the announcement section within the tool-guidance band. */
|
|
3282
3478
|
const SECTION_ORDER = 150;
|
|
3283
3479
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
3284
|
-
const IMAGEGEN_GUIDANCE = "本机已安装 dsh-imagegen 插件(DSH AI 生图):侧边栏「AI 生图」入口。能力:通过「渠道」对接 OpenAI 兼容图像生成 API(每个渠道 = 一个 API 端点 + 各自的模型目录),支持文生图(/images/generations)与图生图(/images/edits,上传参考图,grok-imagine 模型按官方 JSON image_url 协议发送,nanobanana 系列按 aspect_ratio / image_size 参数协议发送;seedream 系列统一走 /images/generations,参考图以 JSON image 数组发送;智谱 `glm-image` 使用官方 `/api/paas/v4/images/generations`,当前仅支持文生图)。API 地址与密钥在 GUI 设置中按渠道配置,密钥仅存于本机设置文档;生成请求由本地宿主代理转发,结果以 base64 返回面板,可预览与下载。模型只能使用用户在各渠道配置目录中的模型;检测模型时会过滤聊天、Embedding 等非图片模型,但模型出现在 /models 中仍不等于其网关原生支持生图协议,遇到 Qwen、Gemini 等非 OpenAI 生图协议时应如实说明上游兼容性。可一键把满意的图片加入「画廊」。内置「提示词模板库」(面板提示词框左下角「模板库」按钮):打包 awesome-gpt-image-2 的数百条提示词案例,可搜索、筛选与复用。Agent 可直接调用 `generate_image` 提交文生图,也可用 `edit_image`
|
|
3480
|
+
const IMAGEGEN_GUIDANCE = "本机已安装 dsh-imagegen 插件(DSH AI 生图):侧边栏「AI 生图」入口。能力:通过「渠道」对接 OpenAI 兼容图像生成 API(每个渠道 = 一个 API 端点 + 各自的模型目录),支持文生图(/images/generations)与图生图(/images/edits,上传参考图,grok-imagine 模型按官方 JSON image_url 协议发送,nanobanana 系列按 aspect_ratio / image_size 参数协议发送;seedream 系列统一走 /images/generations,参考图以 JSON image 数组发送;智谱 `glm-image` 使用官方 `/api/paas/v4/images/generations`,当前仅支持文生图)。API 地址与密钥在 GUI 设置中按渠道配置,密钥仅存于本机设置文档;生成请求由本地宿主代理转发,结果以 base64 返回面板,可预览与下载。模型只能使用用户在各渠道配置目录中的模型;检测模型时会过滤聊天、Embedding 等非图片模型,但模型出现在 /models 中仍不等于其网关原生支持生图协议,遇到 Qwen、Gemini 等非 OpenAI 生图协议时应如实说明上游兼容性。可一键把满意的图片加入「画廊」。内置「提示词模板库」(面板提示词框左下角「模板库」按钮):打包 awesome-gpt-image-2 的数百条提示词案例,可搜索、筛选与复用。Agent 可直接调用 `generate_image` 提交文生图,也可用 `edit_image` 图生图;默认保持工具调用等待直到任务完成,完成图片显示在工具调用对应的左侧结果区域,模型收到状态和附件引用,不会额外伪造用户消息。用户也可以使用 `/edit_image <修改描述>`,命令会直接读取当前对话最近图片并调用插件图片模型,不经过对话模型的图片能力检查。若明确需要后台执行,可传 `wait_for_completion: false`,之后再用 `get_image_generation_task` 查询;不要反复轮询。限制:生成消耗上游 API 额度;图片内容由上游模型生成,可能不符合预期或包含不适宜内容;api_key 以明文存储在设置文档中;参考图会发送至所配置的 API 服务;模板库在线刷新与参考图首次加载需要访问 vibeui.top。用户提到「生图 / 绘画 / 生成图片 / 文生图 / 图生图 / 画廊 / 提示词模板」时即指本插件,请据此协作。";
|
|
3285
3481
|
/** Append the live channel × model table so an Agent can honor user choices. */
|
|
3286
3482
|
function guidanceFor(channels, defaultChannelId) {
|
|
3287
3483
|
if (channels.length === 0) return `${IMAGEGEN_GUIDANCE} 尚未配置任何渠道:请先在「设置 → 插件 → AI 生图」添加渠道并填写 API 地址与密钥。`;
|
|
@@ -3381,6 +3577,7 @@ function apply(ctx, config) {
|
|
|
3381
3577
|
};
|
|
3382
3578
|
};
|
|
3383
3579
|
const runtime = new ImageGenerationRuntime(channelsView);
|
|
3580
|
+
const pendingConversationImages = /* @__PURE__ */ new Map();
|
|
3384
3581
|
ctx.inject(["settings", "attachments"], (sctx) => {
|
|
3385
3582
|
const seam = sctx.get("settings");
|
|
3386
3583
|
sctx.effect(() => {
|
|
@@ -3409,6 +3606,7 @@ function apply(ctx, config) {
|
|
|
3409
3606
|
return [...new Set(value.channels.flatMap((channel) => channel.models.map((model) => model.alias)))];
|
|
3410
3607
|
},
|
|
3411
3608
|
attachments: sctx.attachments,
|
|
3609
|
+
pendingConversationImages,
|
|
3412
3610
|
runtime
|
|
3413
3611
|
}).map((route) => ctx.webServer.register(route));
|
|
3414
3612
|
return () => {
|
|
@@ -3416,16 +3614,33 @@ function apply(ctx, config) {
|
|
|
3416
3614
|
};
|
|
3417
3615
|
}, "dsh-imagegen: routes");
|
|
3418
3616
|
});
|
|
3419
|
-
ctx.inject([
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3617
|
+
ctx.inject([
|
|
3618
|
+
"tools",
|
|
3619
|
+
"attachments",
|
|
3620
|
+
"commands"
|
|
3621
|
+
], (tctx) => {
|
|
3622
|
+
tctx.effect(() => {
|
|
3623
|
+
const resolveAgentConfig = () => {
|
|
3624
|
+
const value = resolve();
|
|
3625
|
+
return {
|
|
3626
|
+
enabled: value.enabled,
|
|
3627
|
+
allowAgentImageGeneration: value.allowAgentImageGeneration,
|
|
3628
|
+
channels: value.channels,
|
|
3629
|
+
defaultChannelId: value.defaultChannelId
|
|
3630
|
+
};
|
|
3631
|
+
};
|
|
3632
|
+
const disposeTools = registerAgentImageTools(tctx, runtime, resolveAgentConfig);
|
|
3633
|
+
const disposeCommand = registerEditImageCommand(tctx, runtime, resolveAgentConfig, {
|
|
3634
|
+
get: (sessionId) => pendingConversationImages.get(sessionId),
|
|
3635
|
+
consume: (sessionId, ref) => {
|
|
3636
|
+
if (pendingConversationImages.get(sessionId)?.attachmentId === ref.attachmentId) pendingConversationImages.delete(sessionId);
|
|
3637
|
+
}
|
|
3638
|
+
});
|
|
3639
|
+
return () => {
|
|
3640
|
+
disposeCommand();
|
|
3641
|
+
disposeTools();
|
|
3427
3642
|
};
|
|
3428
|
-
}
|
|
3643
|
+
}, "dsh-imagegen: agent image tools and commands");
|
|
3429
3644
|
});
|
|
3430
3645
|
let disposeSection;
|
|
3431
3646
|
const sync = () => {
|
|
@@ -3451,4 +3666,4 @@ function apply(ctx, config) {
|
|
|
3451
3666
|
sync();
|
|
3452
3667
|
}
|
|
3453
3668
|
//#endregion
|
|
3454
|
-
export { CURRENT_VERSION, Config, IMAGEGEN_GUIDANCE, ImageGenError, ImageGenSettingsNamespace, ImageGenerationRuntime, appendGallery, apply, checkForUpdate, clearGallery, clearTemplateMemo, clearUpdateCache, compareVersions, generateImage, inject, installUpdate, listGallery, listTemplates, makeRoutes, name, profileFromProcess, readGalleryImage, readTemplateImage, refreshTemplates, registerAgentImageTools, removeGallery, updateGalleryTags };
|
|
3669
|
+
export { CURRENT_VERSION, Config, IMAGEGEN_GUIDANCE, ImageGenError, ImageGenSettingsNamespace, ImageGenerationRuntime, appendGallery, apply, checkForUpdate, clearGallery, clearTemplateMemo, clearUpdateCache, compareVersions, generateImage, inject, installUpdate, latestSessionImage, listGallery, listTemplates, makeRoutes, name, profileFromProcess, readGalleryImage, readTemplateImage, refreshTemplates, registerAgentImageTools, registerEditImageCommand, removeGallery, updateGalleryTags };
|