693 lines
19 KiB
TypeScript
693 lines
19 KiB
TypeScript
/**
|
|
* ComfyUI V2 API 服务层
|
|
* 基于新的后端 V2 API 的现代化前端服务接口
|
|
*/
|
|
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
|
|
|
// ==================== 基础类型定义 ====================
|
|
|
|
export interface ComfyUIV2Config {
|
|
base_url: string;
|
|
timeout?: number;
|
|
retry_attempts?: number;
|
|
enable_cache?: boolean;
|
|
max_concurrency?: number;
|
|
websocket_url?: string;
|
|
}
|
|
|
|
export interface ConnectionStatus {
|
|
connected: boolean;
|
|
last_connected_at?: string;
|
|
last_disconnected_at?: string;
|
|
reconnect_attempts: number;
|
|
total_connections: number;
|
|
error_message?: string;
|
|
}
|
|
|
|
export interface SystemInfo {
|
|
version: string;
|
|
python_version: string;
|
|
platform: string;
|
|
gpu_info?: string;
|
|
memory_info?: string;
|
|
disk_info?: string;
|
|
}
|
|
|
|
export interface QueueStatus {
|
|
running_count: number;
|
|
pending_count: number;
|
|
history_count: number;
|
|
}
|
|
|
|
// ==================== 工作流类型 ====================
|
|
|
|
export interface WorkflowV2 {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
category?: string;
|
|
workflow_data: any;
|
|
input_schema?: any;
|
|
output_schema?: any;
|
|
created_at: string;
|
|
updated_at: string;
|
|
version: number;
|
|
is_active: boolean;
|
|
tags: string[];
|
|
}
|
|
|
|
export interface CreateWorkflowRequest {
|
|
name: string;
|
|
description?: string;
|
|
category?: string;
|
|
workflow_data: any;
|
|
input_schema?: any;
|
|
output_schema?: any;
|
|
tags?: string[];
|
|
}
|
|
|
|
export interface UpdateWorkflowRequest {
|
|
name?: string;
|
|
description?: string;
|
|
category?: string;
|
|
workflow_data?: any;
|
|
input_schema?: any;
|
|
output_schema?: any;
|
|
tags?: string[];
|
|
is_active?: boolean;
|
|
}
|
|
|
|
// ==================== 模板类型 ====================
|
|
|
|
export interface TemplateV2 {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
category?: string;
|
|
workflow_id: string;
|
|
parameter_definitions: any;
|
|
default_values?: any;
|
|
created_at: string;
|
|
updated_at: string;
|
|
version: number;
|
|
is_active: boolean;
|
|
tags: string[];
|
|
}
|
|
|
|
export interface CreateTemplateRequest {
|
|
name: string;
|
|
description?: string;
|
|
category?: string;
|
|
workflow_id: string;
|
|
parameter_definitions: any;
|
|
default_values?: any;
|
|
tags?: string[];
|
|
}
|
|
|
|
// ==================== 执行类型 ====================
|
|
|
|
export interface ExecutionV2 {
|
|
id: string;
|
|
workflow_id?: string;
|
|
template_id?: string;
|
|
prompt_id: string;
|
|
parameters: any;
|
|
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
progress?: number;
|
|
results?: any;
|
|
output_urls?: string[];
|
|
error_message?: string;
|
|
execution_time?: number;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface ExecuteWorkflowRequest {
|
|
workflow_id: string;
|
|
parameters?: any;
|
|
}
|
|
|
|
export interface ExecuteTemplateRequest {
|
|
template_id: string;
|
|
parameters: any;
|
|
}
|
|
|
|
// ==================== 实时事件类型 ====================
|
|
|
|
export interface RealtimeEvent {
|
|
event_type: string;
|
|
data: any;
|
|
timestamp: string;
|
|
}
|
|
|
|
export interface ExecutionProgressEvent {
|
|
prompt_id: string;
|
|
execution_id?: string;
|
|
node_id: string;
|
|
progress: number;
|
|
max_progress: number;
|
|
percentage: number;
|
|
timestamp: string;
|
|
}
|
|
|
|
export interface ExecutionCompletedEvent {
|
|
prompt_id: string;
|
|
execution_id?: string;
|
|
outputs: Record<string, any>;
|
|
output_urls: string[];
|
|
execution_time: number;
|
|
timestamp: string;
|
|
}
|
|
|
|
// ==================== ComfyUI V2 服务类 ====================
|
|
|
|
export class ComfyUIV2Service {
|
|
private static eventListeners: Map<string, UnlistenFn> = new Map();
|
|
|
|
// ==================== 连接管理 ====================
|
|
|
|
/**
|
|
* 连接到 ComfyUI 服务
|
|
*/
|
|
static async connect(config: ComfyUIV2Config): Promise<string> {
|
|
try {
|
|
const result = await invoke<{connected: boolean, status: string, base_url: string}>('comfyui_v2_connect', { config });
|
|
return result.connected ? '连接成功' : '连接失败';
|
|
} catch (error) {
|
|
console.error('Failed to connect to ComfyUI:', error);
|
|
throw new Error(`连接 ComfyUI 失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 断开 ComfyUI 连接
|
|
*/
|
|
static async disconnect(): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_disconnect');
|
|
} catch (error) {
|
|
console.error('Failed to disconnect from ComfyUI:', error);
|
|
throw new Error(`断开 ComfyUI 连接失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取连接状态
|
|
*/
|
|
static async getConnectionStatus(): Promise<ConnectionStatus> {
|
|
try {
|
|
return await invoke<ConnectionStatus>('comfyui_v2_get_connection_status');
|
|
} catch (error) {
|
|
console.error('Failed to get connection status:', error);
|
|
throw new Error(`获取连接状态失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 健康检查
|
|
*/
|
|
static async healthCheck(): Promise<boolean> {
|
|
try {
|
|
const result = await invoke<string>('comfyui_v2_health_check');
|
|
return result === 'healthy';
|
|
} catch (error) {
|
|
console.error('Health check failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取系统信息
|
|
*/
|
|
static async getSystemInfo(): Promise<SystemInfo> {
|
|
try {
|
|
return await invoke<SystemInfo>('comfyui_v2_get_system_info');
|
|
} catch (error) {
|
|
console.error('Failed to get system info:', error);
|
|
throw new Error(`获取系统信息失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取队列状态
|
|
*/
|
|
static async getQueueStatus(): Promise<QueueStatus> {
|
|
try {
|
|
return await invoke<QueueStatus>('comfyui_v2_get_queue_status');
|
|
} catch (error) {
|
|
console.error('Failed to get queue status:', error);
|
|
throw new Error(`获取队列状态失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
// ==================== 配置管理 ====================
|
|
|
|
/**
|
|
* 获取配置
|
|
*/
|
|
static async getConfig(): Promise<ComfyUIV2Config> {
|
|
try {
|
|
return await invoke<ComfyUIV2Config>('comfyui_v2_get_config');
|
|
} catch (error) {
|
|
console.error('Failed to get config:', error);
|
|
throw new Error(`获取配置失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新配置
|
|
*/
|
|
static async updateConfig(config: Partial<ComfyUIV2Config>): Promise<string> {
|
|
try {
|
|
// 获取当前配置,然后合并新配置
|
|
let currentConfig: ComfyUIV2Config;
|
|
try {
|
|
currentConfig = await this.getConfig();
|
|
} catch {
|
|
// 如果获取失败,使用默认配置
|
|
currentConfig = {
|
|
base_url: 'http://192.168.0.193:8188',
|
|
timeout: 30000,
|
|
retry_attempts: 3,
|
|
enable_cache: true,
|
|
max_concurrency: 4,
|
|
websocket_url: 'ws://192.168.0.193:8188/ws',
|
|
};
|
|
}
|
|
|
|
// 合并配置
|
|
const fullConfig: ComfyUIV2Config = {
|
|
...currentConfig,
|
|
...config,
|
|
};
|
|
|
|
return await invoke<string>('comfyui_v2_update_config', { configRequest: fullConfig });
|
|
} catch (error) {
|
|
console.error('Failed to update config:', error);
|
|
throw new Error(`更新配置失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证配置
|
|
*/
|
|
static async validateConfig(config: ComfyUIV2Config): Promise<boolean> {
|
|
try {
|
|
const result = await invoke<string>('comfyui_v2_validate_config', { configRequest: config });
|
|
return result === 'valid';
|
|
} catch (error) {
|
|
console.error('Failed to validate config:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ==================== 工作流管理 ====================
|
|
|
|
/**
|
|
* 创建工作流
|
|
*/
|
|
static async createWorkflow(request: CreateWorkflowRequest): Promise<WorkflowV2> {
|
|
try {
|
|
return await invoke<WorkflowV2>('comfyui_v2_create_workflow', { request });
|
|
} catch (error) {
|
|
console.error('Failed to create workflow:', error);
|
|
throw new Error(`创建工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取工作流列表
|
|
*/
|
|
static async listWorkflows(category?: string, tags?: string[]): Promise<WorkflowV2[]> {
|
|
try {
|
|
return await invoke<WorkflowV2[]>('comfyui_v2_list_workflows', { category, tags });
|
|
} catch (error) {
|
|
console.error('Failed to list workflows:', error);
|
|
throw new Error(`获取工作流列表失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取单个工作流
|
|
*/
|
|
static async getWorkflow(id: string): Promise<WorkflowV2> {
|
|
try {
|
|
return await invoke<WorkflowV2>('comfyui_v2_get_workflow', { id });
|
|
} catch (error) {
|
|
console.error('Failed to get workflow:', error);
|
|
throw new Error(`获取工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新工作流
|
|
*/
|
|
static async updateWorkflow(id: string, request: UpdateWorkflowRequest): Promise<WorkflowV2> {
|
|
try {
|
|
return await invoke<WorkflowV2>('comfyui_v2_update_workflow', { id, request });
|
|
} catch (error) {
|
|
console.error('Failed to update workflow:', error);
|
|
throw new Error(`更新工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除工作流
|
|
*/
|
|
static async deleteWorkflow(id: string): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_delete_workflow', { id });
|
|
} catch (error) {
|
|
console.error('Failed to delete workflow:', error);
|
|
throw new Error(`删除工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 搜索工作流
|
|
*/
|
|
static async searchWorkflows(query: string, category?: string): Promise<WorkflowV2[]> {
|
|
try {
|
|
return await invoke<WorkflowV2[]>('comfyui_v2_search_workflows', { query, category });
|
|
} catch (error) {
|
|
console.error('Failed to search workflows:', error);
|
|
throw new Error(`搜索工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
// ==================== 模板管理 ====================
|
|
|
|
/**
|
|
* 创建模板
|
|
*/
|
|
static async createTemplate(request: CreateTemplateRequest): Promise<TemplateV2> {
|
|
try {
|
|
return await invoke<TemplateV2>('comfyui_v2_create_template', { request });
|
|
} catch (error) {
|
|
console.error('Failed to create template:', error);
|
|
throw new Error(`创建模板失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取模板列表
|
|
*/
|
|
static async listTemplates(category?: string, tags?: string[]): Promise<TemplateV2[]> {
|
|
try {
|
|
return await invoke<TemplateV2[]>('comfyui_v2_list_templates', { category, tags });
|
|
} catch (error) {
|
|
console.error('Failed to list templates:', error);
|
|
throw new Error(`获取模板列表失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取单个模板
|
|
*/
|
|
static async getTemplate(id: string): Promise<TemplateV2> {
|
|
try {
|
|
return await invoke<TemplateV2>('comfyui_v2_get_template', { id });
|
|
} catch (error) {
|
|
console.error('Failed to get template:', error);
|
|
throw new Error(`获取模板失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除模板
|
|
*/
|
|
static async deleteTemplate(id: string): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_delete_template', { id });
|
|
} catch (error) {
|
|
console.error('Failed to delete template:', error);
|
|
throw new Error(`删除模板失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 预览模板实例
|
|
*/
|
|
static async previewTemplateInstance(templateId: string, parameters: any): Promise<any> {
|
|
try {
|
|
return await invoke<any>('comfyui_v2_preview_template_instance', { templateId, parameters });
|
|
} catch (error) {
|
|
console.error('Failed to preview template instance:', error);
|
|
throw new Error(`预览模板实例失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证模板参数
|
|
*/
|
|
static async validateTemplateParameters(templateId: string, parameters: any): Promise<boolean> {
|
|
try {
|
|
const result = await invoke<string>('comfyui_v2_validate_template_parameters', { templateId, parameters });
|
|
return result === 'valid';
|
|
} catch (error) {
|
|
console.error('Failed to validate template parameters:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ==================== 执行管理 ====================
|
|
|
|
/**
|
|
* 执行工作流
|
|
*/
|
|
static async executeWorkflow(request: ExecuteWorkflowRequest): Promise<ExecutionV2> {
|
|
try {
|
|
return await invoke<ExecutionV2>('comfyui_v2_execute_workflow', { request });
|
|
} catch (error) {
|
|
console.error('Failed to execute workflow:', error);
|
|
throw new Error(`执行工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 执行模板
|
|
*/
|
|
static async executeTemplate(request: ExecuteTemplateRequest): Promise<ExecutionV2> {
|
|
try {
|
|
return await invoke<ExecutionV2>('comfyui_v2_execute_template', { request });
|
|
} catch (error) {
|
|
console.error('Failed to execute template:', error);
|
|
throw new Error(`执行模板失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取消执行
|
|
*/
|
|
static async cancelExecution(executionId: string): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_cancel_execution', { executionId });
|
|
} catch (error) {
|
|
console.error('Failed to cancel execution:', error);
|
|
throw new Error(`取消执行失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取执行状态
|
|
*/
|
|
static async getExecutionStatus(executionId: string): Promise<ExecutionV2> {
|
|
try {
|
|
return await invoke<ExecutionV2>('comfyui_v2_get_execution_status', { executionId });
|
|
} catch (error) {
|
|
console.error('Failed to get execution status:', error);
|
|
throw new Error(`获取执行状态失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取执行历史
|
|
*/
|
|
static async getExecutionHistory(limit?: number, status?: string): Promise<ExecutionV2[]> {
|
|
try {
|
|
return await invoke<ExecutionV2[]>('comfyui_v2_get_execution_history', { limit, status });
|
|
} catch (error) {
|
|
console.error('Failed to get execution history:', error);
|
|
throw new Error(`获取执行历史失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
// ==================== 实时通信 ====================
|
|
|
|
/**
|
|
* 启动实时监控
|
|
*/
|
|
static async startRealtimeMonitor(config?: any): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_start_realtime_monitor_enhanced', { config });
|
|
} catch (error) {
|
|
console.error('Failed to start realtime monitor:', error);
|
|
throw new Error(`启动实时监控失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止实时监控
|
|
*/
|
|
static async stopRealtimeMonitor(): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_stop_realtime_monitor_enhanced');
|
|
} catch (error) {
|
|
console.error('Failed to stop realtime monitor:', error);
|
|
throw new Error(`停止实时监控失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 订阅实时事件
|
|
*/
|
|
static async subscribeRealtimeEvents(
|
|
eventTypes?: string[],
|
|
callback?: (event: RealtimeEvent) => void
|
|
): Promise<string> {
|
|
try {
|
|
// 订阅后端事件
|
|
const subscriptionId = await invoke<string>('comfyui_v2_subscribe_realtime_events_enhanced', { eventTypes });
|
|
|
|
// 如果提供了回调,设置事件监听
|
|
if (callback) {
|
|
// 监听连接状态变化
|
|
const connectionUnlisten = await listen('comfyui://connection-changed', (event) => {
|
|
callback({
|
|
event_type: 'connection_changed',
|
|
data: event.payload,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
});
|
|
|
|
// 监听执行进度
|
|
const progressUnlisten = await listen('comfyui://execution-progress', (event) => {
|
|
callback({
|
|
event_type: 'execution_progress',
|
|
data: event.payload,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
});
|
|
|
|
// 监听执行完成
|
|
const completedUnlisten = await listen('comfyui://execution-completed', (event) => {
|
|
callback({
|
|
event_type: 'execution_completed',
|
|
data: event.payload,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
});
|
|
|
|
// 监听执行失败
|
|
const failedUnlisten = await listen('comfyui://execution-failed', (event) => {
|
|
callback({
|
|
event_type: 'execution_failed',
|
|
data: event.payload,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
});
|
|
|
|
// 保存监听器以便后续清理
|
|
this.eventListeners.set(`${subscriptionId}_connection`, connectionUnlisten);
|
|
this.eventListeners.set(`${subscriptionId}_progress`, progressUnlisten);
|
|
this.eventListeners.set(`${subscriptionId}_completed`, completedUnlisten);
|
|
this.eventListeners.set(`${subscriptionId}_failed`, failedUnlisten);
|
|
}
|
|
|
|
return subscriptionId;
|
|
} catch (error) {
|
|
console.error('Failed to subscribe to realtime events:', error);
|
|
throw new Error(`订阅实时事件失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取消订阅实时事件
|
|
*/
|
|
static async unsubscribeRealtimeEvents(subscriptionId: string): Promise<string> {
|
|
try {
|
|
// 清理事件监听器
|
|
const listenersToRemove = Array.from(this.eventListeners.keys())
|
|
.filter(key => key.startsWith(subscriptionId));
|
|
|
|
for (const key of listenersToRemove) {
|
|
const unlisten = this.eventListeners.get(key);
|
|
if (unlisten) {
|
|
unlisten();
|
|
this.eventListeners.delete(key);
|
|
}
|
|
}
|
|
|
|
// 取消后端订阅
|
|
return await invoke<string>('comfyui_v2_unsubscribe_realtime_events', { subscriptionId });
|
|
} catch (error) {
|
|
console.error('Failed to unsubscribe from realtime events:', error);
|
|
throw new Error(`取消订阅实时事件失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 注册执行映射
|
|
*/
|
|
static async registerExecutionMapping(promptId: string, executionId: string): Promise<string> {
|
|
try {
|
|
return await invoke<string>('comfyui_v2_register_execution_mapping', { promptId, executionId });
|
|
} catch (error) {
|
|
console.error('Failed to register execution mapping:', error);
|
|
throw new Error(`注册执行映射失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
// ==================== 批量操作 ====================
|
|
|
|
/**
|
|
* 批量执行工作流
|
|
*/
|
|
static async batchExecuteWorkflows(requests: ExecuteWorkflowRequest[]): Promise<ExecutionV2[]> {
|
|
try {
|
|
return await invoke<ExecutionV2[]>('comfyui_v2_batch_execute_workflows', { requests });
|
|
} catch (error) {
|
|
console.error('Failed to batch execute workflows:', error);
|
|
throw new Error(`批量执行工作流失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量执行模板
|
|
*/
|
|
static async batchExecuteTemplates(requests: ExecuteTemplateRequest[]): Promise<ExecutionV2[]> {
|
|
try {
|
|
return await invoke<ExecutionV2[]>('comfyui_v2_batch_execute_templates', { requests });
|
|
} catch (error) {
|
|
console.error('Failed to batch execute templates:', error);
|
|
throw new Error(`批量执行模板失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量取消执行
|
|
*/
|
|
static async batchCancelExecutions(executionIds: string[]): Promise<string[]> {
|
|
try {
|
|
return await invoke<string[]>('comfyui_v2_batch_cancel_executions', { executionIds });
|
|
} catch (error) {
|
|
console.error('Failed to batch cancel executions:', error);
|
|
throw new Error(`批量取消执行失败: ${error}`);
|
|
}
|
|
}
|
|
|
|
// ==================== 工具方法 ====================
|
|
|
|
/**
|
|
* 清理所有事件监听器
|
|
*/
|
|
static cleanup(): void {
|
|
for (const [key, unlisten] of this.eventListeners) {
|
|
unlisten();
|
|
}
|
|
this.eventListeners.clear();
|
|
}
|
|
}
|