feat(workflow): 工作流提供方 - 集成 RunningHub 与 ComfyUI Bridge

This commit is contained in:
yepwait
2026-08-26 15:02:24 +08:00
committed by ddCat
parent 6141bd688c
commit 86fc3fda2f
82 changed files with 11405 additions and 248 deletions

View File

@@ -0,0 +1,100 @@
import { normalizeRunningHubCapability, type AiConfig, type RunningHubCapability, type RunningHubWorkflowKind, type WorkflowFieldMapping } from "@/stores/use-config-store";
export type GenerationWorkflowMode = "text" | "image" | "video" | "audio";
export type GenerationWorkflowExecution = {
provider: "runninghub" | "comfyui-bridge";
kind: RunningHubWorkflowKind;
capability: RunningHubCapability;
interfaceType: string;
name: string;
taskModel: string;
providerModel: string;
workflowId: string;
webappId: string;
workflowJson: Record<string, unknown>;
workflowFields: WorkflowFieldMapping[];
bridgeId: string;
};
// 画布样式计划、任务记录和后端协议必须共用同一个工作流选择结果,避免再次回退到普通模型。
export function resolveGenerationWorkflowExecution(config: AiConfig, mode: GenerationWorkflowMode): GenerationWorkflowExecution | null {
const workflowProvider = mode === "text" ? "model" : config.taskWorkflowProvider || "model";
if (workflowProvider === "model") return null;
if (workflowProvider === "runninghub") {
const runningHub = config.runningHub;
const workflowId = runningHub.workflowId.trim();
const kind = runningHub.selectedKind === "app" ? "app" : "workflow";
// RunningHub 工作流的参数拉取、提交和轮询固定使用积分 API Key;企业级 Key 只用于素材上传。
const apiKey = runningHub.apiKey;
if (!runningHub.enabled || !runningHub.baseUrl.trim() || !apiKey.trim() || !workflowId) {
throw new Error("RunningHub 工作流配置不完整,请填写积分 API Key 并选择已保存条目");
}
const workflow = runningHub.workflows.find((item) => item.workflowId.trim() === workflowId && (item.kind === "app" ? "app" : "workflow") === kind);
if (!workflow) throw new Error("当前 RunningHub 工作流条目不存在,请重新选择已保存条目");
const capability = normalizeRunningHubCapability(workflow.capability, normalizeRunningHubCapability(runningHub.capability));
assertWorkflowCapability("RunningHub", capability, mode);
const webappId = kind === "app" ? (workflow.webappId || workflow.workflowId).trim() : "";
const name = workflow.title?.trim() || webappId || workflowId;
return {
provider: "runninghub",
kind,
capability,
interfaceType: `runninghub-workflow-${capability}`,
name,
taskModel: workflowTaskModel("RunningHub · ", name),
providerModel: workflowId,
workflowId: kind === "workflow" ? workflowId : "",
webappId,
workflowJson: workflow.workflowJson || {},
workflowFields: workflow.fields || [],
bridgeId: "",
};
}
const comfyBridge = config.comfyBridge;
const workflowId = comfyBridge.workflowId.trim();
if (!comfyBridge.enabled || !comfyBridge.bridgeId.trim() || !workflowId) {
throw new Error("ComfyUI Bridge 配置不完整,请检查 Bridge 在线状态和已保存工作流");
}
const workflow = comfyBridge.workflows.find((item) => item.workflowId.trim() === workflowId);
if (!workflow) throw new Error("当前 ComfyUI 工作流条目不存在,请重新选择已保存条目");
const capability = normalizeRunningHubCapability(workflow.capability, normalizeRunningHubCapability(comfyBridge.capability));
assertWorkflowCapability("ComfyUI", capability, mode);
const name = workflow.title?.trim() || workflowId;
return {
provider: "comfyui-bridge",
kind: "workflow",
capability,
interfaceType: `comfyui-bridge-${capability}`,
name,
taskModel: workflowTaskModel("ComfyUI · ", name),
providerModel: workflowId,
workflowId,
webappId: "",
workflowJson: workflow.workflowJson || {},
workflowFields: workflow.fields || [],
bridgeId: comfyBridge.bridgeId.trim(),
};
}
function assertWorkflowCapability(providerName: string, capability: RunningHubCapability, mode: GenerationWorkflowMode) {
if (capability === mode) return;
const capabilityName = workflowCapabilityName(capability);
const modeName = workflowCapabilityName(mode);
throw new Error(`${providerName} 已保存条目的用途为${capabilityName},当前节点是${modeName}生成,请切换匹配的工作流`);
}
function workflowCapabilityName(value: GenerationWorkflowMode) {
if (value === "video") return "视频";
if (value === "audio") return "音频";
if (value === "text") return "文本";
return "图片";
}
function workflowTaskModel(prefix: string, name: string) {
return `${prefix}${Array.from(name)
.slice(0, Math.max(0, 120 - prefix.length))
.join("")}`;
}