chore: commit workspace updates

This commit is contained in:
ddCat committed 2026-09-14 01:11:56 +08:00
1 parent 01a2d171f5
commit 9727656b2b
491 files changed
+12826 -72249

No files matched your search

+10 -99
View File
@@ -3,14 +3,11 @@ import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import { nanoid } from "nanoid";
import { projectDesktopLocalChannelRuntime } from "@/lib/desktop-local-channel";
import { scopedLocalStorage } from "@/lib/user-scope";
import { modelProtocolCapability, normalizeModelProtocol, type ModelProtocol } from "@/lib/model-protocols";
import { normalizeVideoDuration, normalizeVideoResolution } from "@/lib/video-generation-options";
import { workflowFieldRole, workflowFieldSafeToOverride, workflowVideoFieldsFromJson, type ModelCapabilityConfig } from "@/lib/model-capabilities";
import { useLocalDreaminaModelStore } from "@/stores/use-local-dreamina-model-store";
import { useUserStore } from "@/stores/use-user-store";
import type { DreaminaLocalModel } from "@/services/local-dreamina-model-catalog";
import type { CapabilitySpec, PublicLogicalModelPriceTier } from "@/services/api/logical-models";
export type ApiCallFormat = "openai" | "gemini" | "claude";
@@ -336,30 +333,11 @@ export type RunningHubConfig = {
workflows: RunningHubWorkflow[];
};
export type ComfyBridgeWorkflow = {
workflowId: string;
title?: string;
capability: "image" | "video" | "audio";
fields?: WorkflowFieldMapping[];
workflowJson?: Record<string, unknown>;
workflowGraph?: WorkflowGraphPreview;
};
export type WorkflowGraphPreview = {
nodes: Array<{ id: string; title?: string; classType?: string }>;
edges: Array<{ from: string; to: string }>;
};
export type ComfyBridgeConfig = {
enabled: boolean;
bridgeId: string;
comfyUrl: string;
workflowDir: string;
workflowId: string;
capability: "image" | "video" | "audio";
workflows: ComfyBridgeWorkflow[];
};
// 兼容仍在使用旧目录标识的会话恢复和模型选择器。
export const PUBLIC_MODEL_CATALOG_ID = "managed";
@@ -369,7 +347,6 @@ export type ModelChannel = {
publicAlias?: string;
sortOrder?: number;
baseUrl: string;
allowLocalChannel?: boolean;
apiKey: string;
secretKey?: string;
headers?: ChannelHeader[];
@@ -403,20 +380,17 @@ export type ModelChannel = {
logicalPriceTiers?: PublicLogicalModelPriceTier[];
defaultOptions?: Record<string, unknown>;
}>;
transport?: "backend-channel" | "local-runtime";
localModels?: DreaminaLocalModel[];
};
export type AiConfig = {
channelMode: "remote" | "local";
channelMode: "remote";
baseUrl: string;
apiKey: string;
apiFormat: ApiCallFormat;
channels: ModelChannel[];
runningHub: RunningHubConfig;
comfyBridge: ComfyBridgeConfig;
/** 仅用于单次生成任务路由,不属于全局渠道启用状态。 */
taskWorkflowProvider?: "model" | "runninghub" | "comfyui";
taskWorkflowProvider?: "model" | "runninghub";
model: string;
imageModel: string;
videoModel: string;
@@ -452,14 +426,13 @@ const GEMINI_BASE_URL = "https://generativelanguage.googleapis.com";
const LEGACY_DEFAULT_MODEL_NAMES = new Set(["gpt-image-2", "grok-imagine-video", "gpt-5.5", "gpt-4o-mini-tts"]);
export const defaultConfig: AiConfig = {
channelMode: "local",
channelMode: "remote",
baseUrl: OPENAI_BASE_URL,
apiKey: "",
apiFormat: "openai",
// 创作端模型目录只能来自后台公开逻辑模型和用户自定义渠道,不能内置供应商模型。
channels: [],
runningHub: { enabled: false, baseUrl: "https://www.runninghub.cn", apiKey: "", walletApiKey: "", uploadApiKey: "", useWallet: false, capability: "image", selectedKind: "workflow", workflowId: "", workflows: [] },
comfyBridge: { enabled: false, bridgeId: "", comfyUrl: "http://127.0.0.1:8188", workflowDir: "D:\\ComfyUI\\workflows", workflowId: "", capability: "image", workflows: [] },
taskWorkflowProvider: "model",
model: "",
imageModel: "",
@@ -576,8 +549,6 @@ export function filterModelsByCapability(models: string[], capability?: ModelCap
const decoded = decodeChannelModel(model);
const channel = decoded ? channels?.find((item) => item.id === decoded.channelId) : undefined;
const modelName = decoded?.model || modelOptionName(model);
const local = channel?.localModels?.find((item) => item.id === modelName);
if (local) return local.modality === capability;
const costEntry = channel?.modelCosts?.find((item) => item.model === modelName);
// 协议层优先级最高:协议决定 API 端点,明确属于其他能力时直接排除,
// 防止用户将 video/image/audio 协议的模型误标为 text 后混入文本下拉。
@@ -613,11 +584,7 @@ function isAiConfigReady(config: AiConfig, model: string) {
const key = config.runningHub.apiKey;
return Boolean(config.runningHub.enabled && config.runningHub.baseUrl.trim() && key.trim() && config.runningHub.workflowId.trim());
}
if (config.taskWorkflowProvider === "comfyui") {
return Boolean(config.comfyBridge.enabled && config.comfyBridge.bridgeId.trim() && config.comfyBridge.workflowId.trim());
}
const channel = resolveModelChannel(config, model);
if (channel.transport === "local-runtime") return channel.enabled !== false && Boolean(channel.localModels?.some((item) => item.id === modelOptionName(model)));
return Boolean(model.trim() && channel.baseUrl.trim() && channel.apiKey.trim());
}
@@ -685,21 +652,6 @@ export function normalizeConfigSnapshot(snapshot: ConfigStoreSnapshot | undefine
: [];
const runningHubWorkflowID = String(persistedRunningHub?.workflowId || "").trim();
const runningHubSelectedKind = persistedRunningHub?.selectedKind ? normalizeRunningHubWorkflowKind(persistedRunningHub.selectedKind) : normalizeRunningHubWorkflowKind(runningHubWorkflows.find((item) => item.workflowId === runningHubWorkflowID)?.kind);
const persistedComfyBridge = persistedConfig.comfyBridge;
const comfyBridgeCapability = normalizeRunningHubCapability(persistedComfyBridge?.capability, defaultConfig.comfyBridge.capability);
const comfyBridgeWorkflows = Array.isArray(persistedComfyBridge?.workflows)
? persistedComfyBridge.workflows
.filter((item): item is ComfyBridgeWorkflow => Boolean(item && typeof item === "object" && String(item.workflowId || "").trim()))
.map((item) => {
const capability = normalizeRunningHubCapability(item.capability, comfyBridgeCapability);
return {
...item,
workflowId: String(item.workflowId || "").trim(),
capability,
fields: normalizeSavedWorkflowFields(item, capability),
};
})
: [];
const config = {
...defaultConfig,
...persistedConfig,
@@ -712,13 +664,6 @@ export function normalizeConfigSnapshot(snapshot: ConfigStoreSnapshot | undefine
workflowId: runningHubWorkflowID,
workflows: runningHubWorkflows,
},
comfyBridge: {
...defaultConfig.comfyBridge,
...(persistedComfyBridge || {}),
capability: comfyBridgeCapability,
workflowId: String(persistedComfyBridge?.workflowId || "").trim(),
workflows: comfyBridgeWorkflows,
},
};
const hasPersistedChannels = Array.isArray(persistedConfig.channels);
if (!hasPersistedChannels) config.channels = [];
@@ -732,7 +677,7 @@ export function normalizeConfigSnapshot(snapshot: ConfigStoreSnapshot | undefine
return {
config: {
...config,
channelMode: "local" as const,
channelMode: "remote" as const,
apiFormat: normalizeApiFormat(config.apiFormat),
channels,
models,
@@ -770,45 +715,15 @@ function normalizeSelectedModel(value: string, channels: ModelChannel[], options
export function useEffectiveConfig() {
const config = useConfigStore((state) => state.config);
const customChannelsEnabled = useUserStore((state) => state.features.customChannelsEnabled);
const catalogState = useLocalDreaminaModelStore((state) => state.state);
const dreaminaModels = useLocalDreaminaModelStore((state) => state.models);
return useMemo(() => effectiveConfigWithDreamina(effectiveConfigForCustomChannels(config, customChannelsEnabled), catalogState, dreaminaModels), [catalogState, config, customChannelsEnabled, dreaminaModels]);
return useMemo(() => effectiveConfigForCustomChannels(config, customChannelsEnabled), [config, customChannelsEnabled]);
}
export function effectiveConfigForCustomChannels(config: AiConfig, customChannelsEnabled: boolean): AiConfig {
if (customChannelsEnabled) return config;
const channels = config.channels.filter((channel) => channel.scope === "system" || channel.transport === "local-runtime");
const channels = config.channels.filter((channel) => channel.scope === "system");
return normalizeConfigSnapshot({ config: { ...config, channels } }).config;
}
export function effectiveConfigWithDreamina(config: AiConfig, catalogState: "idle" | "loading" | "ready" | "error", dreaminaModels: DreaminaLocalModel[]): AiConfig {
if (catalogState !== "ready" || !dreaminaModels.length) return { ...config, channelMode: "local" };
const channel: ModelChannel = {
id: "local:dreamina-cli",
name: "官方即梦 CLI",
baseUrl: "",
apiKey: "",
apiFormat: "openai",
models: dreaminaModels.map((item) => item.id),
scope: "user",
enabled: true,
transport: "local-runtime",
localModels: dreaminaModels,
};
const channels = [...config.channels.filter((item) => item.id !== channel.id), channel];
const models = modelOptionsFromChannels(channels);
return {
...config,
channelMode: "local",
channels,
models,
imageModels: filterModelsByCapability(models, "image", channels),
videoModels: filterModelsByCapability(models, "video", channels),
textModels: filterModelsByCapability(models, "text", channels),
audioModels: filterModelsByCapability(models, "audio", channels),
};
}
export function createModelChannel(channel?: Partial<ModelChannel>): ModelChannel {
const apiFormat = normalizeApiFormat(channel?.apiFormat);
const interfaceType = normalizeChannelInterfaceType(channel?.interfaceType);
@@ -818,7 +733,6 @@ export function createModelChannel(channel?: Partial<ModelChannel>): ModelChanne
name: channel?.name?.trim() || "新渠道",
sortOrder: channel?.sortOrder ?? 0,
baseUrl: providedBaseUrl || (interfaceType ? defaultBaseUrlForChannelInterface(interfaceType) : defaultBaseUrlForApiFormat(apiFormat)),
allowLocalChannel: channel?.allowLocalChannel === true,
apiKey: channel?.apiKey || "",
secretKey: channel?.secretKey || "",
headers: Array.isArray(channel?.headers) ? channel.headers.map((header) => ({ name: String(header.name || ""), value: String(header.value || "") })) : [],
@@ -842,8 +756,6 @@ export function isChannelModelValue(value: string) {
}
export function decodeChannelModel(value: string) {
const local = /^local:dreamina-cli:([A-Za-z0-9][A-Za-z0-9._:-]{0,119})$/.exec(value.trim());
if (local) return { channelId: "local:dreamina-cli", model: local[1] };
const index = value.indexOf(CHANNEL_MODEL_SEPARATOR);
if (index < 0) return null;
return { channelId: value.slice(0, index), model: value.slice(index + CHANNEL_MODEL_SEPARATOR.length) };
@@ -883,7 +795,7 @@ export function modelOptionsFromChannels(channels: ModelChannel[]) {
.map(normalizeRawModelName)
.filter(Boolean)
.filter((model) => channel.scope !== "system" || hasSystemModelPrice(channel, model))
.map((model) => (channel.transport === "local-runtime" ? `local:dreamina-cli:${model}` : encodeChannelModel(channel.id, model))),
.map((model) => encodeChannelModel(channel.id, model)),
),
);
}
@@ -934,7 +846,7 @@ export function logicalModelIDForConfig(config: AiConfig) {
}
export function channelConnectionSignature(channel: ModelChannel) {
return [channel.baseUrl.trim(), channel.apiKey.trim(), channel.secretKey?.trim() || "", channel.apiFormat, channel.interfaceType || "auto", channel.allowLocalChannel === true ? "local:1" : "local:0", JSON.stringify(channel.headers || [])].join("\n");
return [channel.baseUrl.trim(), channel.apiKey.trim(), channel.secretKey?.trim() || "", channel.apiFormat, channel.interfaceType || "auto", JSON.stringify(channel.headers || [])].join("\n");
}
export function resolveModelRequestConfig(config: AiConfig, value: string) {
@@ -942,18 +854,17 @@ export function resolveModelRequestConfig(config: AiConfig, value: string) {
const model = modelOptionName(value || config.model);
const modelProtocol = channel.modelCosts?.find((item) => item.model === model)?.protocol;
const interfaceType = modelProtocol || channel.interfaceType;
return projectDesktopLocalChannelRuntime({
return {
...config,
model,
baseUrl: channel.baseUrl,
allowLocalChannel: channel.allowLocalChannel === true,
apiKey: channel.apiKey,
secretKey: channel.secretKey,
headers: channel.headers,
apiFormat: interfaceType ? (interfaceType === "gemini-veo" || interfaceType === "gemini-image" ? ("gemini" as const) : interfaceType === "claude-api" ? ("claude" as const) : ("openai" as const)) : channel.apiFormat,
interfaceType,
channelId: channel.scope === "system" ? channel.id : "",
});
};
}
function normalizeChannels(config: AiConfig, ensureDefault = true) {