Skip to content

Commit 3535909

Browse files
Add support for Comfy API keys (#8041)
* Handle Comfy API key based authorizaton (#167) Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com> * Bump frontend version to include API key features (#170) * bump templates version --------- Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
1 parent 235d390 commit 3535909

15 files changed

+319
-224
lines changed

comfy_api_nodes/apinode_utils.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from __future__ import annotations
12
import io
23
import logging
34
from typing import Optional
@@ -314,7 +315,7 @@ def upload_file_to_comfyapi(
314315
file_bytes_io: BytesIO,
315316
filename: str,
316317
upload_mime_type: str,
317-
auth_token: Optional[str] = None,
318+
auth_kwargs: Optional[dict[str,str]] = None,
318319
) -> str:
319320
"""
320321
Uploads a single file to ComfyUI API and returns its download URL.
@@ -323,7 +324,7 @@ def upload_file_to_comfyapi(
323324
file_bytes_io: BytesIO object containing the file data.
324325
filename: The filename of the file.
325326
upload_mime_type: MIME type of the file.
326-
auth_token: Optional authentication token.
327+
auth_kwargs: Optional authentication token(s).
327328
328329
Returns:
329330
The download URL for the uploaded file.
@@ -337,7 +338,7 @@ def upload_file_to_comfyapi(
337338
response_model=UploadResponse,
338339
),
339340
request=request_object,
340-
auth_token=auth_token,
341+
auth_kwargs=auth_kwargs,
341342
)
342343

343344
response: UploadResponse = operation.execute()
@@ -351,7 +352,7 @@ def upload_file_to_comfyapi(
351352

352353
def upload_video_to_comfyapi(
353354
video: VideoInput,
354-
auth_token: Optional[str] = None,
355+
auth_kwargs: Optional[dict[str,str]] = None,
355356
container: VideoContainer = VideoContainer.MP4,
356357
codec: VideoCodec = VideoCodec.H264,
357358
max_duration: Optional[int] = None,
@@ -362,7 +363,7 @@ def upload_video_to_comfyapi(
362363
363364
Args:
364365
video: VideoInput object (Comfy VIDEO type).
365-
auth_token: Optional authentication token.
366+
auth_kwargs: Optional authentication token(s).
366367
container: The video container format to use (default: MP4).
367368
codec: The video codec to use (default: H264).
368369
max_duration: Optional maximum duration of the video in seconds. If the video is longer than this, an error will be raised.
@@ -390,7 +391,7 @@ def upload_video_to_comfyapi(
390391
video_bytes_io.seek(0)
391392

392393
return upload_file_to_comfyapi(
393-
video_bytes_io, filename, upload_mime_type, auth_token
394+
video_bytes_io, filename, upload_mime_type, auth_kwargs
394395
)
395396

396397

@@ -453,7 +454,7 @@ def audio_ndarray_to_bytesio(
453454

454455
def upload_audio_to_comfyapi(
455456
audio: AudioInput,
456-
auth_token: Optional[str] = None,
457+
auth_kwargs: Optional[dict[str,str]] = None,
457458
container_format: str = "mp4",
458459
codec_name: str = "aac",
459460
mime_type: str = "audio/mp4",
@@ -465,7 +466,7 @@ def upload_audio_to_comfyapi(
465466
466467
Args:
467468
audio: a Comfy `AUDIO` type (contains waveform tensor and sample_rate)
468-
auth_token: Optional authentication token.
469+
auth_kwargs: Optional authentication token(s).
469470
470471
Returns:
471472
The download URL for the uploaded audio file.
@@ -477,11 +478,11 @@ def upload_audio_to_comfyapi(
477478
audio_data_np, sample_rate, container_format, codec_name
478479
)
479480

480-
return upload_file_to_comfyapi(audio_bytes_io, filename, mime_type, auth_token)
481+
return upload_file_to_comfyapi(audio_bytes_io, filename, mime_type, auth_kwargs)
481482

482483

483484
def upload_images_to_comfyapi(
484-
image: torch.Tensor, max_images=8, auth_token=None, mime_type: Optional[str] = None
485+
image: torch.Tensor, max_images=8, auth_kwargs: Optional[dict[str,str]] = None, mime_type: Optional[str] = None
485486
) -> list[str]:
486487
"""
487488
Uploads images to ComfyUI API and returns download URLs.
@@ -490,7 +491,7 @@ def upload_images_to_comfyapi(
490491
Args:
491492
image: Input torch.Tensor image.
492493
max_images: Maximum number of images to upload.
493-
auth_token: Optional authentication token.
494+
auth_kwargs: Optional authentication token(s).
494495
mime_type: Optional MIME type for the image.
495496
"""
496497
# if batch, try to upload each file if max_images is greater than 0
@@ -521,7 +522,7 @@ def upload_images_to_comfyapi(
521522
response_model=UploadResponse,
522523
),
523524
request=request_object,
524-
auth_token=auth_token,
525+
auth_kwargs=auth_kwargs,
525526
)
526527
response = operation.execute()
527528

comfy_api_nodes/apis/client.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
# 1. Create the API client
2121
api_client = ApiClient(
2222
base_url="https://api.example.com",
23-
api_key="your_api_key_here",
23+
auth_token="your_auth_token_here",
24+
comfy_api_key="your_comfy_api_key_here",
2425
timeout=30.0,
2526
verify_ssl=True
2627
)
@@ -146,12 +147,14 @@ class ApiClient:
146147
def __init__(
147148
self,
148149
base_url: str,
149-
api_key: Optional[str] = None,
150+
auth_token: Optional[str] = None,
151+
comfy_api_key: Optional[str] = None,
150152
timeout: float = 3600.0,
151153
verify_ssl: bool = True,
152154
):
153155
self.base_url = base_url
154-
self.api_key = api_key
156+
self.auth_token = auth_token
157+
self.comfy_api_key = comfy_api_key
155158
self.timeout = timeout
156159
self.verify_ssl = verify_ssl
157160

@@ -201,8 +204,10 @@ def get_headers(self) -> Dict[str, str]:
201204
"""Get headers for API requests, including authentication if available"""
202205
headers = {"Content-Type": "application/json", "Accept": "application/json"}
203206

204-
if self.api_key:
205-
headers["Authorization"] = f"Bearer {self.api_key}"
207+
if self.auth_token:
208+
headers["Authorization"] = f"Bearer {self.auth_token}"
209+
elif self.comfy_api_key:
210+
headers["X-API-KEY"] = self.comfy_api_key
206211

207212
return headers
208213

@@ -236,7 +241,7 @@ def request(
236241
requests.RequestException: If the request fails
237242
"""
238243
url = urljoin(self.base_url, path)
239-
self.check_auth_token(self.api_key)
244+
self.check_auth(self.auth_token, self.comfy_api_key)
240245
# Combine default headers with any provided headers
241246
request_headers = self.get_headers()
242247
if headers:
@@ -320,11 +325,11 @@ def request(
320325
return response.json()
321326
return {}
322327

323-
def check_auth_token(self, auth_token):
324-
"""Verify that an auth token is present."""
325-
if auth_token is None:
328+
def check_auth(self, auth_token, comfy_api_key):
329+
"""Verify that an auth token is present or comfy_api_key is present"""
330+
if auth_token is None and comfy_api_key is None:
326331
raise Exception("Unauthorized: Please login first to use this node.")
327-
return auth_token
332+
return auth_token or comfy_api_key
328333

329334
@staticmethod
330335
def upload_file(
@@ -392,6 +397,8 @@ def __init__(
392397
files: Optional[Dict[str, Any]] = None,
393398
api_base: str | None = None,
394399
auth_token: Optional[str] = None,
400+
comfy_api_key: Optional[str] = None,
401+
auth_kwargs: Optional[Dict[str,str]] = None,
395402
timeout: float = 604800.0,
396403
verify_ssl: bool = True,
397404
content_type: str = "application/json",
@@ -403,6 +410,10 @@ def __init__(
403410
self.error = None
404411
self.api_base: str = api_base or args.comfy_api_base
405412
self.auth_token = auth_token
413+
self.comfy_api_key = comfy_api_key
414+
if auth_kwargs is not None:
415+
self.auth_token = auth_kwargs.get("auth_token", self.auth_token)
416+
self.comfy_api_key = auth_kwargs.get("comfy_api_key", self.comfy_api_key)
406417
self.timeout = timeout
407418
self.verify_ssl = verify_ssl
408419
self.files = files
@@ -415,7 +426,8 @@ def execute(self, client: Optional[ApiClient] = None) -> R:
415426
if client is None:
416427
client = ApiClient(
417428
base_url=self.api_base,
418-
api_key=self.auth_token,
429+
auth_token=self.auth_token,
430+
comfy_api_key=self.comfy_api_key,
419431
timeout=self.timeout,
420432
verify_ssl=self.verify_ssl,
421433
)
@@ -502,12 +514,18 @@ def __init__(
502514
request: Optional[T] = None,
503515
api_base: str | None = None,
504516
auth_token: Optional[str] = None,
517+
comfy_api_key: Optional[str] = None,
518+
auth_kwargs: Optional[Dict[str,str]] = None,
505519
poll_interval: float = 5.0,
506520
):
507521
self.poll_endpoint = poll_endpoint
508522
self.request = request
509523
self.api_base: str = api_base or args.comfy_api_base
510524
self.auth_token = auth_token
525+
self.comfy_api_key = comfy_api_key
526+
if auth_kwargs is not None:
527+
self.auth_token = auth_kwargs.get("auth_token", self.auth_token)
528+
self.comfy_api_key = auth_kwargs.get("comfy_api_key", self.comfy_api_key)
511529
self.poll_interval = poll_interval
512530

513531
# Polling configuration
@@ -528,7 +546,8 @@ def execute(self, client: Optional[ApiClient] = None) -> R:
528546
if client is None:
529547
client = ApiClient(
530548
base_url=self.api_base,
531-
api_key=self.auth_token,
549+
auth_token=self.auth_token,
550+
comfy_api_key=self.comfy_api_key,
532551
)
533552
return self._poll_until_complete(client)
534553
except Exception as e:

comfy_api_nodes/nodes_bfl.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ def INPUT_TYPES(s):
179179
},
180180
"hidden": {
181181
"auth_token": "AUTH_TOKEN_COMFY_ORG",
182+
"comfy_api_key": "API_KEY_COMFY_ORG",
182183
},
183184
}
184185

@@ -211,7 +212,6 @@ def api_call(
211212
seed=0,
212213
image_prompt=None,
213214
image_prompt_strength=0.1,
214-
auth_token=None,
215215
**kwargs,
216216
):
217217
if image_prompt is None:
@@ -244,7 +244,7 @@ def api_call(
244244
None if image_prompt is None else round(image_prompt_strength, 2)
245245
),
246246
),
247-
auth_token=auth_token,
247+
auth_kwargs=kwargs,
248248
)
249249
output_image = handle_bfl_synchronous_operation(operation)
250250
return (output_image,)
@@ -319,6 +319,7 @@ def INPUT_TYPES(s):
319319
},
320320
"hidden": {
321321
"auth_token": "AUTH_TOKEN_COMFY_ORG",
322+
"comfy_api_key": "API_KEY_COMFY_ORG",
322323
},
323324
}
324325

@@ -337,7 +338,6 @@ def api_call(
337338
seed=0,
338339
image_prompt=None,
339340
# image_prompt_strength=0.1,
340-
auth_token=None,
341341
**kwargs,
342342
):
343343
image_prompt = (
@@ -361,7 +361,7 @@ def api_call(
361361
seed=seed,
362362
image_prompt=image_prompt,
363363
),
364-
auth_token=auth_token,
364+
auth_kwargs=kwargs,
365365
)
366366
output_image = handle_bfl_synchronous_operation(operation)
367367
return (output_image,)
@@ -461,6 +461,7 @@ def INPUT_TYPES(s):
461461
},
462462
"hidden": {
463463
"auth_token": "AUTH_TOKEN_COMFY_ORG",
464+
"comfy_api_key": "API_KEY_COMFY_ORG",
464465
},
465466
}
466467

@@ -482,7 +483,6 @@ def api_call(
482483
steps: int,
483484
guidance: float,
484485
seed=0,
485-
auth_token=None,
486486
**kwargs,
487487
):
488488
image = convert_image_to_base64(image)
@@ -506,7 +506,7 @@ def api_call(
506506
seed=seed,
507507
image=image,
508508
),
509-
auth_token=auth_token,
509+
auth_kwargs=kwargs,
510510
)
511511
output_image = handle_bfl_synchronous_operation(operation)
512512
return (output_image,)
@@ -572,6 +572,7 @@ def INPUT_TYPES(s):
572572
},
573573
"hidden": {
574574
"auth_token": "AUTH_TOKEN_COMFY_ORG",
575+
"comfy_api_key": "API_KEY_COMFY_ORG",
575576
},
576577
}
577578

@@ -590,7 +591,6 @@ def api_call(
590591
steps: int,
591592
guidance: float,
592593
seed=0,
593-
auth_token=None,
594594
**kwargs,
595595
):
596596
# prepare mask
@@ -615,7 +615,7 @@ def api_call(
615615
image=image,
616616
mask=mask,
617617
),
618-
auth_token=auth_token,
618+
auth_kwargs=kwargs,
619619
)
620620
output_image = handle_bfl_synchronous_operation(operation)
621621
return (output_image,)
@@ -706,6 +706,7 @@ def INPUT_TYPES(s):
706706
},
707707
"hidden": {
708708
"auth_token": "AUTH_TOKEN_COMFY_ORG",
709+
"comfy_api_key": "API_KEY_COMFY_ORG",
709710
},
710711
}
711712

@@ -726,7 +727,6 @@ def api_call(
726727
steps: int,
727728
guidance: float,
728729
seed=0,
729-
auth_token=None,
730730
**kwargs,
731731
):
732732
control_image = convert_image_to_base64(control_image[:,:,:,:3])
@@ -763,7 +763,7 @@ def scale_value(value: float, min_val=0, max_val=500):
763763
canny_high_threshold=canny_high_threshold,
764764
preprocessed_image=preprocessed_image,
765765
),
766-
auth_token=auth_token,
766+
auth_kwargs=kwargs,
767767
)
768768
output_image = handle_bfl_synchronous_operation(operation)
769769
return (output_image,)
@@ -834,6 +834,7 @@ def INPUT_TYPES(s):
834834
},
835835
"hidden": {
836836
"auth_token": "AUTH_TOKEN_COMFY_ORG",
837+
"comfy_api_key": "API_KEY_COMFY_ORG",
837838
},
838839
}
839840

@@ -852,7 +853,6 @@ def api_call(
852853
steps: int,
853854
guidance: float,
854855
seed=0,
855-
auth_token=None,
856856
**kwargs,
857857
):
858858
control_image = convert_image_to_base64(control_image[:,:,:,:3])
@@ -878,7 +878,7 @@ def api_call(
878878
control_image=control_image,
879879
preprocessed_image=preprocessed_image,
880880
),
881-
auth_token=auth_token,
881+
auth_kwargs=kwargs,
882882
)
883883
output_image = handle_bfl_synchronous_operation(operation)
884884
return (output_image,)

0 commit comments

Comments
 (0)