Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/desktop/src/bun/remote/remote-runtime-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {
ArkImageGenerationConfig,
ImageGenerationConfig,
AgentEvent,
BuiltinTool,
CustomModel,
Expand Down Expand Up @@ -135,7 +135,7 @@ export class RemoteRuntimeClient implements RuntimeClient {
}

createSubagentThread(
input: Parameters<RuntimeClient["createSubagentThread"]>[0],
input: Parameters<RuntimeClient["createSubagentThread"]>[0]
) {
return this._rpc<
Awaited<ReturnType<RuntimeClient["createSubagentThread"]>>
Expand Down Expand Up @@ -315,7 +315,7 @@ export class RemoteRuntimeClient implements RuntimeClient {
api?:
"anthropic-messages" | "openai-completions" | "openai-responses" | null;
icon?: string | null;
imageGeneration?: ArkImageGenerationConfig;
imageGeneration?: ImageGenerationConfig;
}) {
return this._rpc<ModelProviderGroup[]>("models.updateProvider", input);
}
Expand Down
53 changes: 40 additions & 13 deletions apps/desktop/src/components/settings/models-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import {
formatProviderProfileLabel,
getArkImageModelDefinitions,
type ArkImageGenerationConfig,
getImageModelDefinitions,
type CustomModel,
type ImageGenerationApi,
type ImageGenerationConfig,
type ModelProviderGroup,
type ProviderProfile,
type SeedreamImageModelDefinition,
Expand Down Expand Up @@ -499,9 +501,7 @@ function ProviderListItem({
role="button"
tabIndex={0}
aria-label={`Select ${provider.name} provider`}
aria-expanded={
provider.profiles.length > 1 ? expanded : undefined
}
aria-expanded={provider.profiles.length > 1 ? expanded : undefined}
onClick={handleGroupClick}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
Expand Down Expand Up @@ -613,7 +613,7 @@ function ProviderListItem({
<button
type="button"
aria-label={`${profile.name} profile actions`}
className="text-muted-foreground hover:text-foreground mr-1 inline-flex size-5 shrink-0 items-center justify-center rounded opacity-0 hover:bg-accent group-hover/profile:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100"
className="text-muted-foreground hover:text-foreground hover:bg-accent mr-1 inline-flex size-5 shrink-0 items-center justify-center rounded opacity-0 group-hover/profile:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100"
>
<MoreHorizontal className="size-3.5" />
</button>
Expand Down Expand Up @@ -1073,8 +1073,8 @@ function ProviderEditor({
</div>
) : null}

{provider.id === "ark" && canManageModels ? (
<_ArkImageGenerationEditor provider={provider} />
{(provider.id === "ark" || !isBuiltin) && canManageModels ? (
<_ImageGenerationEditor provider={provider} />
) : null}
</div>
</ScrollArea>
Expand Down Expand Up @@ -1173,8 +1173,8 @@ function _ProviderProfileEditor({
</div>
{isOfficial ? (
<div className="list-item">
Leave it blank to use the official {provider.name}{" "}
environment variable
Leave it blank to use the official {provider.name} environment
variable
</div>
) : null}
</div>
Expand Down Expand Up @@ -1214,15 +1214,18 @@ function _ProviderProfileEditor({
);
}

/** Chat-model-parity inventory management for Ark image models. */
function _ArkImageGenerationEditor({
/** Chat-model-parity inventory management for provider-owned image models. */
function _ImageGenerationEditor({
provider,
}: {
provider: ModelProviderGroup;
}) {
const updateProvider = useUpdateProvider();
const config = provider.imageGeneration ?? {};
const models = getArkImageModelDefinitions(config);
const models =
provider.id === "ark"
? getArkImageModelDefinitions(config)
: getImageModelDefinitions(config);
const disabledModels = new Set(config.disabledModels ?? []);
const enabledModels = models.filter((model) => !disabledModels.has(model.id));
const customModels = new Set((config.models ?? []).map((model) => model.id));
Expand All @@ -1240,7 +1243,7 @@ function _ArkImageGenerationEditor({
return true;
});

const update = (imageGeneration: ArkImageGenerationConfig) => {
const update = (imageGeneration: ImageGenerationConfig) => {
void updateProvider(provider.id, { imageGeneration }).catch((error) => {
toast.error("Failed to update image generation", {
description:
Expand Down Expand Up @@ -1307,6 +1310,30 @@ function _ArkImageGenerationEditor({

return (
<>
{provider.id !== "ark" ? (
<div className="flex flex-col gap-2">
<span className="text-sm font-medium">Image API type</span>
<Select
value={config.api ?? "openai-images"}
onValueChange={(api) =>
update({ ...config, api: api as ImageGenerationApi })
}
>
<SelectTrigger
className="w-full"
aria-label={`${provider.name} image API type`}
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="openai-images">OpenAI Images</SelectItem>
<SelectItem value="openai-images-extra-body">
OpenAI Images with extra_body
</SelectItem>
</SelectContent>
</Select>
</div>
) : null}
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">Image models</span>
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/shared/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {
ArkImageGenerationConfig,
ImageGenerationConfig,
AgentEvent,
AgentStreamRequest,
BuiltinTool,
Expand Down Expand Up @@ -208,7 +208,7 @@ export interface DesktopRPCType {
| "openai-responses"
| null;
icon?: string | null;
imageGeneration?: ArkImageGenerationConfig;
imageGeneration?: ImageGenerationConfig;
};
response: ModelProviderGroup[];
};
Expand Down
64 changes: 52 additions & 12 deletions packages/core/src/types/models/image-generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export const SEEDREAM_IMAGE_SIZES = ["1K", "2K", "3K", "4K"] as const;

export type SeedreamImageSize = (typeof SEEDREAM_IMAGE_SIZES)[number];

export interface SeedreamImageModelDefinition {
export interface ImageModelDefinition {
id: string;
name: string;
supportedSizes: readonly SeedreamImageSize[];
Expand All @@ -11,6 +11,9 @@ export interface SeedreamImageModelDefinition {
icon?: string;
}

/** @deprecated Use ImageModelDefinition. */
export type SeedreamImageModelDefinition = ImageModelDefinition;

/** Curated Ark Seedream catalog used by settings and runtime validation. */
export const SEEDREAM_IMAGE_MODELS = [
{
Expand All @@ -37,25 +40,33 @@ export const SEEDREAM_IMAGE_MODELS = [
supportedSizes: ["1K", "2K", "4K"],
defaultSize: "2K",
},
] as const satisfies readonly SeedreamImageModelDefinition[];
] as const satisfies readonly ImageModelDefinition[];

export type SeedreamImageModelId = (typeof SEEDREAM_IMAGE_MODELS)[number]["id"];

export interface ArkImageGenerationConfig {
/** User-added Ark image models layered on top of the curated catalog. */
models?: SeedreamImageModelDefinition[];
export type ImageGenerationApi =
"ark-images" | "openai-images" | "openai-images-extra-body";

export interface ImageGenerationConfig {
/** Request protocol. Ark defaults to its native API; custom providers use OpenAI Images. */
api?: ImageGenerationApi;
/** User-added image models layered on top of an optional provider catalog. */
models?: ImageModelDefinition[];
/** Image-model ids disabled in Settings. Absent means every model is enabled. */
disabledModels?: string[];
}

/** @deprecated Use ImageGenerationConfig. */
export type ArkImageGenerationConfig = ImageGenerationConfig;

/** Per-Thread configuration owned by one `generate_image` tool instance. */
export interface GenerateImageToolConfig {
model: string;
size: SeedreamImageSize;
watermark: boolean;
}

export const DEFAULT_ARK_IMAGE_GENERATION_CONFIG: ArkImageGenerationConfig = {};
export const DEFAULT_ARK_IMAGE_GENERATION_CONFIG: ImageGenerationConfig = {};

/** Find one curated Seedream model definition by its stable Ark model id. */
export function getSeedreamImageModelDefinition(
Expand All @@ -66,21 +77,40 @@ export function getSeedreamImageModelDefinition(

/** Merge the curated Seedream catalog with user-added Ark image models. */
export function getArkImageModelDefinitions(
config: ArkImageGenerationConfig
): readonly SeedreamImageModelDefinition[] {
return [...SEEDREAM_IMAGE_MODELS, ...(config.models ?? [])];
config: ImageGenerationConfig
): readonly ImageModelDefinition[] {
return getImageModelDefinitions(config, SEEDREAM_IMAGE_MODELS);
}

/** Merge a provider's optional built-in catalog with its user-owned models. */
export function getImageModelDefinitions(
config: ImageGenerationConfig,
catalog: readonly ImageModelDefinition[] = []
): readonly ImageModelDefinition[] {
return [...catalog, ...(config.models ?? [])];
}

/** Resolve a curated or user-added Ark image model by id. */
export function getArkImageModelDefinition(
config: ArkImageGenerationConfig,
config: ImageGenerationConfig,
modelId: string
): SeedreamImageModelDefinition | undefined {
return getArkImageModelDefinitions(config).find(
(model) => model.id === modelId
);
}

/** Resolve one configured image model using the owning provider's catalog. */
export function getImageModelDefinition(
config: ImageGenerationConfig,
modelId: string,
catalog: readonly ImageModelDefinition[] = []
): ImageModelDefinition | undefined {
return getImageModelDefinitions(config, catalog).find(
(model) => model.id === modelId
);
}

/** Whether a size preset is supported by the selected Seedream model. */
export function isSeedreamImageSizeSupported(
modelId: string,
Expand All @@ -95,12 +125,22 @@ export function isSeedreamImageSizeSupported(

/** Whether a curated or user-added Ark model supports a size preset. */
export function isArkImageSizeSupported(
config: ArkImageGenerationConfig,
config: ImageGenerationConfig,
modelId: string,
size: string
): boolean {
return isImageSizeSupported(config, modelId, size, SEEDREAM_IMAGE_MODELS);
}

/** Whether a configured provider image model supports a size preset. */
export function isImageSizeSupported(
config: ImageGenerationConfig,
modelId: string,
size: string,
catalog: readonly ImageModelDefinition[] = []
): boolean {
return Boolean(
getArkImageModelDefinition(config, modelId)?.supportedSizes.some(
getImageModelDefinition(config, modelId, catalog)?.supportedSizes.some(
(supported) => supported === size
)
);
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/types/models/provider-group.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as pi from "@earendil-works/pi-ai";

import type { ArkImageGenerationConfig } from "./image-generation";
import type { ImageGenerationConfig } from "./image-generation";
import type { ProviderProfile } from "./provider-profile";

/**
Expand All @@ -25,8 +25,8 @@ export interface ModelProviderGroup {
disabledModels?: string[];
/** Ids of the user-added custom models within this provider. */
customModels?: string[];
/** Native Ark image-model inventory. Present only on the Ark provider. */
imageGeneration?: ArkImageGenerationConfig;
/** Provider-owned image-model inventory and request protocol. */
imageGeneration?: ImageGenerationConfig;
websiteLink?: string;
websiteURL?: string;
apiKeyURL?: string;
Expand Down
Loading