dsh-image-gen 0.9.3 → 0.10.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/README.en.md +454 -454
- package/README.md +454 -454
- package/lib/client.js +927 -356
- package/lib/index.js +276 -61
- 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/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/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/package.json +139 -139
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
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 = {
|
|
@@ -28872,6 +29053,7 @@ function cleanImageToolArgs(args) {
|
|
|
28872
29053
|
const fields = clean;
|
|
28873
29054
|
for (const key of [
|
|
28874
29055
|
"model",
|
|
29056
|
+
"channel",
|
|
28875
29057
|
"size",
|
|
28876
29058
|
"aspect_ratio",
|
|
28877
29059
|
"image_size",
|
|
@@ -28891,6 +29073,11 @@ function providerOverrideOf(value) {
|
|
|
28891
29073
|
if (typeof value !== "string" || !IMAGE_PROVIDERS.includes(value)) throw new Error(`Unsupported provider ${JSON.stringify(value)}. Supported providers: ${IMAGE_PROVIDERS.join(", ")}.`);
|
|
28892
29074
|
return value;
|
|
28893
29075
|
}
|
|
29076
|
+
function configForToolCall(config, provider, channel) {
|
|
29077
|
+
const selected = provider ?? config.provider ?? "google";
|
|
29078
|
+
if (channel !== void 0 && selected !== "openai-compat") throw new Error("The channel argument is only supported with provider=openai-compat.");
|
|
29079
|
+
return selected === "openai-compat" ? withOpenAICompatChannel(config, channel) : config;
|
|
29080
|
+
}
|
|
28894
29081
|
function apply(ctx, config = {}) {
|
|
28895
29082
|
const plainConfig = (source) => {
|
|
28896
29083
|
const out = {};
|
|
@@ -28971,7 +29158,7 @@ function apply(ctx, config = {}) {
|
|
|
28971
29158
|
kind: "exact",
|
|
28972
29159
|
path: TEST_CONNECTION_ROUTE,
|
|
28973
29160
|
handler: (req, res) => serveTestConnection(req, res, {
|
|
28974
|
-
resolveKey: (provider) => resolveApiKey(ctx, provider),
|
|
29161
|
+
resolveKey: (provider, channel) => resolveApiKey(ctx, provider, provider === "openai-compat" ? resolveOpenAICompatChannel(current(), channel).keyRef : void 0),
|
|
28975
29162
|
config: () => current(),
|
|
28976
29163
|
subscriptionManager
|
|
28977
29164
|
})
|
|
@@ -29030,7 +29217,8 @@ function apply(ctx, config = {}) {
|
|
|
29030
29217
|
const fallbackRoot = Array.from(knownWorkspaceRoots)[0] || process.cwd();
|
|
29031
29218
|
return generateFromStudio(ctx, current(), input, signal, fallbackRoot, subscriptionManager);
|
|
29032
29219
|
},
|
|
29033
|
-
maxBodyBytes: Math.ceil(ctx.attachments.imageLimits.maxImageBytes * 1.4 * 5) + 262144
|
|
29220
|
+
maxBodyBytes: Math.ceil(ctx.attachments.imageLimits.maxImageBytes * 1.4 * 5) + 262144,
|
|
29221
|
+
timeoutMs: current().cloudTimeoutMs ?? 15e4
|
|
29034
29222
|
})
|
|
29035
29223
|
}), "dsh-image-gen: studio route");
|
|
29036
29224
|
const serveInspiration = createInspirationRoute();
|
|
@@ -29047,7 +29235,8 @@ function apply(ctx, config = {}) {
|
|
|
29047
29235
|
/** Per-item generation request shared by generate_image and generate_images. */
|
|
29048
29236
|
const generateSingle = async (rawArgs, exec) => {
|
|
29049
29237
|
const args = cleanImageToolArgs(rawArgs);
|
|
29050
|
-
const
|
|
29238
|
+
const requestConfig = configForToolCall(current(), providerOverrideOf(args.provider), args.channel);
|
|
29239
|
+
const active = resolveProvider(withProviderOverrides(requestConfig, providerOverrideOf(args.provider), args.model));
|
|
29051
29240
|
if (active.provider === "comfyui") {
|
|
29052
29241
|
const workflow = selectComfyUIWorkflow(active, args.workflow);
|
|
29053
29242
|
return saveGenerated(ctx, await generateComfyUIImage({
|
|
@@ -29074,11 +29263,12 @@ function apply(ctx, config = {}) {
|
|
|
29074
29263
|
signal: exec.signal
|
|
29075
29264
|
}), active.provider, active.model, "subscription", current(), exec, knownWorkspaceRoots);
|
|
29076
29265
|
}
|
|
29077
|
-
const
|
|
29266
|
+
const runCloud = (operation) => withGenerationTimeout(current().cloudTimeoutMs ?? 15e4, exec.signal, operation);
|
|
29267
|
+
const credential = await requireApiKey(ctx, active.provider, "generate_image", active.provider === "openai-compat" ? resolveOpenAICompatChannel(requestConfig, args.channel).keyRef : void 0);
|
|
29078
29268
|
if (active.provider === "google") {
|
|
29079
29269
|
const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
|
|
29080
29270
|
const imageSize = args.image_size ?? active.imageSize;
|
|
29081
|
-
return saveGenerated(ctx, await generateGoogleImage({
|
|
29271
|
+
return saveGenerated(ctx, await runCloud((signal) => generateGoogleImage({
|
|
29082
29272
|
apiKey: credential,
|
|
29083
29273
|
endpoint: active.endpoint,
|
|
29084
29274
|
native: active.native,
|
|
@@ -29087,20 +29277,20 @@ function apply(ctx, config = {}) {
|
|
|
29087
29277
|
aspectRatio,
|
|
29088
29278
|
imageSize,
|
|
29089
29279
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29090
|
-
signal
|
|
29091
|
-
}), active.provider, active.model, `${aspectRatio}, ${imageSize}`, current(), exec, knownWorkspaceRoots);
|
|
29280
|
+
signal
|
|
29281
|
+
})), active.provider, active.model, `${aspectRatio}, ${imageSize}`, current(), exec, knownWorkspaceRoots);
|
|
29092
29282
|
}
|
|
29093
29283
|
if (active.provider === "dashscope") {
|
|
29094
29284
|
const size = args.size ?? active.imageSize;
|
|
29095
|
-
return saveGenerated(ctx, await generateDashScopeImage({
|
|
29285
|
+
return saveGenerated(ctx, await runCloud((signal) => generateDashScopeImage({
|
|
29096
29286
|
apiKey: credential,
|
|
29097
29287
|
endpoint: active.endpoint,
|
|
29098
29288
|
model: active.model,
|
|
29099
29289
|
prompt: args.prompt,
|
|
29100
29290
|
size,
|
|
29101
29291
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29102
|
-
signal
|
|
29103
|
-
}), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
29292
|
+
signal
|
|
29293
|
+
})), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
29104
29294
|
}
|
|
29105
29295
|
if (active.provider === "xai") {
|
|
29106
29296
|
const extraBody = xaiToolParameters({
|
|
@@ -29108,7 +29298,7 @@ function apply(ctx, config = {}) {
|
|
|
29108
29298
|
...args.image_size !== void 0 ? { imageSize: args.image_size } : {},
|
|
29109
29299
|
...args.size !== void 0 ? { size: args.size } : {}
|
|
29110
29300
|
});
|
|
29111
|
-
return saveGenerated(ctx, await generateOpenAICompatibleImage({
|
|
29301
|
+
return saveGenerated(ctx, await runCloud((signal) => generateOpenAICompatibleImage({
|
|
29112
29302
|
provider: "xai",
|
|
29113
29303
|
apiKey: credential,
|
|
29114
29304
|
baseURL: active.baseURL,
|
|
@@ -29116,15 +29306,15 @@ function apply(ctx, config = {}) {
|
|
|
29116
29306
|
prompt: args.prompt,
|
|
29117
29307
|
extraBody,
|
|
29118
29308
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29119
|
-
signal
|
|
29120
|
-
}), active.provider, active.model, extraBody.aspect_ratio ?? "auto", current(), exec, knownWorkspaceRoots);
|
|
29309
|
+
signal
|
|
29310
|
+
})), active.provider, active.model, extraBody.aspect_ratio ?? "auto", current(), exec, knownWorkspaceRoots);
|
|
29121
29311
|
}
|
|
29122
29312
|
const size = args.size ?? active.imageSize;
|
|
29123
29313
|
const arkOptions = active.provider === "seedream" ? active.arkOptions : void 0;
|
|
29124
29314
|
if (active.provider === "openai") validateOfficialSize(active.model, size);
|
|
29125
|
-
const quality = active.provider === "openai-compat" ? compatRequestQuality(
|
|
29315
|
+
const quality = active.provider === "openai-compat" ? compatRequestQuality(requestConfig, args.quality) : active.provider === "openai" ? args.quality : void 0;
|
|
29126
29316
|
if (active.provider === "openai" && quality !== void 0 && !officialQualityOptions(active.model).some((option) => option.value === quality)) throw new Error("该 OpenAI 模型不支持所选生成质量");
|
|
29127
|
-
|
|
29317
|
+
const value = await saveGenerated(ctx, await runCloud((signal) => generateOpenAICompatibleImage({
|
|
29128
29318
|
provider: active.provider,
|
|
29129
29319
|
apiKey: credential,
|
|
29130
29320
|
baseURL: active.baseURL,
|
|
@@ -29132,11 +29322,15 @@ function apply(ctx, config = {}) {
|
|
|
29132
29322
|
prompt: args.prompt,
|
|
29133
29323
|
size,
|
|
29134
29324
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
29135
|
-
signal
|
|
29325
|
+
signal,
|
|
29136
29326
|
...quality === void 0 ? {} : { quality },
|
|
29137
|
-
...active.provider === "openai-compat" ? { extraBody:
|
|
29327
|
+
...active.provider === "openai-compat" ? { extraBody: requestConfig.openaiCompatExtraBody ?? {} } : {},
|
|
29138
29328
|
...arkOptions === void 0 ? {} : { arkOptions }
|
|
29139
|
-
}), active.provider, active.model, quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
29329
|
+
})), active.provider, active.model, quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
29330
|
+
return active.provider === "openai-compat" ? {
|
|
29331
|
+
...value,
|
|
29332
|
+
channel: resolveOpenAICompatChannel(requestConfig, args.channel).id
|
|
29333
|
+
} : value;
|
|
29140
29334
|
};
|
|
29141
29335
|
ctx.tools.register(defineTool({
|
|
29142
29336
|
name: "generate_image",
|
|
@@ -29168,6 +29362,10 @@ function apply(ctx, config = {}) {
|
|
|
29168
29362
|
type: "string",
|
|
29169
29363
|
description: "Optional model name for this call only, overriding the configured model. Not used by ComfyUI (use workflow instead) nor by the subscription providers (model fixed by the subscription)."
|
|
29170
29364
|
},
|
|
29365
|
+
channel: {
|
|
29366
|
+
type: "string",
|
|
29367
|
+
description: "Optional configured OpenAI-compatible channel id for this call only. Only meaningful with provider=openai-compat; omit to use the active channel."
|
|
29368
|
+
},
|
|
29171
29369
|
aspect_ratio: {
|
|
29172
29370
|
type: "string",
|
|
29173
29371
|
enum: [
|
|
@@ -29243,6 +29441,10 @@ function apply(ctx, config = {}) {
|
|
|
29243
29441
|
type: "string",
|
|
29244
29442
|
description: "Optional model name for this call only, applied to every item."
|
|
29245
29443
|
},
|
|
29444
|
+
channel: {
|
|
29445
|
+
type: "string",
|
|
29446
|
+
description: "Optional configured OpenAI-compatible channel id applied to every item; omit to use the active channel."
|
|
29447
|
+
},
|
|
29246
29448
|
aspect_ratio: {
|
|
29247
29449
|
type: "string",
|
|
29248
29450
|
enum: [
|
|
@@ -29338,6 +29540,7 @@ function apply(ctx, config = {}) {
|
|
|
29338
29540
|
type: "string",
|
|
29339
29541
|
required: true
|
|
29340
29542
|
},
|
|
29543
|
+
channel: { type: "string" },
|
|
29341
29544
|
model: {
|
|
29342
29545
|
type: "string",
|
|
29343
29546
|
required: true
|
|
@@ -29410,6 +29613,7 @@ function apply(ctx, config = {}) {
|
|
|
29410
29613
|
prompt,
|
|
29411
29614
|
...args.provider !== void 0 ? { provider: args.provider } : {},
|
|
29412
29615
|
...args.model !== void 0 ? { model: args.model } : {},
|
|
29616
|
+
...args.channel !== void 0 ? { channel: args.channel } : {},
|
|
29413
29617
|
...args.aspect_ratio !== void 0 ? { aspect_ratio: args.aspect_ratio } : {},
|
|
29414
29618
|
...args.image_size !== void 0 ? { image_size: args.image_size } : {},
|
|
29415
29619
|
...args.size !== void 0 ? { size: args.size } : {},
|
|
@@ -29465,6 +29669,10 @@ function apply(ctx, config = {}) {
|
|
|
29465
29669
|
type: "string",
|
|
29466
29670
|
description: "Optional model name for this call only, overriding the configured model. Not used by ComfyUI (use workflow instead) nor by the subscription providers (model fixed by the subscription)."
|
|
29467
29671
|
},
|
|
29672
|
+
channel: {
|
|
29673
|
+
type: "string",
|
|
29674
|
+
description: "Optional configured OpenAI-compatible channel id for this call only. Only meaningful with provider=openai-compat; omit to use the active channel."
|
|
29675
|
+
},
|
|
29468
29676
|
source: {
|
|
29469
29677
|
type: "string",
|
|
29470
29678
|
enum: ["canvas_selection"],
|
|
@@ -29529,7 +29737,8 @@ function apply(ctx, config = {}) {
|
|
|
29529
29737
|
output: imageOutput("Edited"),
|
|
29530
29738
|
async execute(rawArgs, exec) {
|
|
29531
29739
|
const args = cleanImageToolArgs(rawArgs);
|
|
29532
|
-
const
|
|
29740
|
+
const requestConfig = configForToolCall(current(), providerOverrideOf(args.provider), args.channel);
|
|
29741
|
+
const active = resolveProvider(withProviderOverrides(requestConfig, providerOverrideOf(args.provider), args.model, "edit"));
|
|
29533
29742
|
const canvasSelection = args.source === "canvas_selection";
|
|
29534
29743
|
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
29744
|
const sourceImages = canvasSelection ? await resolveCanvasSelectionReferences({
|
|
@@ -29583,7 +29792,7 @@ function apply(ctx, config = {}) {
|
|
|
29583
29792
|
signal: exec.signal
|
|
29584
29793
|
}), active.provider, active.model, "subscription edit", current(), exec, knownWorkspaceRoots);
|
|
29585
29794
|
}
|
|
29586
|
-
const credential = await requireApiKey(ctx, active.provider, "edit_image");
|
|
29795
|
+
const credential = await requireApiKey(ctx, active.provider, "edit_image", active.provider === "openai-compat" ? resolveOpenAICompatChannel(requestConfig, args.channel).keyRef : void 0);
|
|
29587
29796
|
if (active.provider === "google") {
|
|
29588
29797
|
const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
|
|
29589
29798
|
const imageSize = args.image_size ?? active.imageSize;
|
|
@@ -29603,14 +29812,14 @@ function apply(ctx, config = {}) {
|
|
|
29603
29812
|
const size = args.size ?? active.imageSize;
|
|
29604
29813
|
if (active.provider === "openai" || active.provider === "openai-compat" || active.provider === "xai" || active.provider === "zhipu") {
|
|
29605
29814
|
if (active.provider === "openai") validateOfficialSize(active.model, size);
|
|
29606
|
-
const quality = active.provider === "openai-compat" ? compatRequestQuality(
|
|
29815
|
+
const quality = active.provider === "openai-compat" ? compatRequestQuality(requestConfig, args.quality) : active.provider === "openai" ? args.quality : void 0;
|
|
29607
29816
|
if (active.provider === "openai" && quality !== void 0 && !officialQualityOptions(active.model).some((option) => option.value === quality)) throw new Error("该 OpenAI 模型不支持所选生成质量");
|
|
29608
29817
|
const xaiExtraBody = active.provider === "xai" ? xaiToolParameters({
|
|
29609
29818
|
...args.aspect_ratio !== void 0 ? { aspectRatio: args.aspect_ratio } : {},
|
|
29610
29819
|
...args.image_size !== void 0 ? { imageSize: args.image_size } : {},
|
|
29611
29820
|
...args.size !== void 0 ? { size: args.size } : {}
|
|
29612
29821
|
}) : void 0;
|
|
29613
|
-
|
|
29822
|
+
const value = await saveGenerated(ctx, await editOpenAICompatibleImage({
|
|
29614
29823
|
apiKey: credential,
|
|
29615
29824
|
baseURL: active.baseURL,
|
|
29616
29825
|
model: active.model,
|
|
@@ -29623,12 +29832,16 @@ function apply(ctx, config = {}) {
|
|
|
29623
29832
|
...active.provider === "openai-compat" ? {
|
|
29624
29833
|
editFormat: active.editFormat,
|
|
29625
29834
|
editExtra: active.editExtra,
|
|
29626
|
-
editSizeMode:
|
|
29835
|
+
editSizeMode: requestConfig.openaiCompatEditSizeMode ?? "auto"
|
|
29627
29836
|
} : active.provider === "xai" ? {
|
|
29628
29837
|
editFormat: "xaiJson",
|
|
29629
29838
|
...xaiExtraBody === void 0 ? {} : { extraBody: xaiExtraBody }
|
|
29630
29839
|
} : {}
|
|
29631
29840
|
}), active.provider, active.model, active.provider === "xai" ? xaiExtraBody?.aspect_ratio ?? "auto" : quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
29841
|
+
return active.provider === "openai-compat" ? {
|
|
29842
|
+
...value,
|
|
29843
|
+
channel: resolveOpenAICompatChannel(requestConfig, args.channel).id
|
|
29844
|
+
} : value;
|
|
29632
29845
|
}
|
|
29633
29846
|
if (active.provider === "seedream") return saveGenerated(ctx, await editSeedreamImage({
|
|
29634
29847
|
apiKey: credential,
|
|
@@ -29793,6 +30006,7 @@ function imageOutput(verb) {
|
|
|
29793
30006
|
type: "string",
|
|
29794
30007
|
required: true
|
|
29795
30008
|
},
|
|
30009
|
+
channel: { type: "string" },
|
|
29796
30010
|
model: {
|
|
29797
30011
|
type: "string",
|
|
29798
30012
|
required: true
|
|
@@ -29823,6 +30037,7 @@ function imageOutput(verb) {
|
|
|
29823
30037
|
provider: value.provider,
|
|
29824
30038
|
model: value.model,
|
|
29825
30039
|
output: value.output,
|
|
30040
|
+
...value.channel === void 0 ? {} : { channel: value.channel },
|
|
29826
30041
|
...verb === "Edited" ? { operation: "edit" } : {},
|
|
29827
30042
|
...typeof value.savedTo === "string" ? { savedTo: value.savedTo } : {},
|
|
29828
30043
|
...typeof value.seed === "number" ? { seed: value.seed } : {},
|