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
This commit is contained in:
imeepos
2025-08-15 15:31:33 +08:00
parent 5380d9973f
commit 08b981c31f
12 changed files with 2193 additions and 0 deletions

View File

@@ -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(())
}