dsh-image-gen 0.9.3 → 0.10.1
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/client.js +965 -405
- package/lib/index.js +306 -67
- package/lib/types/client/conversation-image-revisions.d.ts +1 -0
- package/lib/types/client/conversation-regenerate.d.ts +1 -0
- package/lib/types/client/gallery-store.d.ts +1 -0
- package/lib/types/client/image-result-node.d.ts +1 -0
- package/lib/types/client/index.d.ts +6 -1
- package/lib/types/client/multi-model-compare.d.ts +3 -3
- package/lib/types/client/provider-pill.d.ts +9 -3
- package/lib/types/client/studio-view.d.ts +1 -0
- package/lib/types/client/tl/tl-trace-bridge.d.ts +55 -0
- package/lib/types/client/tl/trace-playback.d.ts +101 -0
- package/lib/types/config.d.ts +11 -2
- package/lib/types/credentials.d.ts +2 -2
- package/lib/types/generation-timeout.d.ts +5 -0
- package/lib/types/image-tool-context.d.ts +1 -1
- package/lib/types/shared.d.ts +53 -0
- package/lib/types/studio-route.d.ts +1 -0
- package/lib/types/studio.d.ts +1 -1
- package/lib/types/test-route.d.ts +3 -3
- package/lib/types/trace-drawing.d.ts +102 -0
- package/lib/types/trace-route.d.ts +12 -0
- package/lib/types/trace-sketch.d.ts +26 -0
- package/lib/types/trace-store.d.ts +23 -0
- package/lib/types/vendor/drawai/raster.d.ts +43 -0
- package/lib/types/vendor/drawai/trace-image.d.ts +257 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -5764,12 +5764,80 @@ const CLOUD_IMAGE_PROVIDERS = [
|
|
|
5764
5764
|
"xai",
|
|
5765
5765
|
"zhipu"
|
|
5766
5766
|
];
|
|
5767
|
+
/** Normalize the relay model list while keeping the legacy default model selectable. */
|
|
5768
|
+
function resolveOpenAICompatModels(config) {
|
|
5769
|
+
const defaultModel = config.openaiCompatModel?.trim() ?? "";
|
|
5770
|
+
const models = [...new Set((config.openaiCompatModels ?? []).map((model) => model.trim()).filter(Boolean))];
|
|
5771
|
+
if (defaultModel.length > 0 && !models.includes(defaultModel)) models.unshift(defaultModel);
|
|
5772
|
+
return models;
|
|
5773
|
+
}
|
|
5774
|
+
const DEFAULT_OPENAI_COMPAT_CHANNEL_ID = "default";
|
|
5775
|
+
function openAICompatKeyRef(channelId) {
|
|
5776
|
+
return channelId === "default" ? OPENAI_COMPAT_API_KEY_ENV : `${OPENAI_COMPAT_API_KEY_ENV}_${channelId.replace(/[^A-Za-z0-9]/g, "_").toUpperCase()}`;
|
|
5777
|
+
}
|
|
5778
|
+
/** The legacy flat fields remain the first channel; additional entries are fully independent. */
|
|
5779
|
+
function resolveOpenAICompatChannels(source) {
|
|
5780
|
+
const legacy = {
|
|
5781
|
+
id: DEFAULT_OPENAI_COMPAT_CHANNEL_ID,
|
|
5782
|
+
label: source.openaiCompatLabel?.trim() || "OpenAI 兼容",
|
|
5783
|
+
keyRef: OPENAI_COMPAT_API_KEY_ENV,
|
|
5784
|
+
baseURL: source.openaiCompatBaseURL ?? "",
|
|
5785
|
+
model: source.openaiCompatModel ?? "",
|
|
5786
|
+
models: [...source.openaiCompatModels ?? []],
|
|
5787
|
+
editModel: source.openaiCompatEditModel ?? "",
|
|
5788
|
+
quality: source.openaiCompatQuality ?? "",
|
|
5789
|
+
qualityOptions: [...source.openaiCompatQualityOptions ?? []],
|
|
5790
|
+
extraBody: source.openaiCompatExtraBody ?? {},
|
|
5791
|
+
editSizeMode: source.openaiCompatEditSizeMode ?? "auto",
|
|
5792
|
+
editFormat: source.openaiCompatEditFormat ?? "multipart",
|
|
5793
|
+
editExtra: source.openaiCompatEditExtra ?? {},
|
|
5794
|
+
sizes: source.openaiCompatSizes ?? {}
|
|
5795
|
+
};
|
|
5796
|
+
const channels = [legacy];
|
|
5797
|
+
const seen = /* @__PURE__ */ new Set([legacy.id]);
|
|
5798
|
+
for (const [index, entry] of (source.openaiCompatChannels ?? []).entries()) {
|
|
5799
|
+
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
|
|
5800
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(id) || seen.has(id)) continue;
|
|
5801
|
+
seen.add(id);
|
|
5802
|
+
const storedKeyRef = typeof entry.keyRef === "string" ? entry.keyRef.trim() : "";
|
|
5803
|
+
channels.push({
|
|
5804
|
+
id,
|
|
5805
|
+
label: typeof entry.label === "string" && entry.label.trim().length > 0 ? entry.label.trim() : `OpenAI 兼容 ${index + 2}`,
|
|
5806
|
+
keyRef: /^DSH_IMAGE_GEN_OPENAI_COMPAT_KEY_[A-Z0-9_]+$/.test(storedKeyRef) ? storedKeyRef : openAICompatKeyRef(id),
|
|
5807
|
+
baseURL: typeof entry.baseURL === "string" ? entry.baseURL : "",
|
|
5808
|
+
model: typeof entry.model === "string" ? entry.model : "",
|
|
5809
|
+
models: Array.isArray(entry.models) ? entry.models.filter((model) => typeof model === "string") : [],
|
|
5810
|
+
editModel: typeof entry.editModel === "string" ? entry.editModel : "",
|
|
5811
|
+
quality: typeof entry.quality === "string" ? entry.quality : "",
|
|
5812
|
+
qualityOptions: Array.isArray(entry.qualityOptions) ? entry.qualityOptions.filter((quality) => typeof quality === "string") : [],
|
|
5813
|
+
extraBody: entry.extraBody ?? {},
|
|
5814
|
+
editSizeMode: entry.editSizeMode === "selected" ? "selected" : "auto",
|
|
5815
|
+
editFormat: entry.editFormat ?? "multipart",
|
|
5816
|
+
editExtra: entry.editExtra ?? {},
|
|
5817
|
+
sizes: entry.sizes ?? {}
|
|
5818
|
+
});
|
|
5819
|
+
}
|
|
5820
|
+
return channels;
|
|
5821
|
+
}
|
|
5822
|
+
function resolveOpenAICompatChannel(source, channelId) {
|
|
5823
|
+
const channels = resolveOpenAICompatChannels(source);
|
|
5824
|
+
const explicit = channelId?.trim() ?? "";
|
|
5825
|
+
if (explicit.length > 0) {
|
|
5826
|
+
const selected = channels.find((channel) => channel.id === explicit);
|
|
5827
|
+
if (selected === void 0) throw new Error(`Unknown OpenAI-compatible channel "${explicit}". Available channels: ${channels.map((channel) => channel.id).join(", ")}.`);
|
|
5828
|
+
return selected;
|
|
5829
|
+
}
|
|
5830
|
+
const active = source.openaiCompatActiveChannel?.trim() ?? "";
|
|
5831
|
+
return channels.find((channel) => channel.id === active) ?? channels[0];
|
|
5832
|
+
}
|
|
5767
5833
|
/**
|
|
5768
5834
|
* Providers the browser workbench can drive: the BYOK cloud set plus the
|
|
5769
5835
|
* logged-in subscription channels. ComfyUI stays out; it has its own workflow
|
|
5770
5836
|
* pipeline and no shared request shape.
|
|
5771
5837
|
*/
|
|
5772
5838
|
const STUDIO_PROVIDERS = [...CLOUD_IMAGE_PROVIDERS, ...SUBSCRIPTION_PROVIDERS];
|
|
5839
|
+
/** Default timeout for API-key cloud generation in the browser workbench. */
|
|
5840
|
+
const DEFAULT_CLOUD_TIMEOUT_MS = 15e4;
|
|
5773
5841
|
/** Timeout for one subscription image call, shared by the tool path and the
|
|
5774
5842
|
* studio route so neither drifts from the other. */
|
|
5775
5843
|
const SUBSCRIPTION_TIMEOUT_MS = 3e5;
|
|
@@ -5831,9 +5899,15 @@ const STUDIO_WATERMARK_VALUES = [
|
|
|
5831
5899
|
"off",
|
|
5832
5900
|
"on"
|
|
5833
5901
|
];
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5902
|
+
function studioModelOptionsForMode(profile, mode) {
|
|
5903
|
+
if (mode === "edit" && profile.editModel !== void 0) return [{
|
|
5904
|
+
value: profile.editModel,
|
|
5905
|
+
label: profile.editModel
|
|
5906
|
+
}];
|
|
5907
|
+
return profile.modelOptions ?? [{
|
|
5908
|
+
value: profile.model,
|
|
5909
|
+
label: profile.model
|
|
5910
|
+
}];
|
|
5837
5911
|
}
|
|
5838
5912
|
function studioWatermarkCapability(profile, mode) {
|
|
5839
5913
|
return profile?.watermarkCapabilities?.[mode] ?? (mode === "generate" && profile?.watermarkOptions?.length ? { status: "supported" } : {
|
|
@@ -5961,14 +6035,39 @@ function volatile(schema) {
|
|
|
5961
6035
|
*/
|
|
5962
6036
|
const Config = z.object({
|
|
5963
6037
|
provider: volatile(z.union(IMAGE_PROVIDERS).default("google")),
|
|
6038
|
+
cloudTimeoutMs: volatile(z.number().min(5e3).max(36e5).default(DEFAULT_CLOUD_TIMEOUT_MS)),
|
|
5964
6039
|
googleModel: volatile(z.string().default(DEFAULT_GOOGLE_MODEL)),
|
|
5965
6040
|
googleEndpoint: volatile(z.string().default(DEFAULT_GOOGLE_ENDPOINT)),
|
|
5966
6041
|
googleNative: volatile(z.boolean().default(false)),
|
|
5967
6042
|
openaiBaseURL: volatile(z.string().default(DEFAULT_OPENAI_BASE_URL)),
|
|
5968
6043
|
openaiModel: volatile(z.string().default(DEFAULT_OPENAI_MODEL)),
|
|
5969
6044
|
openaiSizes: volatile(z.dict(z.dict(z.string())).default({})),
|
|
6045
|
+
openaiCompatLabel: volatile(z.string().default("")),
|
|
5970
6046
|
openaiCompatBaseURL: volatile(z.string().default("")),
|
|
5971
6047
|
openaiCompatModel: volatile(z.string().default("")),
|
|
6048
|
+
openaiCompatModels: volatile(z.array(z.string()).default([])),
|
|
6049
|
+
openaiCompatChannels: volatile(z.array(z.object({
|
|
6050
|
+
id: z.string(),
|
|
6051
|
+
label: z.string().default(""),
|
|
6052
|
+
keyRef: z.string(),
|
|
6053
|
+
baseURL: z.string().default(""),
|
|
6054
|
+
model: z.string().default(""),
|
|
6055
|
+
models: z.array(z.string()).default([]),
|
|
6056
|
+
editModel: z.string().default(""),
|
|
6057
|
+
quality: z.string().default(""),
|
|
6058
|
+
qualityOptions: z.array(z.string()).default([]),
|
|
6059
|
+
extraBody: z.dict(z.any()).default({}),
|
|
6060
|
+
editSizeMode: z.union(["auto", "selected"]).default("auto"),
|
|
6061
|
+
editFormat: z.union([
|
|
6062
|
+
z.const("multipart"),
|
|
6063
|
+
z.const("jsonImageUrlArray"),
|
|
6064
|
+
z.const("formReferenceImages"),
|
|
6065
|
+
z.const("siliconflowJson")
|
|
6066
|
+
]).default("multipart"),
|
|
6067
|
+
editExtra: z.dict(z.any()).default({}),
|
|
6068
|
+
sizes: z.dict(z.dict(z.string())).default({})
|
|
6069
|
+
})).default([])),
|
|
6070
|
+
openaiCompatActiveChannel: volatile(z.string().default("")),
|
|
5972
6071
|
openaiCompatEditModel: volatile(z.string().default("")),
|
|
5973
6072
|
openaiCompatQuality: volatile(z.string().default("")),
|
|
5974
6073
|
openaiCompatQualityOptions: volatile(z.array(z.string()).default([
|
|
@@ -6107,6 +6206,30 @@ function resolveProvider(config) {
|
|
|
6107
6206
|
};
|
|
6108
6207
|
}
|
|
6109
6208
|
}
|
|
6209
|
+
/** Project one named relay channel onto the legacy flat fields used by all provider adapters. */
|
|
6210
|
+
function withOpenAICompatChannel(config, channelId) {
|
|
6211
|
+
const channel = resolveOpenAICompatChannel(config, channelId);
|
|
6212
|
+
if (channel.id === "default") return {
|
|
6213
|
+
...config,
|
|
6214
|
+
openaiCompatActiveChannel: "default"
|
|
6215
|
+
};
|
|
6216
|
+
return {
|
|
6217
|
+
...config,
|
|
6218
|
+
openaiCompatLabel: channel.label,
|
|
6219
|
+
openaiCompatBaseURL: channel.baseURL,
|
|
6220
|
+
openaiCompatModel: channel.model,
|
|
6221
|
+
openaiCompatModels: channel.models ?? [],
|
|
6222
|
+
openaiCompatEditModel: channel.editModel ?? "",
|
|
6223
|
+
openaiCompatQuality: channel.quality ?? "",
|
|
6224
|
+
openaiCompatQualityOptions: channel.qualityOptions ?? [],
|
|
6225
|
+
openaiCompatExtraBody: channel.extraBody ?? {},
|
|
6226
|
+
openaiCompatEditSizeMode: channel.editSizeMode ?? "auto",
|
|
6227
|
+
openaiCompatEditFormat: channel.editFormat ?? "multipart",
|
|
6228
|
+
openaiCompatEditExtra: channel.editExtra ?? {},
|
|
6229
|
+
openaiCompatSizes: channel.sizes ?? {},
|
|
6230
|
+
openaiCompatActiveChannel: channel.id
|
|
6231
|
+
};
|
|
6232
|
+
}
|
|
6110
6233
|
/**
|
|
6111
6234
|
* Apply a per-call provider and/or model override on top of the saved config.
|
|
6112
6235
|
* `model` is ignored for ComfyUI, whose per-call equivalent is `workflow`.
|
|
@@ -6170,6 +6293,7 @@ function migrateOpenAICompatConfig(config) {
|
|
|
6170
6293
|
...config,
|
|
6171
6294
|
openaiCompatBaseURL: legacyBase,
|
|
6172
6295
|
...compatModel.length > 0 ? {} : { openaiCompatModel: config.openaiModel },
|
|
6296
|
+
...(config.openaiCompatModels?.length ?? 0) > 0 ? {} : { openaiCompatModels: [compatModel || config.openaiModel?.trim() || ""].filter(Boolean) },
|
|
6173
6297
|
openaiBaseURL: DEFAULT_OPENAI_BASE_URL,
|
|
6174
6298
|
...config.provider === "openai" ? { provider: "openai-compat" } : {}
|
|
6175
6299
|
};
|
|
@@ -6192,6 +6316,31 @@ function selectComfyUIWorkflow(active, requested) {
|
|
|
6192
6316
|
return workflow;
|
|
6193
6317
|
}
|
|
6194
6318
|
//#endregion
|
|
6319
|
+
//#region lib/types/generation-timeout.js
|
|
6320
|
+
const UPSTREAM_TIMEOUT_HINT = "上游可能已扣费或仍在生成,请到服务商后台确认";
|
|
6321
|
+
var GenerationTimeoutError = class extends Error {
|
|
6322
|
+
constructor(timeoutMs) {
|
|
6323
|
+
super(`生图超时(${String(timeoutMs / 1e3)} 秒未返回),请检查网络或代理后重试;${UPSTREAM_TIMEOUT_HINT}`);
|
|
6324
|
+
this.name = "GenerationTimeoutError";
|
|
6325
|
+
}
|
|
6326
|
+
};
|
|
6327
|
+
async function withGenerationTimeout(timeoutMs, signal, operation) {
|
|
6328
|
+
const watchdog = new AbortController();
|
|
6329
|
+
const combinedSignal = AbortSignal.any([signal, watchdog.signal]);
|
|
6330
|
+
const timeout = setTimeout(() => {
|
|
6331
|
+
watchdog.abort();
|
|
6332
|
+
}, timeoutMs);
|
|
6333
|
+
timeout.unref?.();
|
|
6334
|
+
try {
|
|
6335
|
+
return await operation(combinedSignal);
|
|
6336
|
+
} catch (error) {
|
|
6337
|
+
if (watchdog.signal.aborted && !signal.aborted) throw new GenerationTimeoutError(timeoutMs);
|
|
6338
|
+
throw error;
|
|
6339
|
+
} finally {
|
|
6340
|
+
clearTimeout(timeout);
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
//#endregion
|
|
6195
6344
|
//#region node_modules/.pnpm/@deepseek-ai+dsh-credential_98fa173416f048996704a605a96a2b0e/node_modules/@deepseek-ai/dsh-credentials/lib/index.js
|
|
6196
6345
|
/**
|
|
6197
6346
|
* Service Definition for the credential-reference capability seam (`ctx.credentials`). Settings and composition files carry
|
|
@@ -6232,8 +6381,8 @@ function isCredentialRefName(value) {
|
|
|
6232
6381
|
* never count as configured: the host treats an empty stored value as absent,
|
|
6233
6382
|
* and a whitespace-only key is never sent to a provider.
|
|
6234
6383
|
*/
|
|
6235
|
-
async function resolveApiKey(ctx, provider) {
|
|
6236
|
-
const value = (await ctx.credentials.resolve(credentialRef(CLOUD_CREDENTIAL_REFS[provider])))?.value.trim() ?? "";
|
|
6384
|
+
async function resolveApiKey(ctx, provider, keyRef) {
|
|
6385
|
+
const value = (await ctx.credentials.resolve(credentialRef(keyRef ?? CLOUD_CREDENTIAL_REFS[provider])))?.value.trim() ?? "";
|
|
6237
6386
|
return value.length > 0 ? value : void 0;
|
|
6238
6387
|
}
|
|
6239
6388
|
/**
|
|
@@ -6242,8 +6391,8 @@ async function resolveApiKey(ctx, provider) {
|
|
|
6242
6391
|
* wording; without it the message is the browser-facing Chinese one used by
|
|
6243
6392
|
* the Studio route.
|
|
6244
6393
|
*/
|
|
6245
|
-
async function requireApiKey(ctx, provider, tool) {
|
|
6246
|
-
const value = await resolveApiKey(ctx, provider);
|
|
6394
|
+
async function requireApiKey(ctx, provider, tool, keyRef) {
|
|
6395
|
+
const value = await resolveApiKey(ctx, provider, keyRef);
|
|
6247
6396
|
if (value !== void 0) return value;
|
|
6248
6397
|
throw new Error(tool === void 0 ? `${PROVIDER_DISPLAY_NAMES[provider]} 尚未配置 API Key,请先到 设置 > 插件 > 图像生成 配置` : `${tool} requires the ${PROVIDER_DISPLAY_NAMES[provider]} API key; configure it in Settings > Plugins > Image generation.`);
|
|
6249
6398
|
}
|
|
@@ -27810,11 +27959,14 @@ function openAICompatStudioProfile(config, model, configured) {
|
|
|
27810
27959
|
}
|
|
27811
27960
|
/** Return only browser-safe capability data. */
|
|
27812
27961
|
async function describeStudio(ctx, config, subscriptions) {
|
|
27813
|
-
const
|
|
27962
|
+
const compatChannels = resolveOpenAICompatChannels(config);
|
|
27963
|
+
const configuredEntries = await Promise.all([...CLOUD_IMAGE_PROVIDERS.filter((provider) => provider !== "openai-compat").map(async (provider) => {
|
|
27814
27964
|
return [provider, await resolveApiKey(ctx, provider) !== void 0];
|
|
27815
|
-
}))
|
|
27965
|
+
}), ...compatChannels.map(async (channel) => {
|
|
27966
|
+
return [`openai-compat:${channel.id}`, await resolveApiKey(ctx, "openai-compat", channel.keyRef) !== void 0];
|
|
27967
|
+
})]);
|
|
27816
27968
|
const configured = Object.fromEntries(configuredEntries);
|
|
27817
|
-
const cloudProfiles = CLOUD_IMAGE_PROVIDERS.
|
|
27969
|
+
const cloudProfiles = CLOUD_IMAGE_PROVIDERS.flatMap((provider) => provider === "openai-compat" ? compatChannels.map((channel) => studioProfile(config, provider, configured[`openai-compat:${channel.id}`] === true, channel.id)) : [studioProfile(config, provider, configured[provider] === true)]);
|
|
27818
27970
|
const subStatuses = await Promise.all(SUBSCRIPTION_PROVIDERS.map(async (provider) => {
|
|
27819
27971
|
return [provider, subscriptions !== void 0 && (await subscriptions.loginStatus(vendorOf(provider))).state === "logged-in"];
|
|
27820
27972
|
}));
|
|
@@ -27823,9 +27975,12 @@ async function describeStudio(ctx, config, subscriptions) {
|
|
|
27823
27975
|
});
|
|
27824
27976
|
const profiles = [...cloudProfiles, ...subProfiles];
|
|
27825
27977
|
const preferred = config.provider;
|
|
27978
|
+
const activeProvider = isStudioPreferred(preferred) ? preferred : profiles.find((profile) => profile.configured)?.provider ?? "google";
|
|
27979
|
+
const activeChannel = activeProvider === "openai-compat" ? profiles.find((profile) => profile.provider === activeProvider && profile.channel === resolveOpenAICompatChannel(config).id)?.channel ?? profiles.find((profile) => profile.provider === activeProvider)?.channel : void 0;
|
|
27826
27980
|
return {
|
|
27827
27981
|
providers: profiles,
|
|
27828
|
-
activeProvider
|
|
27982
|
+
activeProvider,
|
|
27983
|
+
...activeChannel === void 0 ? {} : { activeChannel }
|
|
27829
27984
|
};
|
|
27830
27985
|
}
|
|
27831
27986
|
/** The persisted default provider is usable directly when the workbench can drive it. */
|
|
@@ -27861,12 +28016,14 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27861
28016
|
assertAllowed(subscriptionStudioProfile(input.provider, true), input);
|
|
27862
28017
|
return generateSubscriptionFromStudio(ctx, subscriptions, input, signal);
|
|
27863
28018
|
}
|
|
27864
|
-
|
|
27865
|
-
const
|
|
28019
|
+
const channel = input.provider === "openai-compat" ? resolveOpenAICompatChannel(config, input.channel) : void 0;
|
|
28020
|
+
const requestConfig = input.provider === "openai-compat" ? withOpenAICompatChannel(config, input.channel) : config;
|
|
28021
|
+
assertAllowed(studioProfile(requestConfig, input.provider, true, channel?.id), input);
|
|
28022
|
+
const active = resolveProvider(withProviderOverrides(requestConfig, input.provider, input.model));
|
|
27866
28023
|
if (active.provider === "comfyui") throw new Error("ComfyUI 暂未接入工作台");
|
|
27867
28024
|
if (isSubscriptionProvider(active.provider)) throw new Error("订阅生图应通过订阅渠道执行");
|
|
27868
28025
|
const wired = active;
|
|
27869
|
-
const credential = await requireApiKey(ctx, input.provider);
|
|
28026
|
+
const credential = await requireApiKey(ctx, input.provider, void 0, channel?.keyRef);
|
|
27870
28027
|
const rawRefs = input.references ?? (input.reference ? [input.reference] : []);
|
|
27871
28028
|
if (input.mode === "edit" && rawRefs.length === 0) throw new Error("图生图需要至少一张参考图");
|
|
27872
28029
|
const sourceImages = input.mode === "edit" ? await Promise.all(rawRefs.map((ref) => readStudioReference(ctx, ref, signal))) : [];
|
|
@@ -27903,16 +28060,16 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27903
28060
|
output = `${aspectRatio}, ${imageSize}`;
|
|
27904
28061
|
} else if (wired.provider === "openai" || wired.provider === "openai-compat" || wired.provider === "xai" || wired.provider === "zhipu") {
|
|
27905
28062
|
const p = wired.provider;
|
|
27906
|
-
const officialTable = p === "openai" ? officialSizeTable(
|
|
28063
|
+
const officialTable = p === "openai" ? officialSizeTable(requestConfig, wired.model) : void 0;
|
|
27907
28064
|
const officialResolution = input.resolution ?? (officialTable ? openAICompatTable({ openaiCompatSizes: officialTable }).find((group) => group.ratio === input.ratio)?.tiers[0]?.tier : void 0) ?? "1K";
|
|
27908
|
-
const size = p === "openai-compat" ? openAIRequestSize(
|
|
28065
|
+
const size = p === "openai-compat" ? openAIRequestSize(requestConfig, input.ratio, input.resolution ?? input.quality) : p === "openai" ? openAIRequestSize({ openaiCompatSizes: officialTable }, input.ratio, officialResolution) : p === "xai" ? void 0 : CLOUD_CAPABILITIES[p].sizeFor?.(input.ratio, input.quality);
|
|
27909
28066
|
if (p === "openai" && size !== void 0) validateOfficialSize(wired.model, size);
|
|
27910
28067
|
const requestedQuality = input.generationQuality ?? input.quality;
|
|
27911
|
-
const quality = p === "openai" ? requestedQuality === "auto" ? void 0 : requestedQuality : p === "openai-compat" ? compatRequestQuality(
|
|
28068
|
+
const quality = p === "openai" ? requestedQuality === "auto" ? void 0 : requestedQuality : p === "openai-compat" ? compatRequestQuality(requestConfig, input.generationQuality) : p === "zhipu" ? "hd" : void 0;
|
|
27912
28069
|
const extraBody = p === "xai" ? {
|
|
27913
28070
|
aspect_ratio: input.ratio,
|
|
27914
28071
|
resolution: input.quality
|
|
27915
|
-
} : p === "openai-compat" ? input.mode === "generate" ? compatGenerationExtraBody(
|
|
28072
|
+
} : p === "openai-compat" ? input.mode === "generate" ? compatGenerationExtraBody(requestConfig, input.watermark) : compatEditExtraBody(requestConfig, input.watermark) : void 0;
|
|
27916
28073
|
generated = input.mode === "edit" ? await editOpenAICompatibleImage({
|
|
27917
28074
|
apiKey: credential,
|
|
27918
28075
|
baseURL: wired.baseURL,
|
|
@@ -27927,7 +28084,7 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27927
28084
|
...p === "openai-compat" ? {
|
|
27928
28085
|
editFormat: wired.editFormat,
|
|
27929
28086
|
editExtra: wired.editExtra,
|
|
27930
|
-
editSizeMode:
|
|
28087
|
+
editSizeMode: requestConfig.openaiCompatEditSizeMode ?? "auto"
|
|
27931
28088
|
} : p === "xai" ? { editFormat: "xaiJson" } : {}
|
|
27932
28089
|
}) : await generateOpenAICompatibleImage({
|
|
27933
28090
|
provider: p,
|
|
@@ -28012,6 +28169,7 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
28012
28169
|
attachment: single.attachment,
|
|
28013
28170
|
output: single.output,
|
|
28014
28171
|
provider: input.provider,
|
|
28172
|
+
...channel === void 0 ? {} : { channel: channel.id },
|
|
28015
28173
|
model: input.model,
|
|
28016
28174
|
prompt: input.prompt,
|
|
28017
28175
|
createdAt: Date.now(),
|
|
@@ -28042,6 +28200,7 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
28042
28200
|
attachment: first.attachment,
|
|
28043
28201
|
output: first.output,
|
|
28044
28202
|
provider: input.provider,
|
|
28203
|
+
...channel === void 0 ? {} : { channel: channel.id },
|
|
28045
28204
|
model: input.model,
|
|
28046
28205
|
prompt: input.prompt,
|
|
28047
28206
|
createdAt: Date.now(),
|
|
@@ -28165,13 +28324,19 @@ function subscriptionResponse(input, startedAt, requestedCount, failedCount, ite
|
|
|
28165
28324
|
...errors !== void 0 && errors.length > 0 ? { errors } : {}
|
|
28166
28325
|
};
|
|
28167
28326
|
}
|
|
28168
|
-
function studioProfile(config, provider, configured) {
|
|
28169
|
-
const
|
|
28170
|
-
const
|
|
28327
|
+
function studioProfile(config, provider, configured, channelId) {
|
|
28328
|
+
const channel = provider === "openai-compat" && channelId !== void 0 ? resolveOpenAICompatChannel(config, channelId) : void 0;
|
|
28329
|
+
const scoped = channel === void 0 ? config : withOpenAICompatChannel(config, channel.id);
|
|
28330
|
+
const result = cloudStudioProfile(scoped, provider, configured);
|
|
28331
|
+
const editModel = provider === "openai-compat" ? scoped.openaiCompatEditModel?.trim() : void 0;
|
|
28171
28332
|
return {
|
|
28172
28333
|
...result,
|
|
28334
|
+
...channel === void 0 ? {} : {
|
|
28335
|
+
channel: channel.id,
|
|
28336
|
+
label: channel.label
|
|
28337
|
+
},
|
|
28173
28338
|
...editModel ? { editModel } : {},
|
|
28174
|
-
...watermarkProfile(
|
|
28339
|
+
...watermarkProfile(scoped, provider, result.model)
|
|
28175
28340
|
};
|
|
28176
28341
|
}
|
|
28177
28342
|
function cloudStudioProfile(config, provider, configured) {
|
|
@@ -28210,7 +28375,14 @@ function cloudStudioProfile(config, provider, configured) {
|
|
|
28210
28375
|
/** Output capabilities remain visible before credentials, address or model are configured. */
|
|
28211
28376
|
function compatibleStudioProfile(config, model, configured) {
|
|
28212
28377
|
const groups = openAICompatTable(config);
|
|
28213
|
-
const
|
|
28378
|
+
const capabilityProfile = {
|
|
28379
|
+
modelOptions: resolveOpenAICompatModels({
|
|
28380
|
+
...config,
|
|
28381
|
+
openaiCompatModel: model
|
|
28382
|
+
}).map((value) => ({
|
|
28383
|
+
value,
|
|
28384
|
+
label: value
|
|
28385
|
+
})),
|
|
28214
28386
|
generationQualityOptions: compatQualityOptions(config),
|
|
28215
28387
|
defaultGenerationQuality: compatRequestQuality(config) ?? "__default__",
|
|
28216
28388
|
resolutionOptions: Object.fromEntries(groups.map((group) => [group.ratio, group.tiers.map((entry) => ({
|
|
@@ -28227,7 +28399,7 @@ function compatibleStudioProfile(config, model, configured) {
|
|
|
28227
28399
|
value: "standard",
|
|
28228
28400
|
label: "标准(推荐)"
|
|
28229
28401
|
}], "1:1", "standard"),
|
|
28230
|
-
...
|
|
28402
|
+
...capabilityProfile,
|
|
28231
28403
|
resolutionOptions: Object.fromEntries(groups.map((group) => [group.ratio, [{
|
|
28232
28404
|
value: "standard",
|
|
28233
28405
|
label: `标准 · ${group.tiers[0].size}`
|
|
@@ -28235,7 +28407,7 @@ function compatibleStudioProfile(config, model, configured) {
|
|
|
28235
28407
|
};
|
|
28236
28408
|
return {
|
|
28237
28409
|
...openAICompatStudioProfile(config, model, configured),
|
|
28238
|
-
...
|
|
28410
|
+
...capabilityProfile
|
|
28239
28411
|
};
|
|
28240
28412
|
}
|
|
28241
28413
|
function profile(provider, model, configured, ratioOptions, qualityOptions, defaultRatio, defaultQuality) {
|
|
@@ -28268,7 +28440,7 @@ function option(value) {
|
|
|
28268
28440
|
};
|
|
28269
28441
|
}
|
|
28270
28442
|
function assertAllowed(profile, input) {
|
|
28271
|
-
if (
|
|
28443
|
+
if (!studioModelOptionsForMode(profile, input.mode).some((option) => option.value === input.model)) throw new Error("模型配置已变化,请刷新工作台后重试");
|
|
28272
28444
|
if (!profile.ratioOptions.some((option) => option.value === input.ratio)) throw new Error("该 Provider 不支持所选比例");
|
|
28273
28445
|
const selectedQuality = input.provider === "openai" ? input.generationQuality ?? input.quality : input.resolution ?? input.quality;
|
|
28274
28446
|
if (!profile.qualityOptions.some((option) => option.value === selectedQuality)) throw new Error("该 Provider 不支持所选清晰度");
|
|
@@ -28319,8 +28491,6 @@ async function serveStudio(req, res, deps) {
|
|
|
28319
28491
|
if (!(req.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) return jsonError$1(res, 415, "json-required");
|
|
28320
28492
|
const controller = new AbortController();
|
|
28321
28493
|
const watchdog = new AbortController();
|
|
28322
|
-
const CLOUD_WATCHDOG_MS = 15e4;
|
|
28323
|
-
let subscriptionChannel = false;
|
|
28324
28494
|
const signal = AbortSignal.any([controller.signal, watchdog.signal]);
|
|
28325
28495
|
const onConnectionClose = () => {
|
|
28326
28496
|
if (!res.writableEnded) controller.abort(/* @__PURE__ */ new Error("The browser closed the image generation request."));
|
|
@@ -28337,10 +28507,12 @@ async function serveStudio(req, res, deps) {
|
|
|
28337
28507
|
} catch (error) {
|
|
28338
28508
|
return jsonError$1(res, 400, errorMessage(error, "invalid-request"));
|
|
28339
28509
|
}
|
|
28340
|
-
subscriptionChannel = isSubscriptionProvider(input.provider);
|
|
28341
|
-
|
|
28510
|
+
const subscriptionChannel = isSubscriptionProvider(input.provider);
|
|
28511
|
+
const timeoutMs = subscriptionChannel ? SUBSCRIPTION_TIMEOUT_MS : deps.timeoutMs ?? 15e4;
|
|
28512
|
+
const timeout = setTimeout(() => {
|
|
28342
28513
|
watchdog.abort();
|
|
28343
|
-
},
|
|
28514
|
+
}, timeoutMs);
|
|
28515
|
+
timeout.unref?.();
|
|
28344
28516
|
res.writeHead(200, {
|
|
28345
28517
|
"content-type": "application/json; charset=utf-8",
|
|
28346
28518
|
"cache-control": "no-store",
|
|
@@ -28355,11 +28527,12 @@ async function serveStudio(req, res, deps) {
|
|
|
28355
28527
|
res.end(JSON.stringify(output));
|
|
28356
28528
|
} catch (error) {
|
|
28357
28529
|
if (res.destroyed || res.writableEnded) return;
|
|
28358
|
-
const message = watchdog.signal.aborted ? subscriptionChannel ?
|
|
28530
|
+
const message = watchdog.signal.aborted ? subscriptionChannel ? `上游生成超时(订阅通道 ${String(Math.round(timeoutMs / 1e3))} 秒未返回),请稍后重试;${UPSTREAM_TIMEOUT_HINT}` : `上游生成超时(${String(Math.round(timeoutMs / 1e3))} 秒未返回),请检查网络或代理后重试;${UPSTREAM_TIMEOUT_HINT}` : errorMessage(error, "generation-failed");
|
|
28359
28531
|
res.end(JSON.stringify({ error: message }));
|
|
28360
28532
|
}
|
|
28361
28533
|
} finally {
|
|
28362
28534
|
clearInterval(heartbeat);
|
|
28535
|
+
clearTimeout(timeout);
|
|
28363
28536
|
}
|
|
28364
28537
|
} catch (error) {
|
|
28365
28538
|
if (res.headersSent || res.writableEnded || res.destroyed) return;
|
|
@@ -28375,6 +28548,8 @@ function parseStudioGenerateRequest(value) {
|
|
|
28375
28548
|
if (input === void 0) throw new Error("请求格式无效");
|
|
28376
28549
|
if (input.mode !== "generate" && input.mode !== "edit") throw new Error("请选择生成类型");
|
|
28377
28550
|
if (!studioProvider(input.provider)) throw new Error("不支持该图像 Provider");
|
|
28551
|
+
if (input.channel !== void 0 && input.provider !== "openai-compat") throw new Error("仅 OpenAI 兼容 Provider 支持选择渠道");
|
|
28552
|
+
const channel = input.channel === void 0 ? void 0 : requiredText(input.channel, "请选择渠道", 100);
|
|
28378
28553
|
const prompt = requiredText(input.prompt, "请输入提示词");
|
|
28379
28554
|
const model = requiredText(input.model, "请选择模型", 200);
|
|
28380
28555
|
const ratio = requiredText(input.ratio, "请选择比例", 32);
|
|
@@ -28395,6 +28570,7 @@ function parseStudioGenerateRequest(value) {
|
|
|
28395
28570
|
return {
|
|
28396
28571
|
mode: input.mode,
|
|
28397
28572
|
provider: input.provider,
|
|
28573
|
+
...channel === void 0 ? {} : { channel },
|
|
28398
28574
|
model,
|
|
28399
28575
|
prompt,
|
|
28400
28576
|
ratio,
|
|
@@ -28753,12 +28929,17 @@ async function serveTestConnection(req, res, deps) {
|
|
|
28753
28929
|
const provider = record(body)?.provider;
|
|
28754
28930
|
if (typeof provider !== "string" || !IMAGE_PROVIDERS.includes(provider)) return jsonError(res, 400, "invalid-provider");
|
|
28755
28931
|
const active = provider;
|
|
28756
|
-
|
|
28932
|
+
const action = record(body)?.action;
|
|
28933
|
+
const rawChannel = record(body)?.channel;
|
|
28934
|
+
if (rawChannel !== void 0 && (typeof rawChannel !== "string" || rawChannel.trim().length === 0 || active !== "openai-compat")) return jsonError(res, 400, "invalid-channel");
|
|
28935
|
+
const channel = typeof rawChannel === "string" ? rawChannel.trim() : void 0;
|
|
28936
|
+
const requestConfig = () => active === "openai-compat" ? withOpenAICompatChannel(deps.config(), channel) : deps.config();
|
|
28937
|
+
if (action === "models") {
|
|
28757
28938
|
if (active === "comfyui" || isSubscriptionProvider(active)) return jsonError(res, 400, "models-unsupported");
|
|
28758
28939
|
let result;
|
|
28759
28940
|
try {
|
|
28760
|
-
const apiKey = await deps.resolveKey(active);
|
|
28761
|
-
result = active === "google" ? await fetchGoogleImageModels(
|
|
28941
|
+
const apiKey = await deps.resolveKey(active, channel);
|
|
28942
|
+
result = active === "google" ? await fetchGoogleImageModels(requestConfig(), apiKey) : active === "dashscope" ? await fetchDashScopeImageModels(requestConfig(), apiKey) : await fetchOpenAIImageModels(active, requestConfig(), apiKey);
|
|
28762
28943
|
} catch (error) {
|
|
28763
28944
|
result = {
|
|
28764
28945
|
ok: false,
|
|
@@ -28773,8 +28954,8 @@ async function serveTestConnection(req, res, deps) {
|
|
|
28773
28954
|
if (active === "comfyui") result = await probeComfyUIConnection(deps.config());
|
|
28774
28955
|
else if (isSubscriptionProvider(active)) result = await probeSubscriptionConnection(active, deps.subscriptionManager);
|
|
28775
28956
|
else {
|
|
28776
|
-
const apiKey = await deps.resolveKey(active);
|
|
28777
|
-
result = await probeProviderConnection(active,
|
|
28957
|
+
const apiKey = await deps.resolveKey(active, channel);
|
|
28958
|
+
result = await probeProviderConnection(active, requestConfig(), apiKey);
|
|
28778
28959
|
}
|
|
28779
28960
|
} catch (error) {
|
|
28780
28961
|
result = {
|
|
@@ -28840,8 +29021,10 @@ function serveSettingsHealth(req, res, health) {
|
|
|
28840
29021
|
//#endregion
|
|
28841
29022
|
//#region lib/types/image-tool-context.js
|
|
28842
29023
|
/** Live, credential-free instructions for the conversation model's image tools. */
|
|
28843
|
-
function imageToolContext(config) {
|
|
29024
|
+
function imageToolContext(config, configuredProviders) {
|
|
28844
29025
|
const provider = config.provider ?? "google";
|
|
29026
|
+
const configured = configuredProviders === void 0 ? void 0 : [...configuredProviders];
|
|
29027
|
+
if (configured !== void 0 && !configured.includes("comfyui") && resolveComfyUIWorkflows(config).length > 0) configured.push("comfyui");
|
|
28845
29028
|
let model = "not configured";
|
|
28846
29029
|
let editModel = model;
|
|
28847
29030
|
try {
|
|
@@ -28852,6 +29035,7 @@ function imageToolContext(config) {
|
|
|
28852
29035
|
} catch {}
|
|
28853
29036
|
return [
|
|
28854
29037
|
`Image tools current default provider: ${provider}; generation model: ${JSON.stringify(model)}; editing model: ${JSON.stringify(editModel)}.`,
|
|
29038
|
+
...configured === void 0 ? [] : [`Configured image providers (credential saved, subscription logged in, or ComfyUI workflow present): ${configured.length > 0 ? configured.join(", ") : "none"}. Do not select unlisted providers; configuration presence does not guarantee service reachability.`],
|
|
28855
29039
|
"Unless the user explicitly requests a different image provider or model, OMIT provider and model from generate_image, generate_images and edit_image. Do not guess a channel or switch channels after an error.",
|
|
28856
29040
|
"Omit unused optional arguments entirely; never fill them with empty strings. size and aspect_ratio are alternatives: provide at most one.",
|
|
28857
29041
|
"Subscription parameters: chatgpt-sub accepts size OR aspect_ratio, but NOT image_size. grok-sub image_size: 1K or 2K. google-sub image_size: 1K or 4K. Do not copy parameters from another provider.",
|
|
@@ -28872,6 +29056,7 @@ function cleanImageToolArgs(args) {
|
|
|
28872
29056
|
const fields = clean;
|
|
28873
29057
|
for (const key of [
|
|
28874
29058
|
"model",
|
|
29059
|
+
"channel",
|
|
28875
29060
|
"size",
|
|
28876
29061
|
"aspect_ratio",
|
|
28877
29062
|
"image_size",
|
|
@@ -28891,6 +29076,11 @@ function providerOverrideOf(value) {
|
|
|
28891
29076
|
if (typeof value !== "string" || !IMAGE_PROVIDERS.includes(value)) throw new Error(`Unsupported provider ${JSON.stringify(value)}. Supported providers: ${IMAGE_PROVIDERS.join(", ")}.`);
|
|
28892
29077
|
return value;
|
|
28893
29078
|
}
|
|
29079
|
+
function configForToolCall(config, provider, channel) {
|
|
29080
|
+
const selected = provider ?? config.provider ?? "google";
|
|
29081
|
+
if (channel !== void 0 && selected !== "openai-compat") throw new Error("The channel argument is only supported with provider=openai-compat.");
|
|
29082
|
+
return selected === "openai-compat" ? withOpenAICompatChannel(config, channel) : config;
|
|
29083
|
+
}
|
|
28894
29084
|
function apply(ctx, config = {}) {
|
|
28895
29085
|
const plainConfig = (source) => {
|
|
28896
29086
|
const out = {};
|
|
@@ -28901,11 +29091,23 @@ function apply(ctx, config = {}) {
|
|
|
28901
29091
|
const knownWorkspaceRoots = /* @__PURE__ */ new Set();
|
|
28902
29092
|
const canvasMirror = new CanvasMirror();
|
|
28903
29093
|
const subscriptionManager = new SubscriptionManager(ctx);
|
|
29094
|
+
let configuredImageProviders;
|
|
29095
|
+
let imageContextRevision = 0;
|
|
29096
|
+
const refreshImageToolContext = async () => {
|
|
29097
|
+
const revision = ++imageContextRevision;
|
|
29098
|
+
const config = current();
|
|
29099
|
+
try {
|
|
29100
|
+
const providers = (await describeStudio(ctx, config, subscriptionManager)).providers.filter((profile) => profile.configured).map((profile) => profile.provider === "openai-compat" && profile.channel !== void 0 ? `${profile.provider} (channel: ${profile.channel})` : profile.provider);
|
|
29101
|
+
if (revision === imageContextRevision) configuredImageProviders = providers;
|
|
29102
|
+
} catch {}
|
|
29103
|
+
};
|
|
28904
29104
|
const settingsHealth = installImageSettings(ctx, current(), {
|
|
28905
29105
|
setSource: (source) => {
|
|
28906
29106
|
current = () => migrateOpenAICompatConfig(plainConfig(source()));
|
|
28907
29107
|
},
|
|
28908
|
-
onChange: () => {
|
|
29108
|
+
onChange: () => {
|
|
29109
|
+
refreshImageToolContext();
|
|
29110
|
+
}
|
|
28909
29111
|
});
|
|
28910
29112
|
ctx.inject(["webServer"], (webCtx) => {
|
|
28911
29113
|
registerSubscriptionRoutes(webCtx, subscriptionManager);
|
|
@@ -28971,7 +29173,7 @@ function apply(ctx, config = {}) {
|
|
|
28971
29173
|
kind: "exact",
|
|
28972
29174
|
path: TEST_CONNECTION_ROUTE,
|
|
28973
29175
|
handler: (req, res) => serveTestConnection(req, res, {
|
|
28974
|
-
resolveKey: (provider) => resolveApiKey(ctx, provider),
|
|
29176
|
+
resolveKey: (provider, channel) => resolveApiKey(ctx, provider, provider === "openai-compat" ? resolveOpenAICompatChannel(current(), channel).keyRef : void 0),
|
|
28975
29177
|
config: () => current(),
|
|
28976
29178
|
subscriptionManager
|
|
28977
29179
|
})
|
|
@@ -28995,10 +29197,19 @@ function apply(ctx, config = {}) {
|
|
|
28995
29197
|
}), "dsh-image-gen: canvas asset route");
|
|
28996
29198
|
});
|
|
28997
29199
|
ctx.inject(["systemPrompt"], (promptCtx) => {
|
|
29200
|
+
refreshImageToolContext();
|
|
29201
|
+
ctx.inject(["credentials"], (credentialCtx) => {
|
|
29202
|
+
credentialCtx.on("credentials/reference-updated", () => {
|
|
29203
|
+
refreshImageToolContext();
|
|
29204
|
+
});
|
|
29205
|
+
credentialCtx.on("credentials/record-updated", () => {
|
|
29206
|
+
refreshImageToolContext();
|
|
29207
|
+
});
|
|
29208
|
+
});
|
|
28998
29209
|
promptCtx.effect(() => promptCtx.systemPrompt.context({
|
|
28999
29210
|
name: "dsh-image-gen:image-defaults",
|
|
29000
29211
|
order: 59,
|
|
29001
|
-
text: () => imageToolContext(current())
|
|
29212
|
+
text: () => imageToolContext(current(), configuredImageProviders)
|
|
29002
29213
|
}), "dsh-image-gen: image tool context");
|
|
29003
29214
|
promptCtx.effect(() => promptCtx.systemPrompt.context({
|
|
29004
29215
|
name: "dsh-image-gen:canvas",
|
|
@@ -29030,7 +29241,8 @@ function apply(ctx, config = {}) {
|
|
|
29030
29241
|
const fallbackRoot = Array.from(knownWorkspaceRoots)[0] || process.cwd();
|
|
29031
29242
|
return generateFromStudio(ctx, current(), input, signal, fallbackRoot, subscriptionManager);
|
|
29032
29243
|
},
|
|
29033
|
-
maxBodyBytes: Math.ceil(ctx.attachments.imageLimits.maxImageBytes * 1.4 * 5) + 262144
|
|
29244
|
+
maxBodyBytes: Math.ceil(ctx.attachments.imageLimits.maxImageBytes * 1.4 * 5) + 262144,
|
|
29245
|
+
timeoutMs: current().cloudTimeoutMs ?? 15e4
|
|
29034
29246
|
})
|
|
29035
29247
|
}), "dsh-image-gen: studio route");
|
|
29036
29248
|
const serveInspiration = createInspirationRoute();
|
|
@@ -29047,7 +29259,8 @@ function apply(ctx, config = {}) {
|
|
|
29047
29259
|
/** Per-item generation request shared by generate_image and generate_images. */
|
|
29048
29260
|
const generateSingle = async (rawArgs, exec) => {
|
|
29049
29261
|
const args = cleanImageToolArgs(rawArgs);
|
|
29050
|
-
const
|
|
29262
|
+
const requestConfig = configForToolCall(current(), providerOverrideOf(args.provider), args.channel);
|
|
29263
|
+
const active = resolveProvider(withProviderOverrides(requestConfig, providerOverrideOf(args.provider), args.model));
|
|
29051
29264
|
if (active.provider === "comfyui") {
|
|
29052
29265
|
const workflow = selectComfyUIWorkflow(active, args.workflow);
|
|
29053
29266
|
return saveGenerated(ctx, await generateComfyUIImage({
|
|
@@ -29074,11 +29287,12 @@ function apply(ctx, config = {}) {
|
|
|
29074
29287
|
signal: exec.signal
|
|
29075
29288
|
}), active.provider, active.model, "subscription", current(), exec, knownWorkspaceRoots);
|
|
29076
29289
|
}
|
|
29077
|
-
const
|
|
29290
|
+
const runCloud = (operation) => withGenerationTimeout(current().cloudTimeoutMs ?? 15e4, exec.signal, operation);
|
|
29291
|
+
const credential = await requireApiKey(ctx, active.provider, "generate_image", active.provider === "openai-compat" ? resolveOpenAICompatChannel(requestConfig, args.channel).keyRef : void 0);
|
|
29078
29292
|
if (active.provider === "google") {
|
|
29079
29293
|
const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
|
|
29080
29294
|
const imageSize = args.image_size ?? active.imageSize;
|
|
29081
|
-
return saveGenerated(ctx, await generateGoogleImage({
|
|
29295
|
+
return saveGenerated(ctx, await runCloud((signal) => generateGoogleImage({
|
|
29082
29296
|
apiKey: credential,
|
|
29083
29297
|
endpoint: active.endpoint,
|
|
29084
29298
|
native: active.native,
|
|
@@ -29087,20 +29301,20 @@ function apply(ctx, config = {}) {
|
|
|
29087
29301
|
aspectRatio,
|
|
29088
29302
|
imageSize,
|
|
29089
29303
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29090
|
-
signal
|
|
29091
|
-
}), active.provider, active.model, `${aspectRatio}, ${imageSize}`, current(), exec, knownWorkspaceRoots);
|
|
29304
|
+
signal
|
|
29305
|
+
})), active.provider, active.model, `${aspectRatio}, ${imageSize}`, current(), exec, knownWorkspaceRoots);
|
|
29092
29306
|
}
|
|
29093
29307
|
if (active.provider === "dashscope") {
|
|
29094
29308
|
const size = args.size ?? active.imageSize;
|
|
29095
|
-
return saveGenerated(ctx, await generateDashScopeImage({
|
|
29309
|
+
return saveGenerated(ctx, await runCloud((signal) => generateDashScopeImage({
|
|
29096
29310
|
apiKey: credential,
|
|
29097
29311
|
endpoint: active.endpoint,
|
|
29098
29312
|
model: active.model,
|
|
29099
29313
|
prompt: args.prompt,
|
|
29100
29314
|
size,
|
|
29101
29315
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29102
|
-
signal
|
|
29103
|
-
}), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
29316
|
+
signal
|
|
29317
|
+
})), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
29104
29318
|
}
|
|
29105
29319
|
if (active.provider === "xai") {
|
|
29106
29320
|
const extraBody = xaiToolParameters({
|
|
@@ -29108,7 +29322,7 @@ function apply(ctx, config = {}) {
|
|
|
29108
29322
|
...args.image_size !== void 0 ? { imageSize: args.image_size } : {},
|
|
29109
29323
|
...args.size !== void 0 ? { size: args.size } : {}
|
|
29110
29324
|
});
|
|
29111
|
-
return saveGenerated(ctx, await generateOpenAICompatibleImage({
|
|
29325
|
+
return saveGenerated(ctx, await runCloud((signal) => generateOpenAICompatibleImage({
|
|
29112
29326
|
provider: "xai",
|
|
29113
29327
|
apiKey: credential,
|
|
29114
29328
|
baseURL: active.baseURL,
|
|
@@ -29116,15 +29330,15 @@ function apply(ctx, config = {}) {
|
|
|
29116
29330
|
prompt: args.prompt,
|
|
29117
29331
|
extraBody,
|
|
29118
29332
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29119
|
-
signal
|
|
29120
|
-
}), active.provider, active.model, extraBody.aspect_ratio ?? "auto", current(), exec, knownWorkspaceRoots);
|
|
29333
|
+
signal
|
|
29334
|
+
})), active.provider, active.model, extraBody.aspect_ratio ?? "auto", current(), exec, knownWorkspaceRoots);
|
|
29121
29335
|
}
|
|
29122
29336
|
const size = args.size ?? active.imageSize;
|
|
29123
29337
|
const arkOptions = active.provider === "seedream" ? active.arkOptions : void 0;
|
|
29124
29338
|
if (active.provider === "openai") validateOfficialSize(active.model, size);
|
|
29125
|
-
const quality = active.provider === "openai-compat" ? compatRequestQuality(
|
|
29339
|
+
const quality = active.provider === "openai-compat" ? compatRequestQuality(requestConfig, args.quality) : active.provider === "openai" ? args.quality : void 0;
|
|
29126
29340
|
if (active.provider === "openai" && quality !== void 0 && !officialQualityOptions(active.model).some((option) => option.value === quality)) throw new Error("该 OpenAI 模型不支持所选生成质量");
|
|
29127
|
-
|
|
29341
|
+
const value = await saveGenerated(ctx, await runCloud((signal) => generateOpenAICompatibleImage({
|
|
29128
29342
|
provider: active.provider,
|
|
29129
29343
|
apiKey: credential,
|
|
29130
29344
|
baseURL: active.baseURL,
|
|
@@ -29132,11 +29346,15 @@ function apply(ctx, config = {}) {
|
|
|
29132
29346
|
prompt: args.prompt,
|
|
29133
29347
|
size,
|
|
29134
29348
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29135
|
-
signal
|
|
29349
|
+
signal,
|
|
29136
29350
|
...quality === void 0 ? {} : { quality },
|
|
29137
|
-
...active.provider === "openai-compat" ? { extraBody:
|
|
29351
|
+
...active.provider === "openai-compat" ? { extraBody: requestConfig.openaiCompatExtraBody ?? {} } : {},
|
|
29138
29352
|
...arkOptions === void 0 ? {} : { arkOptions }
|
|
29139
|
-
}), active.provider, active.model, quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
29353
|
+
})), active.provider, active.model, quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
29354
|
+
return active.provider === "openai-compat" ? {
|
|
29355
|
+
...value,
|
|
29356
|
+
channel: resolveOpenAICompatChannel(requestConfig, args.channel).id
|
|
29357
|
+
} : value;
|
|
29140
29358
|
};
|
|
29141
29359
|
ctx.tools.register(defineTool({
|
|
29142
29360
|
name: "generate_image",
|
|
@@ -29166,7 +29384,11 @@ function apply(ctx, config = {}) {
|
|
|
29166
29384
|
},
|
|
29167
29385
|
model: {
|
|
29168
29386
|
type: "string",
|
|
29169
|
-
description: "
|
|
29387
|
+
description: "OMIT unless the user explicitly requests a specific model; omission uses the configured model. Optional override for this call only. Not used by ComfyUI (use workflow instead) nor by the subscription providers (model fixed by the subscription)."
|
|
29388
|
+
},
|
|
29389
|
+
channel: {
|
|
29390
|
+
type: "string",
|
|
29391
|
+
description: "Optional configured OpenAI-compatible channel id for this call only. Only meaningful with provider=openai-compat; omit to use the active channel."
|
|
29170
29392
|
},
|
|
29171
29393
|
aspect_ratio: {
|
|
29172
29394
|
type: "string",
|
|
@@ -29241,7 +29463,11 @@ function apply(ctx, config = {}) {
|
|
|
29241
29463
|
},
|
|
29242
29464
|
model: {
|
|
29243
29465
|
type: "string",
|
|
29244
|
-
description: "
|
|
29466
|
+
description: "OMIT unless the user explicitly requests a specific model for every item; omission uses the configured model."
|
|
29467
|
+
},
|
|
29468
|
+
channel: {
|
|
29469
|
+
type: "string",
|
|
29470
|
+
description: "Optional configured OpenAI-compatible channel id applied to every item; omit to use the active channel."
|
|
29245
29471
|
},
|
|
29246
29472
|
aspect_ratio: {
|
|
29247
29473
|
type: "string",
|
|
@@ -29338,6 +29564,7 @@ function apply(ctx, config = {}) {
|
|
|
29338
29564
|
type: "string",
|
|
29339
29565
|
required: true
|
|
29340
29566
|
},
|
|
29567
|
+
channel: { type: "string" },
|
|
29341
29568
|
model: {
|
|
29342
29569
|
type: "string",
|
|
29343
29570
|
required: true
|
|
@@ -29410,6 +29637,7 @@ function apply(ctx, config = {}) {
|
|
|
29410
29637
|
prompt,
|
|
29411
29638
|
...args.provider !== void 0 ? { provider: args.provider } : {},
|
|
29412
29639
|
...args.model !== void 0 ? { model: args.model } : {},
|
|
29640
|
+
...args.channel !== void 0 ? { channel: args.channel } : {},
|
|
29413
29641
|
...args.aspect_ratio !== void 0 ? { aspect_ratio: args.aspect_ratio } : {},
|
|
29414
29642
|
...args.image_size !== void 0 ? { image_size: args.image_size } : {},
|
|
29415
29643
|
...args.size !== void 0 ? { size: args.size } : {},
|
|
@@ -29463,7 +29691,11 @@ function apply(ctx, config = {}) {
|
|
|
29463
29691
|
},
|
|
29464
29692
|
model: {
|
|
29465
29693
|
type: "string",
|
|
29466
|
-
description: "
|
|
29694
|
+
description: "OMIT unless the user explicitly requests a specific model; omission uses the configured model. Optional override for this call only. Not used by ComfyUI (use workflow instead) nor by the subscription providers (model fixed by the subscription)."
|
|
29695
|
+
},
|
|
29696
|
+
channel: {
|
|
29697
|
+
type: "string",
|
|
29698
|
+
description: "Optional configured OpenAI-compatible channel id for this call only. Only meaningful with provider=openai-compat; omit to use the active channel."
|
|
29467
29699
|
},
|
|
29468
29700
|
source: {
|
|
29469
29701
|
type: "string",
|
|
@@ -29529,7 +29761,8 @@ function apply(ctx, config = {}) {
|
|
|
29529
29761
|
output: imageOutput("Edited"),
|
|
29530
29762
|
async execute(rawArgs, exec) {
|
|
29531
29763
|
const args = cleanImageToolArgs(rawArgs);
|
|
29532
|
-
const
|
|
29764
|
+
const requestConfig = configForToolCall(current(), providerOverrideOf(args.provider), args.channel);
|
|
29765
|
+
const active = resolveProvider(withProviderOverrides(requestConfig, providerOverrideOf(args.provider), args.model, "edit"));
|
|
29533
29766
|
const canvasSelection = args.source === "canvas_selection";
|
|
29534
29767
|
if (canvasSelection && (args.source_attachment_id !== void 0 || Array.isArray(args.source_attachment_ids) || args.source_path !== void 0 || Array.isArray(args.source_paths))) throw new Error("edit_image source=canvas_selection cannot be combined with source_attachment_id, source_attachment_ids, source_path, or source_paths; provide exactly one selector");
|
|
29535
29768
|
const sourceImages = canvasSelection ? await resolveCanvasSelectionReferences({
|
|
@@ -29583,7 +29816,7 @@ function apply(ctx, config = {}) {
|
|
|
29583
29816
|
signal: exec.signal
|
|
29584
29817
|
}), active.provider, active.model, "subscription edit", current(), exec, knownWorkspaceRoots);
|
|
29585
29818
|
}
|
|
29586
|
-
const credential = await requireApiKey(ctx, active.provider, "edit_image");
|
|
29819
|
+
const credential = await requireApiKey(ctx, active.provider, "edit_image", active.provider === "openai-compat" ? resolveOpenAICompatChannel(requestConfig, args.channel).keyRef : void 0);
|
|
29587
29820
|
if (active.provider === "google") {
|
|
29588
29821
|
const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
|
|
29589
29822
|
const imageSize = args.image_size ?? active.imageSize;
|
|
@@ -29603,14 +29836,14 @@ function apply(ctx, config = {}) {
|
|
|
29603
29836
|
const size = args.size ?? active.imageSize;
|
|
29604
29837
|
if (active.provider === "openai" || active.provider === "openai-compat" || active.provider === "xai" || active.provider === "zhipu") {
|
|
29605
29838
|
if (active.provider === "openai") validateOfficialSize(active.model, size);
|
|
29606
|
-
const quality = active.provider === "openai-compat" ? compatRequestQuality(
|
|
29839
|
+
const quality = active.provider === "openai-compat" ? compatRequestQuality(requestConfig, args.quality) : active.provider === "openai" ? args.quality : void 0;
|
|
29607
29840
|
if (active.provider === "openai" && quality !== void 0 && !officialQualityOptions(active.model).some((option) => option.value === quality)) throw new Error("该 OpenAI 模型不支持所选生成质量");
|
|
29608
29841
|
const xaiExtraBody = active.provider === "xai" ? xaiToolParameters({
|
|
29609
29842
|
...args.aspect_ratio !== void 0 ? { aspectRatio: args.aspect_ratio } : {},
|
|
29610
29843
|
...args.image_size !== void 0 ? { imageSize: args.image_size } : {},
|
|
29611
29844
|
...args.size !== void 0 ? { size: args.size } : {}
|
|
29612
29845
|
}) : void 0;
|
|
29613
|
-
|
|
29846
|
+
const value = await saveGenerated(ctx, await editOpenAICompatibleImage({
|
|
29614
29847
|
apiKey: credential,
|
|
29615
29848
|
baseURL: active.baseURL,
|
|
29616
29849
|
model: active.model,
|
|
@@ -29623,12 +29856,16 @@ function apply(ctx, config = {}) {
|
|
|
29623
29856
|
...active.provider === "openai-compat" ? {
|
|
29624
29857
|
editFormat: active.editFormat,
|
|
29625
29858
|
editExtra: active.editExtra,
|
|
29626
|
-
editSizeMode:
|
|
29859
|
+
editSizeMode: requestConfig.openaiCompatEditSizeMode ?? "auto"
|
|
29627
29860
|
} : active.provider === "xai" ? {
|
|
29628
29861
|
editFormat: "xaiJson",
|
|
29629
29862
|
...xaiExtraBody === void 0 ? {} : { extraBody: xaiExtraBody }
|
|
29630
29863
|
} : {}
|
|
29631
29864
|
}), active.provider, active.model, active.provider === "xai" ? xaiExtraBody?.aspect_ratio ?? "auto" : quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
29865
|
+
return active.provider === "openai-compat" ? {
|
|
29866
|
+
...value,
|
|
29867
|
+
channel: resolveOpenAICompatChannel(requestConfig, args.channel).id
|
|
29868
|
+
} : value;
|
|
29632
29869
|
}
|
|
29633
29870
|
if (active.provider === "seedream") return saveGenerated(ctx, await editSeedreamImage({
|
|
29634
29871
|
apiKey: credential,
|
|
@@ -29793,6 +30030,7 @@ function imageOutput(verb) {
|
|
|
29793
30030
|
type: "string",
|
|
29794
30031
|
required: true
|
|
29795
30032
|
},
|
|
30033
|
+
channel: { type: "string" },
|
|
29796
30034
|
model: {
|
|
29797
30035
|
type: "string",
|
|
29798
30036
|
required: true
|
|
@@ -29823,6 +30061,7 @@ function imageOutput(verb) {
|
|
|
29823
30061
|
provider: value.provider,
|
|
29824
30062
|
model: value.model,
|
|
29825
30063
|
output: value.output,
|
|
30064
|
+
...value.channel === void 0 ? {} : { channel: value.channel },
|
|
29826
30065
|
...verb === "Edited" ? { operation: "edit" } : {},
|
|
29827
30066
|
...typeof value.savedTo === "string" ? { savedTo: value.savedTo } : {},
|
|
29828
30067
|
...typeof value.seed === "number" ? { seed: value.seed } : {},
|