dsh-image-gen 0.9.2 → 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.
@@ -5,6 +5,7 @@ export interface ConversationImageRevision {
5
5
  attachment: ImageAttachmentRef;
6
6
  prompt: string;
7
7
  provider: StudioProvider;
8
+ channel?: string;
8
9
  model: string;
9
10
  output: string;
10
11
  createdAt: number;
@@ -1,6 +1,7 @@
1
1
  import { type StudioGenerateRequest, type StudioWatermark } from '../shared.js';
2
2
  export interface RegeneratableImage {
3
3
  provider: string;
4
+ channel?: string | undefined;
4
5
  model: string;
5
6
  output?: string | undefined;
6
7
  }
@@ -11,6 +11,7 @@ export interface GalleryItem {
11
11
  prompt: string;
12
12
  /** Generation provider, or 'import' for user-uploaded images. */
13
13
  provider: ImageProvider | 'import';
14
+ channel?: string | undefined;
14
15
  model: string;
15
16
  createdAt: number;
16
17
  aspectRatio?: string | undefined;
@@ -5,6 +5,7 @@ export interface ImageResultPresentation {
5
5
  readonly attachment: ImageAttachmentRef;
6
6
  readonly prompt: string;
7
7
  readonly provider: string;
8
+ readonly channel?: string;
8
9
  readonly model: string;
9
10
  readonly output: string;
10
11
  readonly savedTo?: string;
@@ -9,27 +9,33 @@ export interface SettingsScope<T> {
9
9
  set(field: string, value: unknown): Promise<boolean | void>;
10
10
  }
11
11
  import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
12
- import { type ComfyUIWorkflowEntry, type ImageProvider } from '../shared.js';
12
+ import { type ComfyUIWorkflowEntry, type OpenAICompatChannel, type ImageProvider } from '../shared.js';
13
13
  import { type LocaleService } from './gallery-view.js';
14
14
  import { type ImageResultPresentation } from './image-result-node.js';
15
15
  type Provider = ImageProvider;
16
16
  interface ImageSettings {
17
17
  provider?: Provider;
18
+ cloudTimeoutMs?: number;
18
19
  googleModel?: string;
19
20
  googleEndpoint?: string;
20
21
  googleNative?: boolean;
21
22
  openaiBaseURL?: string;
22
23
  openaiModel?: string;
23
24
  openaiSizes?: Record<string, Record<string, string>>;
25
+ openaiCompatLabel?: string;
24
26
  openaiCompatBaseURL?: string;
25
27
  openaiCompatModel?: string;
28
+ openaiCompatModels?: string[];
29
+ openaiCompatChannels?: OpenAICompatChannel[];
30
+ openaiCompatActiveChannel?: string;
31
+ openaiCompatEditModel?: string;
26
32
  openaiCompatSizes?: Record<string, Record<string, string>>;
27
33
  openaiCompatQuality?: string;
28
34
  openaiCompatQualityOptions?: string[];
29
35
  openaiCompatExtraBody?: Record<string, unknown>;
30
36
  openaiCompatEditSizeMode?: 'auto' | 'selected';
31
37
  /** Edit-request shape for the openai-compat relay (#41); see src/config.ts. */
32
- openaiCompatEditFormat?: 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages';
38
+ openaiCompatEditFormat?: 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages' | 'siliconflowJson';
33
39
  /** Extra JSON fields merged into the JSON edit body; ignored in multipart mode. */
34
40
  openaiCompatEditExtra?: Record<string, unknown>;
35
41
  seedreamBaseURL?: string;
@@ -1,4 +1,4 @@
1
- import type { StudioOption, StudioProvider, StudioProviderProfile } from '../shared.js';
1
+ import { type StudioOption, type StudioProvider, type StudioProviderProfile } from '../shared.js';
2
2
  export interface ComparisonTarget {
3
3
  profile: StudioProviderProfile;
4
4
  ratio: string;
@@ -8,13 +8,13 @@ export interface ComparisonTarget {
8
8
  generationQuality?: string;
9
9
  }
10
10
  /** Map one shared output intent to settings accepted by every target model. */
11
- export declare function buildComparisonTargets(profiles: readonly StudioProviderProfile[], selectedProviders: readonly StudioProvider[], ratio: string, quality: string, output?: {
11
+ export declare function buildComparisonTargets(profiles: readonly StudioProviderProfile[], selectedTargets: readonly string[], ratio: string, quality: string, output?: {
12
12
  resolution: string;
13
13
  generationQuality: string;
14
14
  }): ComparisonTarget[];
15
15
  /** Keep pixel tiers separate from upstream quality values in comparison mode. */
16
16
  export declare function comparisonOutputOptions(profiles: readonly StudioProviderProfile[], ratio: string, kind: 'resolution' | 'generationQuality'): StudioOption[];
17
17
  /** Start with two models, not every configured API, to avoid surprise spend. */
18
- export declare function initialComparisonProviders(profiles: readonly StudioProviderProfile[], activeProvider: StudioProvider): StudioProvider[];
18
+ export declare function initialComparisonProviders(profiles: readonly StudioProviderProfile[], activeProvider: StudioProvider, activeChannel?: string): string[];
19
19
  /** Union of every configured profile's options, for the shared comparison pickers. */
20
20
  export declare function comparisonOptionUnion(profiles: readonly StudioProviderProfile[], key: 'ratioOptions' | 'qualityOptions'): StudioOption[];
@@ -1,4 +1,4 @@
1
- import { type ComfyUIWorkflowEntry, type ImageProvider } from '../shared.js';
1
+ import { type ComfyUIWorkflowEntry, type OpenAICompatChannel, type ImageProvider } from '../shared.js';
2
2
  import type { SettingsScope } from './index.js';
3
3
  import type { LocaleService } from './gallery-view.js';
4
4
  /** Settings fields the pill reads; a structural subset of the full settings shape. */
@@ -6,7 +6,11 @@ export interface PillSettings {
6
6
  provider?: ImageProvider;
7
7
  googleModel?: string;
8
8
  openaiModel?: string;
9
+ openaiCompatLabel?: string;
9
10
  openaiCompatModel?: string;
11
+ openaiCompatModels?: readonly string[];
12
+ openaiCompatChannels?: readonly OpenAICompatChannel[];
13
+ openaiCompatActiveChannel?: string;
10
14
  seedreamModel?: string;
11
15
  dashscopeModel?: string;
12
16
  xaiModel?: string;
@@ -41,9 +45,11 @@ export interface ProviderPillFace {
41
45
  /** Whether the user opted into the composer pill; hidden (not merely inert) when off. */
42
46
  export declare function pillVisible(value: PillSettings | undefined): boolean;
43
47
  /** Model (or ComfyUI workflow) a provider row will use with the persisted settings. */
44
- export declare function pillModelOf(provider: ImageProvider, value: PillSettings | undefined): string;
48
+ export declare function pillModelOf(provider: ImageProvider, value: PillSettings | undefined, channelId?: string): string;
49
+ /** Optional model choices exposed by a provider; single-model providers return an empty list. */
50
+ export declare function pillModelOptionsOf(provider: ImageProvider, value: PillSettings | undefined, channelId?: string): string[];
45
51
  /** One fixed-position provider menu anchored to the pill button. */
46
52
  export declare function ImageProviderPill(props: ProviderPillFace): import("react").JSX.Element | null;
47
53
  /** Styles for the pill and its fixed-position menu; injected by the client entry. */
48
- export declare const PROVIDER_PILL_STYLE = "\n.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:28px}\n.dsh-ig-pill-button{display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 9px 0 8px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:13px;line-height:20px;cursor:pointer;transition:border-color .15s,background .15s,color .15s;-webkit-user-select:none;user-select:none}\n.dsh-ig-pill-button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed,#9ca3af);color:var(--dsw-alias-label-primary,#111827)}\n.dsh-ig-pill-button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:1px}\n.dsh-ig-pill-button:disabled{opacity:.5;cursor:not-allowed}\n.dsh-ig-pill-button-open{border-color:var(--dsw-alias-brand-primary,#4c78ff);color:var(--dsw-alias-label-primary,#111827)}\n.dsh-ig-pill-icon{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}\n.dsh-ig-pill-provider{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-caret{display:inline-flex;flex:none;transition:transform .15s}\n.dsh-ig-pill-caret-open{transform:rotate(180deg)}\n@container (max-width:700px){\n .dsh-ig-pill-caret{display:none}\n .dsh-ig-pill-button{padding:0 7px 0 6px}\n}\n.dsh-ig-pill-menu{position:fixed;z-index:1000;min-width:264px;max-width:min(320px,calc(100vw - 24px));padding:6px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 12px 32px rgba(0,0,0,.16),0 2px 8px rgba(0,0,0,.08);max-height:min(320px,calc(100vh - 48px));overflow-y:auto}\n.dsh-ig-pill-menu-title{padding:6px 8px 4px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary,#6b7280)}\n.dsh-ig-pill-option{display:flex;width:100%;align-items:center;gap:9px;padding:7px 8px;border:0;border-radius:8px;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer}\n.dsh-ig-pill-option:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,#f3f4f6)}\n.dsh-ig-pill-option:disabled{opacity:.6;cursor:default}\n.dsh-ig-pill-option:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:-2px}\n.dsh-ig-pill-option-active{background:var(--dsw-alias-brand-primary-soft,rgba(76,120,255,.10))}\n.dsh-ig-pill-dot{flex:none;width:7px;height:7px;border-radius:50%}\n.dsh-ig-pill-dot-ok{background:#22c55e}\n.dsh-ig-pill-dot-missing{background:#9ca3af}\n.dsh-ig-pill-dot-neutral{background:#4c78ff}\n.dsh-ig-pill-option-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}\n.dsh-ig-pill-option-name{font-size:12.5px;font-weight:500;color:var(--dsw-alias-label-primary,#111827);line-height:1.2}\n.dsh-ig-pill-option-sub{font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-check{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}\n.dsh-ig-pill-error{margin-top:4px;padding:6px 8px;border-radius:8px;background:rgba(239,68,68,.08);color:#ef4444;font-size:11.5px;line-height:1.4;word-break:break-all}\n";
54
+ export declare const PROVIDER_PILL_STYLE = "\n.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:28px}\n.dsh-ig-pill-button{display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 9px 0 8px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:13px;line-height:20px;cursor:pointer;transition:border-color .15s,background .15s,color .15s;-webkit-user-select:none;user-select:none}\n.dsh-ig-pill-button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed,#9ca3af);color:var(--dsw-alias-label-primary,#111827)}\n.dsh-ig-pill-button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:1px}\n.dsh-ig-pill-button:disabled{opacity:.5;cursor:not-allowed}\n.dsh-ig-pill-button-open{border-color:var(--dsw-alias-brand-primary,#4c78ff);color:var(--dsw-alias-label-primary,#111827)}\n.dsh-ig-pill-icon{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}\n.dsh-ig-pill-provider{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-caret{display:inline-flex;flex:none;transition:transform .15s}\n.dsh-ig-pill-caret-open{transform:rotate(180deg)}\n@container (max-width:700px){\n .dsh-ig-pill-caret{display:none}\n .dsh-ig-pill-button{padding:0 7px 0 6px}\n}\n.dsh-ig-pill-menu{position:fixed;z-index:1000;min-width:264px;max-width:min(320px,calc(100vw - 24px));padding:6px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 12px 32px rgba(0,0,0,.16),0 2px 8px rgba(0,0,0,.08);max-height:min(320px,calc(100vh - 48px));overflow-y:auto}\n.dsh-ig-pill-menu-title{padding:6px 8px 4px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary,#6b7280)}\n.dsh-ig-pill-option{display:flex;width:100%;align-items:center;gap:9px;padding:7px 8px;border:0;border-radius:8px;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer}\n.dsh-ig-pill-option:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,#f3f4f6)}\n.dsh-ig-pill-option:disabled{opacity:.6;cursor:default}\n.dsh-ig-pill-option:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:-2px}\n.dsh-ig-pill-option-active{background:var(--dsw-alias-brand-primary-soft,rgba(76,120,255,.10))}\n.dsh-ig-pill-dot{flex:none;width:7px;height:7px;border-radius:50%}\n.dsh-ig-pill-dot-ok{background:#22c55e}\n.dsh-ig-pill-dot-missing{background:#9ca3af}\n.dsh-ig-pill-dot-neutral{background:#4c78ff}\n.dsh-ig-pill-option-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}\n.dsh-ig-pill-option-name{font-size:12.5px;font-weight:500;color:var(--dsw-alias-label-primary,#111827);line-height:1.2}\n.dsh-ig-pill-option-sub{font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-model-option{padding-left:24px}\n.dsh-ig-pill-model-name{flex:1;min-width:0;font-size:12px;color:var(--dsw-alias-label-secondary,#4b5563);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-check{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}\n.dsh-ig-pill-error{margin-top:4px;padding:6px 8px;border-radius:8px;background:rgba(239,68,68,.08);color:#ef4444;font-size:11.5px;line-height:1.4;word-break:break-all}\n";
49
55
  export {};
@@ -1,8 +1,8 @@
1
1
  /** User-facing configuration for supported image providers. */
2
2
  import z from '@deepseek-ai/schemastery';
3
3
  import type { CompatEditFormat } from './openai-compatible.js';
4
- import { ARK_BACKGROUND_MODES, ARK_OUTPUT_FORMATS, DEFAULT_DASHSCOPE_ENDPOINT, DEFAULT_DASHSCOPE_MODEL, DEFAULT_COMFYUI_BASE_URL, DEFAULT_COMFYUI_TIMEOUT_MS, DEFAULT_COMFYUI_WORKFLOW_LABEL, DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, DEFAULT_XAI_BASE_URL, DEFAULT_XAI_MODEL, DEFAULT_ZHIPU_BASE_URL, DEFAULT_ZHIPU_MODEL, DASHSCOPE_API_KEY_ENV, GOOGLE_API_KEY_ENV, IMAGE_PROVIDERS, OPENAI_API_KEY_ENV, OPENAI_COMPAT_API_KEY_ENV, SEEDREAM_API_KEY_ENV, XAI_API_KEY_ENV, ZHIPU_API_KEY_ENV, activeComfyUIWorkflow, resolveComfyUIWorkflows, type ArkBackgroundMode, type ArkOutputFormat, type ArkOutputOptions, type ComfyUIWorkflowEntry, type ImageProvider } from './shared.js';
5
- export { ARK_BACKGROUND_MODES, ARK_OUTPUT_FORMATS, DEFAULT_DASHSCOPE_ENDPOINT, DEFAULT_DASHSCOPE_MODEL, DEFAULT_COMFYUI_BASE_URL, DEFAULT_COMFYUI_TIMEOUT_MS, DEFAULT_COMFYUI_WORKFLOW_LABEL, DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, DEFAULT_XAI_BASE_URL, DEFAULT_XAI_MODEL, DEFAULT_ZHIPU_BASE_URL, DEFAULT_ZHIPU_MODEL, DASHSCOPE_API_KEY_ENV, GOOGLE_API_KEY_ENV, IMAGE_PROVIDERS, OPENAI_API_KEY_ENV, OPENAI_COMPAT_API_KEY_ENV, SEEDREAM_API_KEY_ENV, XAI_API_KEY_ENV, ZHIPU_API_KEY_ENV, activeComfyUIWorkflow, resolveComfyUIWorkflows, type ArkBackgroundMode, type ArkOutputFormat, type ArkOutputOptions, type ComfyUIWorkflowEntry, type ImageProvider, };
4
+ import { ARK_BACKGROUND_MODES, ARK_OUTPUT_FORMATS, DEFAULT_CLOUD_TIMEOUT_MS, DEFAULT_DASHSCOPE_ENDPOINT, DEFAULT_DASHSCOPE_MODEL, DEFAULT_COMFYUI_BASE_URL, DEFAULT_COMFYUI_TIMEOUT_MS, DEFAULT_COMFYUI_WORKFLOW_LABEL, DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, DEFAULT_XAI_BASE_URL, DEFAULT_XAI_MODEL, DEFAULT_ZHIPU_BASE_URL, DEFAULT_ZHIPU_MODEL, DASHSCOPE_API_KEY_ENV, GOOGLE_API_KEY_ENV, IMAGE_PROVIDERS, OPENAI_API_KEY_ENV, OPENAI_COMPAT_API_KEY_ENV, SEEDREAM_API_KEY_ENV, XAI_API_KEY_ENV, ZHIPU_API_KEY_ENV, activeComfyUIWorkflow, resolveComfyUIWorkflows, type OpenAICompatChannel, type ArkBackgroundMode, type ArkOutputFormat, type ArkOutputOptions, type ComfyUIWorkflowEntry, type ImageProvider } from './shared.js';
5
+ export { ARK_BACKGROUND_MODES, ARK_OUTPUT_FORMATS, DEFAULT_CLOUD_TIMEOUT_MS, DEFAULT_DASHSCOPE_ENDPOINT, DEFAULT_DASHSCOPE_MODEL, DEFAULT_COMFYUI_BASE_URL, DEFAULT_COMFYUI_TIMEOUT_MS, DEFAULT_COMFYUI_WORKFLOW_LABEL, DEFAULT_GOOGLE_ENDPOINT, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_SEEDREAM_BASE_URL, DEFAULT_SEEDREAM_MODEL, DEFAULT_XAI_BASE_URL, DEFAULT_XAI_MODEL, DEFAULT_ZHIPU_BASE_URL, DEFAULT_ZHIPU_MODEL, DASHSCOPE_API_KEY_ENV, GOOGLE_API_KEY_ENV, IMAGE_PROVIDERS, OPENAI_API_KEY_ENV, OPENAI_COMPAT_API_KEY_ENV, SEEDREAM_API_KEY_ENV, XAI_API_KEY_ENV, ZHIPU_API_KEY_ENV, activeComfyUIWorkflow, resolveComfyUIWorkflows, type ArkBackgroundMode, type ArkOutputFormat, type ArkOutputOptions, type ComfyUIWorkflowEntry, type ImageProvider, };
6
6
  /** Default workspace subfolder that receives generated image files. */
7
7
  export declare const DEFAULT_WORKSPACE_FOLDER = "dsh-image-gen";
8
8
  /** Google tool-level controls. Aligned with gemini-3.1-flash-image's common set. */
@@ -13,6 +13,7 @@ export type ImageSize = typeof IMAGE_SIZES[number];
13
13
  /** Bundle configuration from the profile patch and the Web settings page. */
14
14
  export interface Config {
15
15
  provider?: ImageProvider;
16
+ cloudTimeoutMs?: number;
16
17
  googleModel?: string;
17
18
  googleEndpoint?: string;
18
19
  /** Use Gemini generateContent instead of Interactions; opt-in for native-only gateways. */
@@ -22,8 +23,16 @@ export interface Config {
22
23
  /** Official model size table: ratio -> resolution tier -> pixels. Empty uses model presets. */
23
24
  openaiSizes?: Record<string, Record<string, string>>;
24
25
  /** OpenAI-compatible relay settings; independent from the official OpenAI row. */
26
+ openaiCompatLabel?: string;
25
27
  openaiCompatBaseURL?: string;
26
28
  openaiCompatModel?: string;
29
+ /** Models offered by the workbench for the legacy default channel. */
30
+ openaiCompatModels?: string[];
31
+ /** Additional named relay channels; the flat fields above remain the default channel. */
32
+ openaiCompatChannels?: OpenAICompatChannel[];
33
+ openaiCompatActiveChannel?: string;
34
+ /** Editing model; empty reuses the generation model. */
35
+ openaiCompatEditModel?: string;
27
36
  /** Default quality; empty preserves the upstream default by omitting the field. */
28
37
  openaiCompatQuality?: string;
29
38
  /** Quality values accepted by this relay; independent of resolution tiers. */
@@ -40,7 +49,7 @@ export interface Config {
40
49
  * `reference_images` field is a JSON array of base64 strings. Defaults to
41
50
  * `multipart`.
42
51
  */
43
- openaiCompatEditFormat?: 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages';
52
+ openaiCompatEditFormat?: 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages' | 'siliconflowJson';
44
53
  /**
45
54
  * Extra JSON fields merged into the JSON edit body last (can override the
46
55
  * built-in defaults), e.g. SenseNova's `watermark`/`prompt_extend`.
@@ -169,11 +178,13 @@ export declare function resolveProvider(config: Config): {
169
178
  provider: 'google-sub';
170
179
  model: string;
171
180
  };
181
+ /** Project one named relay channel onto the legacy flat fields used by all provider adapters. */
182
+ export declare function withOpenAICompatChannel(config: Config, channelId?: string): Config;
172
183
  /**
173
184
  * Apply a per-call provider and/or model override on top of the saved config.
174
185
  * `model` is ignored for ComfyUI, whose per-call equivalent is `workflow`.
175
186
  */
176
- export declare function withProviderOverrides(config: Config, provider?: ImageProvider, model?: string): Config;
187
+ export declare function withProviderOverrides(config: Config, provider?: ImageProvider, model?: string, mode?: 'generate' | 'edit'): Config;
177
188
  /**
178
189
  * One-time migration: when the legacy single OpenAI slot points at a relay
179
190
  * (non-official base URL) and the compat row is empty, move the relay config
@@ -5,11 +5,11 @@ import { type CloudImageProvider } from './shared.js';
5
5
  * never count as configured: the host treats an empty stored value as absent,
6
6
  * and a whitespace-only key is never sent to a provider.
7
7
  */
8
- export declare function resolveApiKey(ctx: Context, provider: CloudImageProvider): Promise<string | undefined>;
8
+ export declare function resolveApiKey(ctx: Context, provider: CloudImageProvider, keyRef?: string): Promise<string | undefined>;
9
9
  /**
10
10
  * Resolve one provider's API key or fail with a user-facing message that says
11
11
  * where to configure it. Passing `tool` selects the Agent-facing English
12
12
  * wording; without it the message is the browser-facing Chinese one used by
13
13
  * the Studio route.
14
14
  */
15
- export declare function requireApiKey(ctx: Context, provider: CloudImageProvider, tool?: string): Promise<string>;
15
+ export declare function requireApiKey(ctx: Context, provider: CloudImageProvider, tool?: string, keyRef?: string): Promise<string>;
@@ -0,0 +1,5 @@
1
+ export declare const UPSTREAM_TIMEOUT_HINT = "\u4E0A\u6E38\u53EF\u80FD\u5DF2\u6263\u8D39\u6216\u4ECD\u5728\u751F\u6210\uFF0C\u8BF7\u5230\u670D\u52A1\u5546\u540E\u53F0\u786E\u8BA4";
2
+ export declare class GenerationTimeoutError extends Error {
3
+ constructor(timeoutMs: number);
4
+ }
5
+ export declare function withGenerationTimeout<T>(timeoutMs: number, signal: AbortSignal, operation: (signal: AbortSignal) => Promise<T>): Promise<T>;
@@ -0,0 +1,3 @@
1
+ /** Live, credential-free instructions for the conversation model's image tools. */
2
+ import { type Config } from './config.js';
3
+ export declare function imageToolContext(config: Config): string;
@@ -32,7 +32,7 @@ export declare function generateOpenAICompatibleImage(input: {
32
32
  arkOptions?: ArkOutputOptions;
33
33
  }): Promise<GeneratedCompatibleImage>;
34
34
  /** How the edits endpoint expects its request body (#41). */
35
- export type CompatEditFormat = 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages' | 'xaiJson' | 'qwenJson';
35
+ export type CompatEditFormat = 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages' | 'siliconflowJson' | 'xaiJson' | 'qwenJson';
36
36
  export declare function editOpenAICompatibleImage(input: {
37
37
  apiKey: string;
38
38
  baseURL: string;
@@ -141,6 +141,48 @@ export declare function isSubscriptionProvider(provider: ImageProvider): provide
141
141
  /** Providers supported by the first browser workbench release. */
142
142
  export declare const CLOUD_IMAGE_PROVIDERS: readonly ["google", "openai", "openai-compat", "seedream", "dashscope", "xai", "zhipu"];
143
143
  export type CloudImageProvider = typeof CLOUD_IMAGE_PROVIDERS[number];
144
+ /** Normalize the relay model list while keeping the legacy default model selectable. */
145
+ export declare function resolveOpenAICompatModels(config: {
146
+ openaiCompatModel?: string;
147
+ openaiCompatModels?: readonly string[];
148
+ }): string[];
149
+ export declare const DEFAULT_OPENAI_COMPAT_CHANNEL_ID = "default";
150
+ export interface OpenAICompatChannel {
151
+ id: string;
152
+ label: string;
153
+ keyRef: string;
154
+ baseURL: string;
155
+ model: string;
156
+ models?: string[];
157
+ editModel?: string;
158
+ quality?: string;
159
+ qualityOptions?: string[];
160
+ extraBody?: Record<string, unknown>;
161
+ editSizeMode?: 'auto' | 'selected';
162
+ editFormat?: 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages' | 'siliconflowJson';
163
+ editExtra?: Record<string, unknown>;
164
+ sizes?: Record<string, Record<string, string>>;
165
+ }
166
+ export interface OpenAICompatChannelSource {
167
+ openaiCompatLabel?: string;
168
+ openaiCompatBaseURL?: string;
169
+ openaiCompatModel?: string;
170
+ openaiCompatModels?: readonly string[];
171
+ openaiCompatEditModel?: string;
172
+ openaiCompatQuality?: string;
173
+ openaiCompatQualityOptions?: readonly string[];
174
+ openaiCompatExtraBody?: Record<string, unknown>;
175
+ openaiCompatEditSizeMode?: 'auto' | 'selected';
176
+ openaiCompatEditFormat?: 'multipart' | 'jsonImageUrlArray' | 'formReferenceImages' | 'siliconflowJson';
177
+ openaiCompatEditExtra?: Record<string, unknown>;
178
+ openaiCompatSizes?: Record<string, Record<string, string>>;
179
+ openaiCompatChannels?: readonly OpenAICompatChannel[];
180
+ openaiCompatActiveChannel?: string;
181
+ }
182
+ export declare function openAICompatKeyRef(channelId: string): string;
183
+ /** The legacy flat fields remain the first channel; additional entries are fully independent. */
184
+ export declare function resolveOpenAICompatChannels(source: OpenAICompatChannelSource): OpenAICompatChannel[];
185
+ export declare function resolveOpenAICompatChannel(source: OpenAICompatChannelSource, channelId?: string): OpenAICompatChannel;
144
186
  /**
145
187
  * Providers the browser workbench can drive: the BYOK cloud set plus the
146
188
  * logged-in subscription channels. ComfyUI stays out; it has its own workflow
@@ -148,8 +190,11 @@ export type CloudImageProvider = typeof CLOUD_IMAGE_PROVIDERS[number];
148
190
  */
149
191
  export declare const STUDIO_PROVIDERS: readonly ["google", "openai", "openai-compat", "seedream", "dashscope", "xai", "zhipu", "chatgpt-sub", "grok-sub", "google-sub"];
150
192
  export type StudioProvider = CloudImageProvider | SubscriptionProvider;
193
+ export declare function studioTargetKey(provider: StudioProvider, channel?: string): string;
151
194
  /** True when the provider is selectable in the browser workbench. */
152
195
  export declare function isStudioProvider(provider: ImageProvider): provider is StudioProvider;
196
+ /** Default timeout for API-key cloud generation in the browser workbench. */
197
+ export declare const DEFAULT_CLOUD_TIMEOUT_MS = 150000;
153
198
  /** Timeout for one subscription image call, shared by the tool path and the
154
199
  * studio route so neither drifts from the other. */
155
200
  export declare const SUBSCRIPTION_TIMEOUT_MS = 300000;
@@ -198,8 +243,14 @@ export interface StudioWatermarkCapability {
198
243
  /** Browser-safe provider description. Credentials and endpoints never cross this boundary. */
199
244
  export interface StudioProviderProfile {
200
245
  provider: StudioProvider;
246
+ /** OpenAI-compatible channel id; absent for every other provider. */
247
+ channel?: string;
201
248
  label: string;
202
249
  model: string;
250
+ /** Separate configured edit model; absent preserves the generation model. */
251
+ editModel?: string;
252
+ /** Selectable generation models; absent means the single default model. */
253
+ modelOptions?: StudioOption[];
203
254
  configured: boolean;
204
255
  supportsEditing: boolean;
205
256
  ratioOptions: StudioOption[];
@@ -216,6 +267,9 @@ export interface StudioProviderProfile {
216
267
  watermarkOptions?: StudioOption[];
217
268
  watermarkCapabilities?: Record<'generate' | 'edit', StudioWatermarkCapability>;
218
269
  }
270
+ /** A model picker/request must use the same operation-specific default. */
271
+ export declare function studioModelForMode(profile: StudioProviderProfile, mode: 'generate' | 'edit'): string;
272
+ export declare function studioModelOptionsForMode(profile: StudioProviderProfile, mode: 'generate' | 'edit'): StudioOption[];
219
273
  export declare function studioWatermarkCapability(profile: StudioProviderProfile | undefined, mode: 'generate' | 'edit'): StudioWatermarkCapability;
220
274
  /** Lightweight workspace description exposed to the client for scoping. */
221
275
  export interface StudioWorkspaceInfo {
@@ -228,6 +282,7 @@ export interface StudioWorkspaceInfo {
228
282
  export interface StudioConfigResponse {
229
283
  providers: StudioProviderProfile[];
230
284
  activeProvider: StudioProvider;
285
+ activeChannel?: string;
231
286
  workspaceRoot?: string | undefined;
232
287
  workspaces?: StudioWorkspaceInfo[] | undefined;
233
288
  }
@@ -246,6 +301,7 @@ export type StudioReference = StudioEncodedReference | StudioAttachmentReference
246
301
  export interface StudioGenerateRequest {
247
302
  mode: 'generate' | 'edit';
248
303
  provider: StudioProvider;
304
+ channel?: string;
249
305
  model: string;
250
306
  prompt: string;
251
307
  ratio: string;
@@ -268,6 +324,7 @@ export interface StudioGeneratedItem {
268
324
  /** One completed workbench request. */
269
325
  export interface StudioGenerateResponse extends StudioGeneratedItem {
270
326
  provider: StudioProvider;
327
+ channel?: string;
271
328
  model: string;
272
329
  prompt: string;
273
330
  createdAt: number;
@@ -5,6 +5,7 @@ export interface StudioRouteDeps {
5
5
  describe(): Promise<StudioConfigResponse>;
6
6
  generate(input: StudioGenerateRequest, signal: AbortSignal): Promise<StudioGenerateResponse>;
7
7
  maxBodyBytes: number;
8
+ timeoutMs?: number;
8
9
  /** Heartbeat cadence for the streaming generation response; tests shrink it. */
9
10
  heartbeatMs?: number;
10
11
  }
@@ -31,4 +31,4 @@ export declare function describeStudio(ctx: Context, config: Config, subscriptio
31
31
  /** Execute one validated browser workbench request using the existing provider adapters. */
32
32
  export declare function generateFromStudio(ctx: Context, config: Config, input: StudioGenerateRequest, signal: AbortSignal, fallbackWorkspaceRoot?: string | undefined, subscriptions?: SubscriptionManager | undefined): Promise<StudioGenerateResponse>;
33
33
  export declare function runPool<T>(tasks: Array<() => Promise<T>>, concurrency?: number): Promise<PromiseSettledResult<T>[]>;
34
- export declare function studioProfile(config: Config, provider: CloudImageProvider, configured: boolean): StudioProviderProfile;
34
+ export declare function studioProfile(config: Config, provider: CloudImageProvider, configured: boolean, channelId?: string): StudioProviderProfile;
@@ -1,6 +1,6 @@
1
1
  /** Same-origin connection probe the settings card runs per provider. */
2
2
  import type { IncomingMessage, ServerResponse } from 'node:http';
3
- import type { Config, ImageProvider } from './config.js';
3
+ import { type Config, type ImageProvider } from './config.js';
4
4
  import type { SubscriptionManager } from './subscription.js';
5
5
  import { type CloudImageProvider } from './shared.js';
6
6
  export { TEST_CONNECTION_ROUTE } from './shared.js';
@@ -71,8 +71,8 @@ export declare function probeComfyUIConnection(config: Config, signal?: AbortSig
71
71
  export declare function probeSubscriptionConnection(provider: 'chatgpt-sub' | 'grok-sub' | 'google-sub', manager: SubscriptionManager): Promise<ProbeResult>;
72
72
  /** Dependencies the test route needs from the plugin entry. */
73
73
  export interface TestRouteDeps {
74
- /** The credential each cloud provider probes with, when one is stored. */
75
- resolveKey(provider: CloudImageProvider): Promise<string | undefined>;
74
+ /** The credential each cloud provider or named relay channel probes with, when one is stored. */
75
+ resolveKey(provider: CloudImageProvider, channel?: string): Promise<string | undefined>;
76
76
  /** The currently authoritative config, including unsaved defaults. */
77
77
  config(): Config;
78
78
  /** The internal subscription account manager for the login-state probes. */