From 08b981c31ff536f8cffb7daad432f17d2b694099 Mon Sep 17 00:00:00 2001 From: imeepos Date: Fri, 15 Aug 2025 15:31:33 +0800 Subject: [PATCH] feat: add uni-comfyui-sdk - Rust SDK for ComfyUI API - Complete Rust SDK implementation based on OpenAPI specification - All 23 endpoints implemented with type-safe interfaces - Comprehensive error handling and async support - Full documentation and examples included - Client renamed to UniComfyUIClient for clear identification --- cargos/uni-comfyui-sdk/.gitignore | 20 + cargos/uni-comfyui-sdk/Cargo.toml | 23 + cargos/uni-comfyui-sdk/README.md | 124 ++ .../examples/advanced_usage.rs | 172 +++ .../uni-comfyui-sdk/examples/basic_usage.rs | 56 + cargos/uni-comfyui-sdk/src/client.rs | 291 ++++ cargos/uni-comfyui-sdk/src/error.rs | 40 + cargos/uni-comfyui-sdk/src/lib.rs | 58 + cargos/uni-comfyui-sdk/src/models.rs | 130 ++ cargos/uni-comfyui-sdk/src/types.rs | 16 + .../tests/integration_tests.rs | 90 ++ cargos/uni-comfyui-sdk/unicomfy.json | 1173 +++++++++++++++++ 12 files changed, 2193 insertions(+) create mode 100644 cargos/uni-comfyui-sdk/.gitignore create mode 100644 cargos/uni-comfyui-sdk/Cargo.toml create mode 100644 cargos/uni-comfyui-sdk/README.md create mode 100644 cargos/uni-comfyui-sdk/examples/advanced_usage.rs create mode 100644 cargos/uni-comfyui-sdk/examples/basic_usage.rs create mode 100644 cargos/uni-comfyui-sdk/src/client.rs create mode 100644 cargos/uni-comfyui-sdk/src/error.rs create mode 100644 cargos/uni-comfyui-sdk/src/lib.rs create mode 100644 cargos/uni-comfyui-sdk/src/models.rs create mode 100644 cargos/uni-comfyui-sdk/src/types.rs create mode 100644 cargos/uni-comfyui-sdk/tests/integration_tests.rs create mode 100644 cargos/uni-comfyui-sdk/unicomfy.json diff --git a/cargos/uni-comfyui-sdk/.gitignore b/cargos/uni-comfyui-sdk/.gitignore new file mode 100644 index 0000000..37fe539 --- /dev/null +++ b/cargos/uni-comfyui-sdk/.gitignore @@ -0,0 +1,20 @@ +# Rust +/target/ +Cargo.lock + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log + +# Environment +.env +.env.local diff --git a/cargos/uni-comfyui-sdk/Cargo.toml b/cargos/uni-comfyui-sdk/Cargo.toml new file mode 100644 index 0000000..81da095 --- /dev/null +++ b/cargos/uni-comfyui-sdk/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "uni-comfyui-sdk" +version = "0.1.0" +edition = "2021" +description = "Rust SDK for ComfyUI Workflow Service & Management API" +license = "MIT" +repository = "https://github.com/your-repo/uni-comfyui-sdk" +keywords = ["comfyui", "api", "sdk", "workflow"] +categories = ["api-bindings", "web-programming::http-client"] + +[workspace] + +[dependencies] +reqwest = { version = "0.11", features = ["json"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.0", features = ["full"] } +thiserror = "1.0" +url = "2.4" +chrono = { version = "0.4", features = ["serde"] } + +[dev-dependencies] +tokio-test = "0.4" diff --git a/cargos/uni-comfyui-sdk/README.md b/cargos/uni-comfyui-sdk/README.md new file mode 100644 index 0000000..6cf0b71 --- /dev/null +++ b/cargos/uni-comfyui-sdk/README.md @@ -0,0 +1,124 @@ +# uni-comfyui-sdk + +A Rust SDK for ComfyUI Workflow Service & Management API. + +## Features + +- **Status Management**: Get metrics, server status, and file listings +- **Workflow Management**: Create, retrieve, and delete workflows +- **Workflow Execution**: Run workflows and monitor their status +- **Server Management**: Register, unregister, and manage ComfyUI servers +- **Monitoring**: Health checks, system stats, and task monitoring +- **Type Safety**: Strongly typed API with comprehensive error handling + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +uni-comfyui-sdk = "0.1.0" +``` + +## Quick Start + +```rust +use uni_comfyui_sdk::{UniComfyUIClient, Result}; +use std::collections::HashMap; + +#[tokio::main] +async fn main() -> Result<()> { + // Create a client + let client = UniComfyUIClient::new("http://localhost:8000")?; + + // Get system metrics + let metrics = client.get_metrics().await?; + println!("Metrics: {:#?}", metrics); + + // Get all workflows + let workflows = client.get_all_workflows().await?; + println!("Found {} workflows", workflows.len()); + + // Run a workflow + let mut data = HashMap::new(); + data.insert("input".to_string(), serde_json::json!("test input")); + let result = client.run_workflow("my_workflow", None, data).await?; + println!("Workflow result: {:#?}", result); + + Ok(()) +} +``` + +## API Coverage + +### Status Endpoints +- `get_metrics()` - Get queue status overview +- `get_servers_status()` - Get all configured ComfyUI servers status +- `list_server_files(server_index)` - List files in server input/output folders + +### Workflow Endpoints +- `get_all_workflows()` - Get all workflows +- `publish_workflow(data)` - Publish a new workflow +- `delete_workflow(workflow_name)` - Delete a workflow +- `get_one_workflow(base_name, version)` - Get workflow specification + +### Run Endpoints +- `run_workflow(name, version, data)` - Execute workflow asynchronously +- `get_run_status(workflow_run_id)` - Get workflow execution status + +### RunX Endpoints +- `model_with_multi_dress(version, data)` - Model with multiple dresses workflow +- `model_with_multi_dress_json_schema()` - Get workflow definition + +### Monitor Endpoints +- `monitor_dashboard()` - Get monitoring dashboard HTML +- `get_system_stats()` - Get system statistics +- `get_task_stats()` - Get task statistics +- `get_monitor_server_status()` - Get server status information +- `get_recent_tasks(limit)` - Get recent tasks list +- `health_check()` - Health check endpoint + +### ComfyUI Server Management Endpoints +- `register_server(request)` - Register ComfyUI server +- `update_heartbeat(request)` - Update server heartbeat +- `unregister_server(request)` - Unregister ComfyUI server +- `get_server_status(server_name)` - Get specific server status +- `list_all_servers()` - Get all servers list +- `get_system_health()` - Get system overall health status +- `force_health_check()` - Force health check execution + +## Error Handling + +The SDK provides comprehensive error handling through the `ComfyUIError` enum: + +```rust +use uni_comfyui_sdk::{UniComfyUIClient, ComfyUIError}; + +match client.get_metrics().await { + Ok(metrics) => println!("Success: {:#?}", metrics), + Err(ComfyUIError::Http(e)) => println!("HTTP error: {}", e), + Err(ComfyUIError::Api { status, message }) => { + println!("API error {}: {}", status, message) + } + Err(e) => println!("Other error: {}", e), +} +``` + +## Data Models + +The SDK includes strongly typed models for all API requests and responses: + +- `ServerRegistrationRequest` - Server registration data +- `ServerHeartbeatRequest` - Server heartbeat data +- `ServerStatusResponse` - Server status information +- `ServerFiles` - Server file listings +- `FileDetails` - Individual file information +- And more... + +## Examples + +See the `examples/` directory for more usage examples. + +## License + +MIT diff --git a/cargos/uni-comfyui-sdk/examples/advanced_usage.rs b/cargos/uni-comfyui-sdk/examples/advanced_usage.rs new file mode 100644 index 0000000..db93980 --- /dev/null +++ b/cargos/uni-comfyui-sdk/examples/advanced_usage.rs @@ -0,0 +1,172 @@ +use std::collections::HashMap; +use std::time::Duration; +use tokio::time::sleep; +use uni_comfyui_sdk::{ + UniComfyUIClient, ComfyUIError, Result, ServerHeartbeatRequest, ServerRegistrationRequest, + ServerUnregisterRequest, +}; + +#[tokio::main] +async fn main() -> Result<()> { + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + + // Demonstrate comprehensive server management + println!("=== Server Management Demo ==="); + + // Register a new server + let server_name = "demo-server"; + let registration = ServerRegistrationRequest { + name: server_name.to_string(), + http_url: "http://192.168.0.148:8188".to_string(), + ws_url: "ws://192.168.0.148:8188".to_string(), + }; + + match client.register_server(registration).await { + Ok(response) => println!("✓ Server registered: {:?}", response), + Err(ComfyUIError::Api { status: 409, .. }) => { + println!("ℹ Server already exists (409 conflict)") + } + Err(e) => println!("✗ Failed to register server: {}", e), + } + + // Send heartbeat + let heartbeat = ServerHeartbeatRequest { + name: server_name.to_string(), + }; + + match client.update_heartbeat(heartbeat).await { + Ok(response) => println!("✓ Heartbeat sent: {:?}", response), + Err(e) => println!("✗ Failed to send heartbeat: {}", e), + } + + // List all servers + match client.list_all_servers().await { + Ok(servers) => { + println!("✓ Found {} servers:", servers.len()); + for server in servers { + println!(" - {}: {} ({})", server.name, server.http_url, server.status); + } + } + Err(e) => println!("✗ Failed to list servers: {}", e), + } + + // Demonstrate workflow management + println!("\n=== Workflow Management Demo ==="); + + // Get all workflows + match client.get_all_workflows().await { + Ok(workflows) => println!("✓ Found {} workflows", workflows.len()), + Err(e) => println!("✗ Failed to get workflows: {}", e), + } + + // Publish a sample workflow + let mut sample_workflow = HashMap::new(); + sample_workflow.insert("name".to_string(), serde_json::json!("demo_workflow")); + sample_workflow.insert("version".to_string(), serde_json::json!("1.0.0")); + sample_workflow.insert( + "description".to_string(), + serde_json::json!("A demo workflow for testing"), + ); + + match client.publish_workflow(sample_workflow).await { + Ok(_) => println!("✓ Workflow published successfully"), + Err(e) => println!("✗ Failed to publish workflow: {}", e), + } + + // Demonstrate workflow execution + println!("\n=== Workflow Execution Demo ==="); + + let mut execution_data = HashMap::new(); + execution_data.insert("input_text".to_string(), serde_json::json!("Hello, ComfyUI!")); + execution_data.insert("steps".to_string(), serde_json::json!(20)); + execution_data.insert("seed".to_string(), serde_json::json!(42)); + + match client + .run_workflow("demo_workflow", Some("1.0.0"), execution_data) + .await + { + Ok(response) => { + println!("✓ Workflow execution started"); + + // Try to extract run ID from response for status checking + if let Some(run_id) = response.get("run_id").and_then(|v| v.as_str()) { + println!(" Run ID: {}", run_id); + + // Poll for status (demo - in real usage you'd want better polling logic) + for i in 1..=3 { + sleep(Duration::from_secs(2)).await; + match client.get_run_status(run_id).await { + Ok(status) => { + println!(" Status check {}: {:?}", i, status); + } + Err(e) => println!(" Status check {} failed: {}", i, e), + } + } + } + } + Err(e) => println!("✗ Failed to start workflow: {}", e), + } + + // Demonstrate monitoring + println!("\n=== Monitoring Demo ==="); + + // System health + match client.get_system_health().await { + Ok(health) => println!("✓ System health: {:?}", health), + Err(e) => println!("✗ Failed to get system health: {}", e), + } + + // System stats + match client.get_system_stats().await { + Ok(stats) => println!("✓ System stats retrieved"), + Err(e) => println!("✗ Failed to get system stats: {}", e), + } + + // Recent tasks + match client.get_recent_tasks(Some(5)).await { + Ok(tasks) => println!("✓ Found {} recent tasks", tasks.len()), + Err(e) => println!("✗ Failed to get recent tasks: {}", e), + } + + // Force health check + match client.force_health_check().await { + Ok(response) => println!("✓ Health check forced: {:?}", response), + Err(e) => println!("✗ Failed to force health check: {}", e), + } + + // Demonstrate RunX endpoints + println!("\n=== RunX Demo ==="); + + // Get schema for model_with_multi_dress + match client.model_with_multi_dress_json_schema().await { + Ok(schema) => println!("✓ Got model_with_multi_dress schema"), + Err(e) => println!("✗ Failed to get schema: {}", e), + } + + // Try to run model_with_multi_dress workflow + let mut dress_data = HashMap::new(); + dress_data.insert("model_image".to_string(), serde_json::json!("base64_image_data")); + dress_data.insert("dress_images".to_string(), serde_json::json!(["dress1.jpg", "dress2.jpg"])); + + match client + .model_with_multi_dress(None, dress_data) + .await + { + Ok(response) => println!("✓ Model with multi dress executed: {:?}", response), + Err(e) => println!("✗ Failed to execute model with multi dress: {}", e), + } + + // Cleanup: Unregister the demo server + println!("\n=== Cleanup ==="); + let unregister = ServerUnregisterRequest { + name: server_name.to_string(), + }; + + match client.unregister_server(unregister).await { + Ok(response) => println!("✓ Server unregistered: {:?}", response), + Err(e) => println!("✗ Failed to unregister server: {}", e), + } + + println!("\n=== Demo Complete ==="); + Ok(()) +} diff --git a/cargos/uni-comfyui-sdk/examples/basic_usage.rs b/cargos/uni-comfyui-sdk/examples/basic_usage.rs new file mode 100644 index 0000000..45c94e7 --- /dev/null +++ b/cargos/uni-comfyui-sdk/examples/basic_usage.rs @@ -0,0 +1,56 @@ +use std::collections::HashMap; +use uni_comfyui_sdk::{UniComfyUIClient, Result, ServerRegistrationRequest}; + +#[tokio::main] +async fn main() -> Result<()> { + // Create a new client + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + + // Get system metrics + println!("Getting system metrics..."); + let metrics = client.get_metrics().await?; + println!("Metrics: {:#?}", metrics); + + // Get all workflows + println!("\nGetting all workflows..."); + let workflows = client.get_all_workflows().await?; + println!("Found {} workflows", workflows.len()); + + // Get servers status + println!("\nGetting servers status..."); + let servers = client.get_servers_status().await?; + println!("Found {} servers", servers.len()); + + // Health check + println!("\nPerforming health check..."); + let health = client.health_check().await?; + println!("Health status: {:#?}", health); + + // Example: Register a new server + println!("\nRegistering a new server..."); + let registration_request = ServerRegistrationRequest { + name: "test-server".to_string(), + http_url: "http://192.168.0.148:8188".to_string(), + ws_url: "ws://192.168.0.148:8188".to_string(), + }; + + match client.register_server(registration_request).await { + Ok(response) => println!("Server registered: {:#?}", response), + Err(e) => println!("Failed to register server: {}", e), + } + + // Example: Run a workflow + println!("\nRunning a workflow..."); + let mut workflow_data = HashMap::new(); + workflow_data.insert("input".to_string(), serde_json::json!("test input")); + + match client + .run_workflow("test_workflow", None, workflow_data) + .await + { + Ok(response) => println!("Workflow started: {:#?}", response), + Err(e) => println!("Failed to start workflow: {}", e), + } + + Ok(()) +} diff --git a/cargos/uni-comfyui-sdk/src/client.rs b/cargos/uni-comfyui-sdk/src/client.rs new file mode 100644 index 0000000..6e5a0f1 --- /dev/null +++ b/cargos/uni-comfyui-sdk/src/client.rs @@ -0,0 +1,291 @@ +use crate::error::{ComfyUIError, Result}; +use crate::models::*; +use crate::types::*; +use reqwest::{Client, Response}; +use serde_json::Value; +use url::Url; + +/// UniComfyUI API client +#[derive(Debug, Clone)] +pub struct UniComfyUIClient { + client: Client, + base_url: Url, +} + +impl UniComfyUIClient { + /// Create a new UniComfyUI client + pub fn new(base_url: &str) -> Result { + let base_url = Url::parse(base_url)?; + let client = Client::new(); + + Ok(Self { client, base_url }) + } + + /// Create a new UniComfyUI client with custom reqwest client + pub fn with_client(base_url: &str, client: Client) -> Result { + let base_url = Url::parse(base_url)?; + + Ok(Self { client, base_url }) + } + + /// Build URL for an endpoint + fn build_url(&self, path: &str) -> Result { + Ok(self.base_url.join(path)?) + } + + /// Handle HTTP response and convert to appropriate result + async fn handle_response(&self, response: Response) -> Result + where + T: serde::de::DeserializeOwned, + { + let status = response.status(); + + if status.is_success() { + let text = response.text().await?; + if text.is_empty() { + // Handle empty responses for some endpoints + return Ok(serde_json::from_value(Value::Null)?); + } + Ok(serde_json::from_str(&text)?) + } else { + let error_text = response.text().await.unwrap_or_default(); + Err(ComfyUIError::Api { + status: status.as_u16(), + message: error_text, + }) + } + } + + // Status endpoints + + /// Get metrics - 获取队列状态概览 + pub async fn get_metrics(&self) -> Result { + let url = self.build_url("/api/service/metrics")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Get servers status - 获取所有已配置的ComfyUI服务器的配置信息和实时状态 + pub async fn get_servers_status(&self) -> Result> { + let url = self.build_url("/api/service/servers_status")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// List server files - 获取指定ComfyUI服务器的输入和输出文件夹中的文件列表 + pub async fn list_server_files(&self, server_index: u32) -> Result { + let url = self.build_url(&format!("/api/service/servers/{}/files", server_index))?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + // Workflow endpoints + + /// Get all workflows - 获取所有工作流 + pub async fn get_all_workflows(&self) -> Result { + let url = self.build_url("/api/workflow")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Publish workflow - 发布工作流 + pub async fn publish_workflow(&self, data: JsonObject) -> Result { + let url = self.build_url("/api/workflow")?; + let response = self.client.post(url).json(&data).send().await?; + self.handle_response(response).await + } + + /// Delete workflow - 删除工作流 + pub async fn delete_workflow(&self, workflow_name: &str) -> Result { + let url = self.build_url(&format!("/api/workflow/{}", workflow_name))?; + let response = self.client.delete(url).send().await?; + self.handle_response(response).await + } + + /// Get one workflow - 获取工作流规范 + pub async fn get_one_workflow( + &self, + base_name: &str, + version: Option<&str>, + ) -> Result { + let mut url = self.build_url(&format!("/api/workflow/{}", base_name))?; + + if let Some(version) = version { + url.query_pairs_mut().append_pair("version", version); + } + + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + // Run endpoints + + /// Run workflow - 异步执行工作流 + pub async fn run_workflow( + &self, + workflow_name: &str, + workflow_version: Option<&str>, + data: JsonObject, + ) -> Result { + let mut url = self.build_url("/api/run")?; + url.query_pairs_mut().append_pair("workflow_name", workflow_name); + + if let Some(version) = workflow_version { + url.query_pairs_mut().append_pair("workflow_version", version); + } + + let response = self.client.post(url).json(&data).send().await?; + self.handle_response(response).await + } + + /// Get run status - 获取工作流执行状态 + pub async fn get_run_status(&self, workflow_run_id: &str) -> Result { + let url = self.build_url(&format!("/api/run/{}", workflow_run_id))?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + // RunX endpoints + + /// Model with multi dress - 一个模特,穿多件不同的衣服 + pub async fn model_with_multi_dress( + &self, + workflow_version: Option<&str>, + data: JsonObject, + ) -> Result { + let mut url = self.build_url("/api/runx/model_with_multi_dress")?; + + if let Some(version) = workflow_version { + url.query_pairs_mut().append_pair("workflow_version", version); + } + + let response = self.client.post(url).json(&data).send().await?; + self.handle_response(response).await + } + + /// Get model with multi dress JSON schema - 获取工作流定义 + pub async fn model_with_multi_dress_json_schema(&self) -> Result { + let url = self.build_url("/api/runx/model_with_multi_dress/json_schema")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + // Monitor endpoints + + /// Monitor dashboard - 监控仪表板页面 + pub async fn monitor_dashboard(&self) -> Result { + let url = self.build_url("/monitor/")?; + let response = self.client.get(url).send().await?; + + let status = response.status(); + if status.is_success() { + Ok(response.text().await?) + } else { + let error_text = response.text().await.unwrap_or_default(); + Err(ComfyUIError::Api { + status: status.as_u16(), + message: error_text, + }) + } + } + + /// Get system stats - 获取系统统计信息 + pub async fn get_system_stats(&self) -> Result { + let url = self.build_url("/monitor/system-stats")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Get task stats - 获取任务统计信息 + pub async fn get_task_stats(&self) -> Result { + let url = self.build_url("/monitor/task-stats")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Get server status (monitor) - 获取服务器状态信息 + pub async fn get_monitor_server_status(&self) -> Result { + let url = self.build_url("/monitor/server-status")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Get recent tasks - 获取最近的任务列表 + pub async fn get_recent_tasks(&self, limit: Option) -> Result { + let mut url = self.build_url("/monitor/recent-tasks")?; + + if let Some(limit) = limit { + url.query_pairs_mut().append_pair("limit", &limit.to_string()); + } + + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Health check - 健康检查端点 + pub async fn health_check(&self) -> Result { + let url = self.build_url("/monitor/health")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + // ComfyUI Server Management endpoints + + /// Register server - 注册ComfyUI服务器 + pub async fn register_server( + &self, + request: ServerRegistrationRequest, + ) -> Result { + let url = self.build_url("/api/comfy/register")?; + let response = self.client.post(url).json(&request).send().await?; + self.handle_response(response).await + } + + /// Update heartbeat - 更新服务器心跳 + pub async fn update_heartbeat( + &self, + request: ServerHeartbeatRequest, + ) -> Result { + let url = self.build_url("/api/comfy/heartbeat")?; + let response = self.client.post(url).json(&request).send().await?; + self.handle_response(response).await + } + + /// Unregister server - 注销ComfyUI服务器 + pub async fn unregister_server( + &self, + request: ServerUnregisterRequest, + ) -> Result { + let url = self.build_url("/api/comfy/unregister")?; + let response = self.client.post(url).json(&request).send().await?; + self.handle_response(response).await + } + + /// Get server status - 获取指定服务器状态 + pub async fn get_server_status(&self, server_name: &str) -> Result { + let url = self.build_url(&format!("/api/comfy/status/{}", server_name))?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// List all servers - 获取所有服务器列表 + pub async fn list_all_servers(&self) -> Result> { + let url = self.build_url("/api/comfy/list")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Get system health - 获取系统整体健康状态 + pub async fn get_system_health(&self) -> Result { + let url = self.build_url("/api/comfy/health")?; + let response = self.client.get(url).send().await?; + self.handle_response(response).await + } + + /// Force health check - 强制执行健康检查 + pub async fn force_health_check(&self) -> Result { + let url = self.build_url("/api/comfy/force_health_check")?; + let response = self.client.post(url).send().await?; + self.handle_response(response).await + } +} diff --git a/cargos/uni-comfyui-sdk/src/error.rs b/cargos/uni-comfyui-sdk/src/error.rs new file mode 100644 index 0000000..2f305c6 --- /dev/null +++ b/cargos/uni-comfyui-sdk/src/error.rs @@ -0,0 +1,40 @@ +use thiserror::Error; + +/// Result type alias for ComfyUI SDK operations +pub type Result = std::result::Result; + +/// Error types for ComfyUI SDK operations +#[derive(Error, Debug)] +pub enum ComfyUIError { + /// HTTP request error + #[error("HTTP request failed: {0}")] + Http(#[from] reqwest::Error), + + /// JSON serialization/deserialization error + #[error("JSON error: {0}")] + Json(#[from] serde_json::Error), + + /// URL parsing error + #[error("URL parsing error: {0}")] + Url(#[from] url::ParseError), + + /// API error response + #[error("API error: {status} - {message}")] + Api { status: u16, message: String }, + + /// Validation error + #[error("Validation error: {0}")] + Validation(String), + + /// Server not found error + #[error("Server not found: {0}")] + ServerNotFound(String), + + /// Workflow not found error + #[error("Workflow not found: {0}")] + WorkflowNotFound(String), + + /// Generic error + #[error("Error: {0}")] + Generic(String), +} diff --git a/cargos/uni-comfyui-sdk/src/lib.rs b/cargos/uni-comfyui-sdk/src/lib.rs new file mode 100644 index 0000000..4051f85 --- /dev/null +++ b/cargos/uni-comfyui-sdk/src/lib.rs @@ -0,0 +1,58 @@ +//! # uni-comfyui-sdk +//! +//! Rust SDK for ComfyUI Workflow Service & Management API +//! +//! This SDK provides a type-safe interface to interact with the ComfyUI API, +//! including workflow management, execution, monitoring, and server management. +//! +//! ## Features +//! +//! - **Status Management**: Get metrics, server status, and file listings +//! - **Workflow Management**: Create, retrieve, and delete workflows +//! - **Workflow Execution**: Run workflows and monitor their status +//! - **Server Management**: Register, unregister, and manage ComfyUI servers +//! - **Monitoring**: Health checks, system stats, and task monitoring +//! - **Type Safety**: Strongly typed API with comprehensive error handling +//! +//! ## Quick Start +//! +//! ```rust,no_run +//! use uni_comfyui_sdk::{UniComfyUIClient, Result}; +//! use std::collections::HashMap; +//! +//! #[tokio::main] +//! async fn main() -> Result<()> { +//! // Create a client +//! let client = UniComfyUIClient::new("http://localhost:8000")?; +//! +//! // Get system metrics +//! let metrics = client.get_metrics().await?; +//! println!("Metrics: {:#?}", metrics); +//! +//! // Run a workflow +//! let mut data = HashMap::new(); +//! data.insert("input".to_string(), serde_json::json!("test")); +//! let result = client.run_workflow("my_workflow", None, data).await?; +//! println!("Workflow result: {:#?}", result); +//! +//! Ok(()) +//! } +//! ``` + +pub mod client; +pub mod error; +pub mod models; +pub mod types; + +pub use client::UniComfyUIClient; +pub use error::{ComfyUIError, Result}; +pub use models::*; +pub use types::*; + +/// Re-export commonly used types +pub mod prelude { + pub use crate::client::UniComfyUIClient; + pub use crate::error::{ComfyUIError, Result}; + pub use crate::models::*; + pub use crate::types::*; +} diff --git a/cargos/uni-comfyui-sdk/src/models.rs b/cargos/uni-comfyui-sdk/src/models.rs new file mode 100644 index 0000000..c433308 --- /dev/null +++ b/cargos/uni-comfyui-sdk/src/models.rs @@ -0,0 +1,130 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// File details information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FileDetails { + /// File name + pub name: String, + /// File size in KB + pub size_kb: f64, + /// Last modified timestamp + pub modified_at: DateTime, +} + +/// HTTP validation error details +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HTTPValidationError { + /// List of validation errors + pub detail: Vec, +} + +/// Individual validation error +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ValidationError { + /// Error location path + pub loc: Vec, + /// Error message + pub msg: String, + /// Error type + #[serde(rename = "type")] + pub error_type: String, +} + +/// Validation error location (can be string or integer) +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ValidationErrorLocation { + String(String), + Integer(i32), +} + +/// Server files information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerFiles { + /// Server index in configuration list + pub server_index: i32, + /// HTTP URL of the server + pub http_url: String, + /// List of input files + pub input_files: Vec, + /// List of output files + pub output_files: Vec, +} + +/// Server heartbeat request +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerHeartbeatRequest { + /// Server name + pub name: String, +} + +/// Server queue details +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerQueueDetails { + /// Number of running tasks + pub running_count: i32, + /// Number of pending tasks + pub pending_count: i32, +} + +/// Server registration request +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerRegistrationRequest { + /// Server name + pub name: String, + /// HTTP URL of the server + pub http_url: String, + /// WebSocket URL of the server + pub ws_url: String, +} + +/// Server status information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerStatus { + /// Server index in configuration list + pub server_index: i32, + /// HTTP URL of the server + pub http_url: String, + /// WebSocket URL of the server + pub ws_url: String, + /// Whether the server is reachable + pub is_reachable: bool, + /// Whether the server is free + pub is_free: bool, + /// Queue details + pub queue_details: ServerQueueDetails, +} + +/// Server status response +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerStatusResponse { + /// Server name + pub name: String, + /// HTTP URL of the server + pub http_url: String, + /// WebSocket URL of the server + pub ws_url: String, + /// Server status + pub status: String, + /// Last heartbeat timestamp + pub last_heartbeat: Option, + /// Last health check timestamp + pub last_health_check: Option, + /// Current number of tasks + pub current_tasks: i32, + /// Maximum concurrent tasks + pub max_concurrent_tasks: i32, + /// Server capabilities + pub capabilities: HashMap, + /// Server metadata + pub metadata: HashMap, +} + +/// Server unregister request +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerUnregisterRequest { + /// Server name + pub name: String, +} diff --git a/cargos/uni-comfyui-sdk/src/types.rs b/cargos/uni-comfyui-sdk/src/types.rs new file mode 100644 index 0000000..1a0f627 --- /dev/null +++ b/cargos/uni-comfyui-sdk/src/types.rs @@ -0,0 +1,16 @@ +use std::collections::HashMap; + +/// Generic JSON object type +pub type JsonObject = HashMap; + +/// Generic response type for endpoints that return arbitrary JSON +pub type GenericResponse = serde_json::Value; + +/// Response type for endpoints that return a dictionary of strings +pub type StringDictResponse = HashMap; + +/// Response type for endpoints that return an array of objects +pub type ObjectArrayResponse = Vec; + +/// Response type for endpoints that return an object with additional properties +pub type AdditionalPropertiesResponse = JsonObject; diff --git a/cargos/uni-comfyui-sdk/tests/integration_tests.rs b/cargos/uni-comfyui-sdk/tests/integration_tests.rs new file mode 100644 index 0000000..4b09b04 --- /dev/null +++ b/cargos/uni-comfyui-sdk/tests/integration_tests.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; +use uni_comfyui_sdk::{UniComfyUIClient, Result, ServerRegistrationRequest}; + +// Note: These tests require a running ComfyUI service +// They are disabled by default and should be run manually when needed + +#[tokio::test] +#[ignore] +async fn test_client_creation() -> Result<()> { + let _client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + assert!(true); // Just test that client creation doesn't panic + Ok(()) +} + +#[tokio::test] +#[ignore] +async fn test_get_metrics() -> Result<()> { + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + let _metrics = client.get_metrics().await?; + // Just test that the call doesn't fail + Ok(()) +} + +#[tokio::test] +#[ignore] +async fn test_get_all_workflows() -> Result<()> { + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + let workflows = client.get_all_workflows().await?; + assert!(workflows.is_empty() || !workflows.is_empty()); // Just test the call + Ok(()) +} + +#[tokio::test] +#[ignore] +async fn test_health_check() -> Result<()> { + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + let _health = client.health_check().await?; + Ok(()) +} + +#[tokio::test] +#[ignore] +async fn test_server_registration() -> Result<()> { + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + + let registration_request = ServerRegistrationRequest { + name: "test-server".to_string(), + http_url: "http://192.168.0.148:8188".to_string(), + ws_url: "ws://192.168.0.148:8188".to_string(), + }; + + // This might fail if server already exists, which is fine for testing + let _result = client.register_server(registration_request).await; + Ok(()) +} + +#[tokio::test] +#[ignore] +async fn test_workflow_execution() -> Result<()> { + let client = UniComfyUIClient::new("http://192.168.0.148:18000")?; + + let mut workflow_data = HashMap::new(); + workflow_data.insert("test_input".to_string(), serde_json::json!("test_value")); + + // This will likely fail without a real workflow, but tests the API call + let _result = client + .run_workflow("test_workflow", None, workflow_data) + .await; + + Ok(()) +} + +#[test] +fn test_url_building() { + let _client = UniComfyUIClient::new("http://192.168.0.148:18000").unwrap(); + // Test that URL building works correctly + assert!(true); // Basic test that client creation works +} + +#[test] +fn test_error_types() { + use uni_comfyui_sdk::ComfyUIError; + + // Test that error types can be created + let _error = ComfyUIError::Validation("test error".to_string()); + let _error = ComfyUIError::ServerNotFound("test server".to_string()); + let _error = ComfyUIError::WorkflowNotFound("test workflow".to_string()); + + assert!(true); +} diff --git a/cargos/uni-comfyui-sdk/unicomfy.json b/cargos/uni-comfyui-sdk/unicomfy.json new file mode 100644 index 0000000..c3e93b5 --- /dev/null +++ b/cargos/uni-comfyui-sdk/unicomfy.json @@ -0,0 +1,1173 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "ComfyUI Workflow Service & Management API", + "version": "0.1.0" + }, + "paths": { + "/api/service/metrics": { + "get": { + "tags": [ + "Status" + ], + "summary": "Get Metrics", + "description": "获取队列状态概览。", + "operationId": "get_metrics_api_service_metrics_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/service/servers_status": { + "get": { + "tags": [ + "Status" + ], + "summary": "Get Servers Status", + "description": "获取所有已配置的ComfyUI服务器的配置信息和实时状态。", + "operationId": "get_servers_status_api_service_servers_status_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ServerStatus" + }, + "type": "array", + "title": "Response Get Servers Status Api Service Servers Status Get" + } + } + } + } + } + } + }, + "/api/service/servers/{server_index}/files": { + "get": { + "tags": [ + "Status" + ], + "summary": "List Server Files", + "description": "获取指定ComfyUI服务器的输入和输出文件夹中的文件列表。\n注意:由于节点管理不再维护目录,此接口返回空列表。", + "operationId": "list_server_files_api_service_servers__server_index__files_get", + "parameters": [ + { + "name": "server_index", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "minimum": 0, + "description": "服务器在配置列表中的索引", + "title": "Server Index" + }, + "description": "服务器在配置列表中的索引" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerFiles" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/workflow": { + "get": { + "tags": [ + "Workflow" + ], + "summary": "Get All Workflows Endpoint", + "description": "获取所有工作流", + "operationId": "get_all_workflows_endpoint_api_workflow_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Response Get All Workflows Endpoint Api Workflow Get" + } + } + } + } + } + }, + "post": { + "tags": [ + "Workflow" + ], + "summary": "Publish Workflow Endpoint", + "description": "发布工作流", + "operationId": "publish_workflow_endpoint_api_workflow_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Data" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/workflow/{workflow_name}": { + "delete": { + "tags": [ + "Workflow" + ], + "summary": "Delete Workflow Endpoint", + "description": "删除工作流", + "operationId": "delete_workflow_endpoint_api_workflow__workflow_name__delete", + "parameters": [ + { + "name": "workflow_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The full, unique name of the workflow to delete, e.g., 'my_workflow [20250101120000]'", + "title": "Workflow Name" + }, + "description": "The full, unique name of the workflow to delete, e.g., 'my_workflow [20250101120000]'" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/workflow/{base_name}": { + "get": { + "tags": [ + "Workflow" + ], + "summary": "Get One Workflow Endpoint", + "description": "获取工作流规范", + "operationId": "get_one_workflow_endpoint_api_workflow__base_name__get", + "parameters": [ + { + "name": "base_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Base Name" + } + }, + { + "name": "version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/run": { + "post": { + "tags": [ + "Run" + ], + "summary": "Run Workflow", + "description": "异步执行工作流。\n立即返回任务ID,调用者可以通过任务ID查询执行状态。", + "operationId": "run_workflow_api_run_post", + "parameters": [ + { + "name": "workflow_name", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Workflow Name" + } + }, + { + "name": "workflow_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Workflow Version" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/run/{workflow_run_id}": { + "get": { + "tags": [ + "Run" + ], + "summary": "Get Run Status", + "description": "获取工作流执行状态。", + "operationId": "get_run_status_api_run__workflow_run_id__get", + "parameters": [ + { + "name": "workflow_run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Workflow Run Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/runx/model_with_multi_dress": { + "post": { + "tags": [ + "RunX" + ], + "summary": "Model With Multi Dress", + "description": "一个模特,穿多件不同的衣服", + "operationId": "model_with_multi_dress_api_runx_model_with_multi_dress_post", + "parameters": [ + { + "name": "workflow_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Workflow Version" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/runx/model_with_multi_dress/json_schema": { + "get": { + "tags": [ + "RunX" + ], + "summary": "Model With Multi Dress Json Schema", + "description": "获取工作流定义", + "operationId": "model_with_multi_dress_json_schema_api_runx_model_with_multi_dress_json_schema_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/monitor/": { + "get": { + "tags": [ + "监控" + ], + "summary": "Monitor Dashboard", + "description": "监控仪表板页面", + "operationId": "monitor_dashboard_monitor__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "text/html": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/monitor/system-stats": { + "get": { + "tags": [ + "监控" + ], + "summary": "Get System Stats", + "description": "获取系统统计信息", + "operationId": "get_system_stats_monitor_system_stats_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Response Get System Stats Monitor System Stats Get" + } + } + } + } + } + } + }, + "/monitor/task-stats": { + "get": { + "tags": [ + "监控" + ], + "summary": "Get Task Stats", + "description": "获取任务统计信息", + "operationId": "get_task_stats_monitor_task_stats_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Response Get Task Stats Monitor Task Stats Get" + } + } + } + } + } + } + }, + "/monitor/server-status": { + "get": { + "tags": [ + "监控" + ], + "summary": "Get Server Status", + "description": "获取服务器状态信息", + "operationId": "get_server_status_monitor_server_status_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Response Get Server Status Monitor Server Status Get" + } + } + } + } + } + } + }, + "/monitor/recent-tasks": { + "get": { + "tags": [ + "监控" + ], + "summary": "Get Recent Tasks", + "description": "获取最近的任务列表", + "operationId": "get_recent_tasks_monitor_recent_tasks_get", + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 10, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + }, + "title": "Response Get Recent Tasks Monitor Recent Tasks Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/monitor/health": { + "get": { + "tags": [ + "监控" + ], + "summary": "Health Check", + "description": "健康检查端点", + "operationId": "health_check_monitor_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Response Health Check Monitor Health Get" + } + } + } + } + } + } + }, + "/api/comfy/register": { + "post": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "Register Server", + "description": "注册ComfyUI服务器", + "operationId": "register_server_api_comfy_register_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerRegistrationRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Response Register Server Api Comfy Register Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/comfy/heartbeat": { + "post": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "Update Heartbeat", + "description": "更新服务器心跳", + "operationId": "update_heartbeat_api_comfy_heartbeat_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerHeartbeatRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Response Update Heartbeat Api Comfy Heartbeat Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/comfy/unregister": { + "post": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "Unregister Server", + "description": "注销ComfyUI服务器", + "operationId": "unregister_server_api_comfy_unregister_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerUnregisterRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Response Unregister Server Api Comfy Unregister Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/comfy/status/{server_name}": { + "get": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "Get Server Status", + "description": "获取指定服务器状态", + "operationId": "get_server_status_api_comfy_status__server_name__get", + "parameters": [ + { + "name": "server_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Server Name" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerStatusResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/comfy/list": { + "get": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "List All Servers", + "description": "获取所有服务器列表", + "operationId": "list_all_servers_api_comfy_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ServerStatusResponse" + }, + "type": "array", + "title": "Response List All Servers Api Comfy List Get" + } + } + } + } + } + } + }, + "/api/comfy/health": { + "get": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "Get System Health", + "description": "获取系统整体健康状态", + "operationId": "get_system_health_api_comfy_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Response Get System Health Api Comfy Health Get" + } + } + } + } + } + } + }, + "/api/comfy/force_health_check": { + "post": { + "tags": [ + "ComfyUI Server Management" + ], + "summary": "Force Health Check", + "description": "强制执行健康检查", + "operationId": "force_health_check_api_comfy_force_health_check_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Response Force Health Check Api Comfy Force Health Check Post" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "FileDetails": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "size_kb": { + "type": "number", + "title": "Size Kb" + }, + "modified_at": { + "type": "string", + "format": "date-time", + "title": "Modified At" + } + }, + "type": "object", + "required": [ + "name", + "size_kb", + "modified_at" + ], + "title": "FileDetails" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "ServerFiles": { + "properties": { + "server_index": { + "type": "integer", + "title": "Server Index" + }, + "http_url": { + "type": "string", + "title": "Http Url" + }, + "input_files": { + "items": { + "$ref": "#/components/schemas/FileDetails" + }, + "type": "array", + "title": "Input Files" + }, + "output_files": { + "items": { + "$ref": "#/components/schemas/FileDetails" + }, + "type": "array", + "title": "Output Files" + } + }, + "type": "object", + "required": [ + "server_index", + "http_url", + "input_files", + "output_files" + ], + "title": "ServerFiles" + }, + "ServerHeartbeatRequest": { + "properties": { + "name": { + "type": "string", + "title": "Name" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "ServerHeartbeatRequest", + "description": "服务器心跳请求模型" + }, + "ServerQueueDetails": { + "properties": { + "running_count": { + "type": "integer", + "title": "Running Count" + }, + "pending_count": { + "type": "integer", + "title": "Pending Count" + } + }, + "type": "object", + "required": [ + "running_count", + "pending_count" + ], + "title": "ServerQueueDetails" + }, + "ServerRegistrationRequest": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "http_url": { + "type": "string", + "title": "Http Url" + }, + "ws_url": { + "type": "string", + "title": "Ws Url" + } + }, + "type": "object", + "required": [ + "name", + "http_url", + "ws_url" + ], + "title": "ServerRegistrationRequest", + "description": "服务器注册请求模型" + }, + "ServerStatus": { + "properties": { + "server_index": { + "type": "integer", + "title": "Server Index" + }, + "http_url": { + "type": "string", + "title": "Http Url" + }, + "ws_url": { + "type": "string", + "title": "Ws Url" + }, + "is_reachable": { + "type": "boolean", + "title": "Is Reachable" + }, + "is_free": { + "type": "boolean", + "title": "Is Free" + }, + "queue_details": { + "$ref": "#/components/schemas/ServerQueueDetails" + } + }, + "type": "object", + "required": [ + "server_index", + "http_url", + "ws_url", + "is_reachable", + "is_free", + "queue_details" + ], + "title": "ServerStatus" + }, + "ServerStatusResponse": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "http_url": { + "type": "string", + "title": "Http Url" + }, + "ws_url": { + "type": "string", + "title": "Ws Url" + }, + "status": { + "type": "string", + "title": "Status" + }, + "last_heartbeat": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Heartbeat" + }, + "last_health_check": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Health Check" + }, + "current_tasks": { + "type": "integer", + "title": "Current Tasks" + }, + "max_concurrent_tasks": { + "type": "integer", + "title": "Max Concurrent Tasks" + }, + "capabilities": { + "additionalProperties": true, + "type": "object", + "title": "Capabilities" + }, + "metadata": { + "additionalProperties": true, + "type": "object", + "title": "Metadata" + } + }, + "type": "object", + "required": [ + "name", + "http_url", + "ws_url", + "status", + "current_tasks", + "max_concurrent_tasks", + "capabilities", + "metadata" + ], + "title": "ServerStatusResponse", + "description": "服务器状态响应模型" + }, + "ServerUnregisterRequest": { + "properties": { + "name": { + "type": "string", + "title": "Name" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "ServerUnregisterRequest", + "description": "服务器注销请求模型" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + } + } +} \ No newline at end of file