comfy-test 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comfy_test/__init__.py +94 -0
- comfy_test/cli.py +277 -0
- comfy_test/comfyui/__init__.py +11 -0
- comfy_test/comfyui/api.py +184 -0
- comfy_test/comfyui/server.py +154 -0
- comfy_test/comfyui/workflow.py +191 -0
- comfy_test/errors.py +77 -0
- comfy_test/github/__init__.py +1 -0
- comfy_test/runner.py +81 -0
- comfy_test/test/__init__.py +13 -0
- comfy_test/test/config.py +131 -0
- comfy_test/test/config_file.py +250 -0
- comfy_test/test/manager.py +197 -0
- comfy_test/test/platform/__init__.py +69 -0
- comfy_test/test/platform/base.py +200 -0
- comfy_test/test/platform/linux.py +183 -0
- comfy_test/test/platform/windows.py +186 -0
- comfy_test/test/platform/windows_portable.py +272 -0
- comfy_test-0.0.1.dist-info/METADATA +128 -0
- comfy_test-0.0.1.dist-info/RECORD +23 -0
- comfy_test-0.0.1.dist-info/WHEEL +4 -0
- comfy_test-0.0.1.dist-info/entry_points.txt +2 -0
- comfy_test-0.0.1.dist-info/licenses/LICENSE +21 -0
comfy_test/__init__.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
comfy-test: Installation testing infrastructure for ComfyUI custom nodes.
|
|
3
|
+
|
|
4
|
+
This package provides:
|
|
5
|
+
- Multi-platform installation testing (Linux, Windows, Windows Portable)
|
|
6
|
+
- Workflow execution verification
|
|
7
|
+
- GitHub Actions integration
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
from comfy_test import run_tests, verify_nodes
|
|
12
|
+
|
|
13
|
+
# Run all tests from config
|
|
14
|
+
results = run_tests()
|
|
15
|
+
|
|
16
|
+
# Or verify nodes only
|
|
17
|
+
results = verify_nodes()
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
comfy-test run # Run installation tests
|
|
22
|
+
comfy-test verify # Verify node registration
|
|
23
|
+
comfy-test info # Show configuration
|
|
24
|
+
comfy-test init-ci # Generate GitHub Actions workflow
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
Create comfy-test.toml in your custom node directory:
|
|
29
|
+
|
|
30
|
+
[test]
|
|
31
|
+
name = "MyNode"
|
|
32
|
+
expected_nodes = ["MyNode1", "MyNode2"]
|
|
33
|
+
|
|
34
|
+
[test.workflow]
|
|
35
|
+
file = "tests/workflows/smoke_test.json"
|
|
36
|
+
|
|
37
|
+
## GitHub Actions
|
|
38
|
+
|
|
39
|
+
Add this workflow to your repository:
|
|
40
|
+
|
|
41
|
+
# .github/workflows/test-install.yml
|
|
42
|
+
name: Test Installation
|
|
43
|
+
on: [push, pull_request]
|
|
44
|
+
|
|
45
|
+
jobs:
|
|
46
|
+
test:
|
|
47
|
+
uses: PozzettiAndrea/comfy-test/.github/workflows/test-matrix.yml@main
|
|
48
|
+
with:
|
|
49
|
+
config-file: "comfy-test.toml"
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
__version__ = "0.0.1"
|
|
53
|
+
|
|
54
|
+
from .test.config import TestConfig, WorkflowConfig, PlatformTestConfig
|
|
55
|
+
from .test.config_file import load_config, discover_config, CONFIG_FILE_NAMES
|
|
56
|
+
from .test.manager import TestManager, TestResult
|
|
57
|
+
from .errors import (
|
|
58
|
+
TestError,
|
|
59
|
+
ConfigError,
|
|
60
|
+
SetupError,
|
|
61
|
+
ServerError,
|
|
62
|
+
WorkflowError,
|
|
63
|
+
VerificationError,
|
|
64
|
+
TimeoutError,
|
|
65
|
+
DownloadError,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Convenience functions
|
|
69
|
+
from .runner import run_tests, verify_nodes
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
# Config
|
|
73
|
+
"TestConfig",
|
|
74
|
+
"WorkflowConfig",
|
|
75
|
+
"PlatformTestConfig",
|
|
76
|
+
"load_config",
|
|
77
|
+
"discover_config",
|
|
78
|
+
"CONFIG_FILE_NAMES",
|
|
79
|
+
# Manager
|
|
80
|
+
"TestManager",
|
|
81
|
+
"TestResult",
|
|
82
|
+
# Errors
|
|
83
|
+
"TestError",
|
|
84
|
+
"ConfigError",
|
|
85
|
+
"SetupError",
|
|
86
|
+
"ServerError",
|
|
87
|
+
"WorkflowError",
|
|
88
|
+
"VerificationError",
|
|
89
|
+
"TimeoutError",
|
|
90
|
+
"DownloadError",
|
|
91
|
+
# Convenience
|
|
92
|
+
"run_tests",
|
|
93
|
+
"verify_nodes",
|
|
94
|
+
]
|
comfy_test/cli.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""CLI for comfy-test."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .test.config_file import discover_config, load_config, CONFIG_FILE_NAMES
|
|
8
|
+
from .test.manager import TestManager
|
|
9
|
+
from .errors import TestError, ConfigError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_run(args) -> int:
|
|
13
|
+
"""Run installation tests."""
|
|
14
|
+
try:
|
|
15
|
+
# Load config
|
|
16
|
+
if args.config:
|
|
17
|
+
config = load_config(args.config)
|
|
18
|
+
else:
|
|
19
|
+
config = discover_config()
|
|
20
|
+
|
|
21
|
+
# Create manager
|
|
22
|
+
manager = TestManager(config)
|
|
23
|
+
|
|
24
|
+
# Run tests
|
|
25
|
+
if args.platform:
|
|
26
|
+
results = [manager.run_platform(args.platform, args.dry_run)]
|
|
27
|
+
else:
|
|
28
|
+
results = manager.run_all(args.dry_run)
|
|
29
|
+
|
|
30
|
+
# Report results
|
|
31
|
+
print(f"\n{'='*60}")
|
|
32
|
+
print("RESULTS")
|
|
33
|
+
print(f"{'='*60}")
|
|
34
|
+
|
|
35
|
+
all_passed = True
|
|
36
|
+
for result in results:
|
|
37
|
+
status = "PASS" if result.success else "FAIL"
|
|
38
|
+
print(f" {result.platform}: {status}")
|
|
39
|
+
if not result.success:
|
|
40
|
+
all_passed = False
|
|
41
|
+
if result.error:
|
|
42
|
+
print(f" Error: {result.error}")
|
|
43
|
+
|
|
44
|
+
return 0 if all_passed else 1
|
|
45
|
+
|
|
46
|
+
except ConfigError as e:
|
|
47
|
+
print(f"Configuration error: {e.message}", file=sys.stderr)
|
|
48
|
+
if e.details:
|
|
49
|
+
print(f"Details: {e.details}", file=sys.stderr)
|
|
50
|
+
return 1
|
|
51
|
+
except TestError as e:
|
|
52
|
+
print(f"Test error: {e.message}", file=sys.stderr)
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def cmd_verify(args) -> int:
|
|
57
|
+
"""Verify node registration only."""
|
|
58
|
+
try:
|
|
59
|
+
if args.config:
|
|
60
|
+
config = load_config(args.config)
|
|
61
|
+
else:
|
|
62
|
+
config = discover_config()
|
|
63
|
+
|
|
64
|
+
manager = TestManager(config)
|
|
65
|
+
results = manager.verify_only(args.platform)
|
|
66
|
+
|
|
67
|
+
all_passed = all(r.success for r in results)
|
|
68
|
+
for result in results:
|
|
69
|
+
status = "PASS" if result.success else "FAIL"
|
|
70
|
+
print(f"{result.platform}: {status}")
|
|
71
|
+
if not result.success and result.error:
|
|
72
|
+
print(f" Error: {result.error}")
|
|
73
|
+
|
|
74
|
+
return 0 if all_passed else 1
|
|
75
|
+
|
|
76
|
+
except (ConfigError, TestError) as e:
|
|
77
|
+
print(f"Error: {e.message}", file=sys.stderr)
|
|
78
|
+
return 1
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def cmd_info(args) -> int:
|
|
82
|
+
"""Show configuration and environment info."""
|
|
83
|
+
try:
|
|
84
|
+
if args.config:
|
|
85
|
+
config = load_config(args.config)
|
|
86
|
+
config_path = args.config
|
|
87
|
+
else:
|
|
88
|
+
try:
|
|
89
|
+
config = discover_config()
|
|
90
|
+
config_path = "auto-discovered"
|
|
91
|
+
except ConfigError:
|
|
92
|
+
print("No configuration file found.")
|
|
93
|
+
print(f"Searched for: {', '.join(CONFIG_FILE_NAMES)}")
|
|
94
|
+
return 1
|
|
95
|
+
|
|
96
|
+
print(f"Configuration: {config_path}")
|
|
97
|
+
print(f" Name: {config.name}")
|
|
98
|
+
print(f" ComfyUI Version: {config.comfyui_version}")
|
|
99
|
+
print(f" Python Version: {config.python_version}")
|
|
100
|
+
print(f" CPU Only: {config.cpu_only}")
|
|
101
|
+
print(f" Timeout: {config.timeout}s")
|
|
102
|
+
print()
|
|
103
|
+
print("Platforms:")
|
|
104
|
+
print(f" Linux: {'enabled' if config.linux.enabled else 'disabled'}")
|
|
105
|
+
print(f" Windows: {'enabled' if config.windows.enabled else 'disabled'}")
|
|
106
|
+
print(f" Windows Portable: {'enabled' if config.windows_portable.enabled else 'disabled'}")
|
|
107
|
+
print()
|
|
108
|
+
print("Verification:")
|
|
109
|
+
if config.expected_nodes:
|
|
110
|
+
print(f" Expected nodes ({len(config.expected_nodes)}):")
|
|
111
|
+
for node in config.expected_nodes:
|
|
112
|
+
print(f" - {node}")
|
|
113
|
+
else:
|
|
114
|
+
print(" No expected nodes configured")
|
|
115
|
+
print()
|
|
116
|
+
print("Workflow:")
|
|
117
|
+
if config.workflow.file:
|
|
118
|
+
print(f" File: {config.workflow.file}")
|
|
119
|
+
print(f" Timeout: {config.workflow.timeout}s")
|
|
120
|
+
else:
|
|
121
|
+
print(" No workflow configured")
|
|
122
|
+
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
except ConfigError as e:
|
|
126
|
+
print(f"Error: {e.message}", file=sys.stderr)
|
|
127
|
+
return 1
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def cmd_init_ci(args) -> int:
|
|
131
|
+
"""Generate GitHub Actions workflow file."""
|
|
132
|
+
output_path = Path(args.output)
|
|
133
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
134
|
+
|
|
135
|
+
workflow_content = '''name: Test Installation
|
|
136
|
+
on: [push, pull_request]
|
|
137
|
+
|
|
138
|
+
jobs:
|
|
139
|
+
test:
|
|
140
|
+
uses: PozzettiAndrea/comfy-test/.github/workflows/test-matrix.yml@main
|
|
141
|
+
with:
|
|
142
|
+
config-file: "comfy-test.toml"
|
|
143
|
+
'''
|
|
144
|
+
|
|
145
|
+
with open(output_path, "w") as f:
|
|
146
|
+
f.write(workflow_content)
|
|
147
|
+
|
|
148
|
+
print(f"Generated GitHub Actions workflow: {output_path}")
|
|
149
|
+
print()
|
|
150
|
+
print("Make sure to:")
|
|
151
|
+
print(" 1. Create a comfy-test.toml in your repository root")
|
|
152
|
+
print(" 2. Commit both files to your repository")
|
|
153
|
+
print()
|
|
154
|
+
print("Example comfy-test.toml:")
|
|
155
|
+
print('''
|
|
156
|
+
[test]
|
|
157
|
+
name = "MyNode"
|
|
158
|
+
python_version = "3.10"
|
|
159
|
+
|
|
160
|
+
[test.verification]
|
|
161
|
+
expected_nodes = ["MyNode1", "MyNode2"]
|
|
162
|
+
''')
|
|
163
|
+
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def cmd_download_portable(args) -> int:
|
|
168
|
+
"""Download ComfyUI Portable for testing."""
|
|
169
|
+
from .test.platform.windows_portable import WindowsPortableTestPlatform
|
|
170
|
+
|
|
171
|
+
platform = WindowsPortableTestPlatform()
|
|
172
|
+
|
|
173
|
+
version = args.version
|
|
174
|
+
if version == "latest":
|
|
175
|
+
version = platform._get_latest_release_tag()
|
|
176
|
+
|
|
177
|
+
output_path = Path(args.output)
|
|
178
|
+
archive_path = output_path / f"ComfyUI_portable_{version}.7z"
|
|
179
|
+
|
|
180
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
181
|
+
platform._download_portable(version, archive_path)
|
|
182
|
+
|
|
183
|
+
print(f"Downloaded to: {archive_path}")
|
|
184
|
+
return 0
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def main(args=None) -> int:
|
|
188
|
+
"""Main CLI entry point."""
|
|
189
|
+
parser = argparse.ArgumentParser(
|
|
190
|
+
prog="comfy-test",
|
|
191
|
+
description="Installation testing for ComfyUI custom nodes",
|
|
192
|
+
)
|
|
193
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
194
|
+
|
|
195
|
+
# run command
|
|
196
|
+
run_parser = subparsers.add_parser(
|
|
197
|
+
"run",
|
|
198
|
+
help="Run installation tests",
|
|
199
|
+
)
|
|
200
|
+
run_parser.add_argument(
|
|
201
|
+
"--config", "-c",
|
|
202
|
+
help="Path to config file (default: auto-discover)",
|
|
203
|
+
)
|
|
204
|
+
run_parser.add_argument(
|
|
205
|
+
"--platform", "-p",
|
|
206
|
+
choices=["linux", "windows", "windows-portable"],
|
|
207
|
+
help="Run on specific platform only",
|
|
208
|
+
)
|
|
209
|
+
run_parser.add_argument(
|
|
210
|
+
"--dry-run",
|
|
211
|
+
action="store_true",
|
|
212
|
+
help="Show what would be done without doing it",
|
|
213
|
+
)
|
|
214
|
+
run_parser.set_defaults(func=cmd_run)
|
|
215
|
+
|
|
216
|
+
# verify command
|
|
217
|
+
verify_parser = subparsers.add_parser(
|
|
218
|
+
"verify",
|
|
219
|
+
help="Verify node registration only",
|
|
220
|
+
)
|
|
221
|
+
verify_parser.add_argument(
|
|
222
|
+
"--config", "-c",
|
|
223
|
+
help="Path to config file",
|
|
224
|
+
)
|
|
225
|
+
verify_parser.add_argument(
|
|
226
|
+
"--platform", "-p",
|
|
227
|
+
choices=["linux", "windows", "windows-portable"],
|
|
228
|
+
help="Platform to verify on",
|
|
229
|
+
)
|
|
230
|
+
verify_parser.set_defaults(func=cmd_verify)
|
|
231
|
+
|
|
232
|
+
# info command
|
|
233
|
+
info_parser = subparsers.add_parser(
|
|
234
|
+
"info",
|
|
235
|
+
help="Show configuration info",
|
|
236
|
+
)
|
|
237
|
+
info_parser.add_argument(
|
|
238
|
+
"--config", "-c",
|
|
239
|
+
help="Path to config file",
|
|
240
|
+
)
|
|
241
|
+
info_parser.set_defaults(func=cmd_info)
|
|
242
|
+
|
|
243
|
+
# init-ci command
|
|
244
|
+
init_ci_parser = subparsers.add_parser(
|
|
245
|
+
"init-ci",
|
|
246
|
+
help="Generate GitHub Actions workflow",
|
|
247
|
+
)
|
|
248
|
+
init_ci_parser.add_argument(
|
|
249
|
+
"--output", "-o",
|
|
250
|
+
default=".github/workflows/test-install.yml",
|
|
251
|
+
help="Output file path",
|
|
252
|
+
)
|
|
253
|
+
init_ci_parser.set_defaults(func=cmd_init_ci)
|
|
254
|
+
|
|
255
|
+
# download-portable command
|
|
256
|
+
download_parser = subparsers.add_parser(
|
|
257
|
+
"download-portable",
|
|
258
|
+
help="Download ComfyUI Portable",
|
|
259
|
+
)
|
|
260
|
+
download_parser.add_argument(
|
|
261
|
+
"--version", "-v",
|
|
262
|
+
default="latest",
|
|
263
|
+
help="Version to download (default: latest)",
|
|
264
|
+
)
|
|
265
|
+
download_parser.add_argument(
|
|
266
|
+
"--output", "-o",
|
|
267
|
+
default=".",
|
|
268
|
+
help="Output directory",
|
|
269
|
+
)
|
|
270
|
+
download_parser.set_defaults(func=cmd_download_portable)
|
|
271
|
+
|
|
272
|
+
parsed_args = parser.parse_args(args)
|
|
273
|
+
return parsed_args.func(parsed_args)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
sys.exit(main())
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""ComfyUI REST API client."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from ..errors import ServerError, VerificationError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ComfyUIAPI:
|
|
12
|
+
"""Client for ComfyUI REST API.
|
|
13
|
+
|
|
14
|
+
Provides methods to interact with a running ComfyUI server.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
base_url: Base URL of the ComfyUI server (e.g., "http://127.0.0.1:8188")
|
|
18
|
+
timeout: Request timeout in seconds
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> api = ComfyUIAPI("http://127.0.0.1:8188")
|
|
22
|
+
>>> nodes = api.get_object_info()
|
|
23
|
+
>>> print(list(nodes.keys())[:5])
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, base_url: str = "http://127.0.0.1:8188", timeout: int = 30):
|
|
27
|
+
self.base_url = base_url.rstrip("/")
|
|
28
|
+
self.timeout = timeout
|
|
29
|
+
self.session = requests.Session()
|
|
30
|
+
|
|
31
|
+
def health_check(self) -> bool:
|
|
32
|
+
"""Check if the server is responsive.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
True if server responds, False otherwise
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
response = self.session.get(
|
|
39
|
+
f"{self.base_url}/system_stats",
|
|
40
|
+
timeout=self.timeout,
|
|
41
|
+
)
|
|
42
|
+
return response.status_code == 200
|
|
43
|
+
except requests.RequestException:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def get_object_info(self) -> Dict[str, Any]:
|
|
47
|
+
"""Get information about all registered nodes.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Dictionary mapping node names to their info
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
ServerError: If request fails
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
response = self.session.get(
|
|
57
|
+
f"{self.base_url}/object_info",
|
|
58
|
+
timeout=self.timeout,
|
|
59
|
+
)
|
|
60
|
+
response.raise_for_status()
|
|
61
|
+
return response.json()
|
|
62
|
+
except requests.RequestException as e:
|
|
63
|
+
raise ServerError(
|
|
64
|
+
"Failed to get object_info from ComfyUI",
|
|
65
|
+
str(e)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def verify_nodes(self, expected_nodes: List[str]) -> None:
|
|
69
|
+
"""Verify that expected nodes are registered.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
expected_nodes: List of node names that must exist
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
VerificationError: If any expected nodes are missing
|
|
76
|
+
"""
|
|
77
|
+
nodes = self.get_object_info()
|
|
78
|
+
missing = [name for name in expected_nodes if name not in nodes]
|
|
79
|
+
|
|
80
|
+
if missing:
|
|
81
|
+
raise VerificationError(
|
|
82
|
+
f"Expected nodes not found: {', '.join(missing)}",
|
|
83
|
+
missing_nodes=missing,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def queue_prompt(self, workflow: Dict[str, Any]) -> str:
|
|
87
|
+
"""Queue a workflow for execution.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
workflow: Workflow definition (the "prompt" part of a workflow JSON)
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Prompt ID for tracking execution
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
ServerError: If request fails
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
response = self.session.post(
|
|
100
|
+
f"{self.base_url}/prompt",
|
|
101
|
+
json={"prompt": workflow},
|
|
102
|
+
timeout=self.timeout,
|
|
103
|
+
)
|
|
104
|
+
response.raise_for_status()
|
|
105
|
+
data = response.json()
|
|
106
|
+
return data["prompt_id"]
|
|
107
|
+
except requests.RequestException as e:
|
|
108
|
+
raise ServerError(
|
|
109
|
+
"Failed to queue prompt",
|
|
110
|
+
str(e)
|
|
111
|
+
)
|
|
112
|
+
except KeyError:
|
|
113
|
+
raise ServerError(
|
|
114
|
+
"Invalid response from /prompt endpoint",
|
|
115
|
+
"Missing prompt_id in response"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def get_history(self, prompt_id: str) -> Optional[Dict[str, Any]]:
|
|
119
|
+
"""Get execution history for a prompt.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
prompt_id: ID from queue_prompt
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
History data if available, None if prompt hasn't started
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
ServerError: If request fails
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
response = self.session.get(
|
|
132
|
+
f"{self.base_url}/history/{prompt_id}",
|
|
133
|
+
timeout=self.timeout,
|
|
134
|
+
)
|
|
135
|
+
response.raise_for_status()
|
|
136
|
+
data = response.json()
|
|
137
|
+
return data.get(prompt_id)
|
|
138
|
+
except requests.RequestException as e:
|
|
139
|
+
raise ServerError(
|
|
140
|
+
f"Failed to get history for prompt {prompt_id}",
|
|
141
|
+
str(e)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def get_queue(self) -> Dict[str, Any]:
|
|
145
|
+
"""Get current queue status.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Queue data with 'queue_running' and 'queue_pending'
|
|
149
|
+
|
|
150
|
+
Raises:
|
|
151
|
+
ServerError: If request fails
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
response = self.session.get(
|
|
155
|
+
f"{self.base_url}/queue",
|
|
156
|
+
timeout=self.timeout,
|
|
157
|
+
)
|
|
158
|
+
response.raise_for_status()
|
|
159
|
+
return response.json()
|
|
160
|
+
except requests.RequestException as e:
|
|
161
|
+
raise ServerError(
|
|
162
|
+
"Failed to get queue status",
|
|
163
|
+
str(e)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def interrupt(self) -> None:
|
|
167
|
+
"""Interrupt currently running workflow."""
|
|
168
|
+
try:
|
|
169
|
+
self.session.post(
|
|
170
|
+
f"{self.base_url}/interrupt",
|
|
171
|
+
timeout=self.timeout,
|
|
172
|
+
)
|
|
173
|
+
except requests.RequestException:
|
|
174
|
+
pass # Best effort
|
|
175
|
+
|
|
176
|
+
def close(self) -> None:
|
|
177
|
+
"""Close the session."""
|
|
178
|
+
self.session.close()
|
|
179
|
+
|
|
180
|
+
def __enter__(self) -> "ComfyUIAPI":
|
|
181
|
+
return self
|
|
182
|
+
|
|
183
|
+
def __exit__(self, *args) -> None:
|
|
184
|
+
self.close()
|