From 1a57866c47b7e60cfcd5b81e256e550331a2c714 Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Mon, 2 Feb 2026 00:20:30 +0000 Subject: [PATCH 1/2] =?UTF-8?q?##=20Python=20SDK=20Changes:=20*=20`comfy?= =?UTF-8?q?=5Fdeploy.run.get()`:=20=20=20*=20=20`request.queue=5Fposition`?= =?UTF-8?q?=20**Added**=20=20=20*=20=20`response`=20**Changed**=20**Breaki?= =?UTF-8?q?ng**=20=E2=9A=A0=EF=B8=8F=20*=20`comfy=5Fdeploy.run.deployment.?= =?UTF-8?q?queue()`:=20=20`request`=20**Changed**?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .devcontainer/setup.sh | 1 + .gitignore | 5 + .speakeasy/gen.lock | 444 +++++++++++++++--- .speakeasy/gen.yaml | 29 +- .speakeasy/workflow.lock | 14 +- README.md | 102 ++-- RELEASES.md | 12 +- USAGE.md | 7 +- codeSamples.yaml | 32 +- .../models/components/deploymentrunrequest.md | 5 +- docs/models/components/gpu.md | 5 +- docs/models/components/workflowrunmodel.md | 24 +- .../components/workflowrunoutputmodel.md | 1 + docs/models/components/workflowrunstatus.md | 16 - .../components/workflowrunwebhookbody.md | 12 - .../components/workflowrunwebhookresponse.md | 8 - .../operations/getrunrunrunidgetrequest.md | 3 +- ...runupdaterequestbodywebhookpostresponse.md | 9 - docs/sdks/comfydeploy/README.md | 22 - docs/sdks/deployment/README.md | 12 +- docs/sdks/run/README.md | 6 +- poetry.lock | 383 +++++++-------- poetry.toml | 1 + pyproject.toml | 17 +- scripts/prepare_readme.py | 2 + scripts/publish.sh | 1 - src/comfydeploy/_hooks/__init__.py | 1 - src/comfydeploy/_hooks/sdkhooks.py | 2 - src/comfydeploy/_hooks/types.py | 7 + src/comfydeploy/_version.py | 6 +- src/comfydeploy/basesdk.py | 70 ++- src/comfydeploy/deployment.py | 63 +-- src/comfydeploy/httpclient.py | 23 +- src/comfydeploy/models/__init__.py | 1 - src/comfydeploy/models/components/__init__.py | 131 ++++-- .../models/components/deploymentrunrequest.py | 44 +- .../models/components/mediaitem.py | 35 +- .../models/components/workflowrunmodel.py | 142 +++--- .../components/workflowrunoutputmodel.py | 38 +- .../models/components/workflowrunstatus.py | 16 - .../components/workflowrunwebhookbody.py | 62 --- .../components/workflowrunwebhookresponse.py | 13 - src/comfydeploy/models/errors/__init__.py | 67 ++- .../models/errors/comfydeployerror.py | 30 ++ .../models/errors/httpvalidationerror.py | 23 +- .../models/errors/no_response_error.py | 17 + .../models/errors/responsevalidationerror.py | 27 ++ src/comfydeploy/models/errors/sdkerror.py | 46 +- src/comfydeploy/models/operations/__init__.py | 88 +++- .../cancel_run_run_run_id_cancel_post.py | 19 +- .../operations/get_run_run_run_id_get.py | 43 +- ...eployment_run_run_deployment_queue_post.py | 19 +- src/comfydeploy/models/webhooks/__init__.py | 12 - .../run_update_request_body_webhook_post.py | 30 -- src/comfydeploy/run.py | 135 +++--- src/comfydeploy/sdk.py | 63 ++- src/comfydeploy/sdkconfiguration.py | 7 - src/comfydeploy/types/basemodel.py | 44 +- src/comfydeploy/utils/__init__.py | 199 ++++++-- src/comfydeploy/utils/annotations.py | 40 +- src/comfydeploy/utils/datetimes.py | 23 + src/comfydeploy/utils/enums.py | 150 +++++- src/comfydeploy/utils/eventstreaming.py | 10 + src/comfydeploy/utils/forms.py | 92 ++-- src/comfydeploy/utils/queryparams.py | 16 +- src/comfydeploy/utils/requestbodies.py | 6 +- src/comfydeploy/utils/retries.py | 74 ++- src/comfydeploy/utils/serializers.py | 56 ++- .../utils/unmarshal_json_response.py | 38 ++ 69 files changed, 2109 insertions(+), 1092 deletions(-) delete mode 100644 docs/models/components/workflowrunstatus.md delete mode 100644 docs/models/components/workflowrunwebhookbody.md delete mode 100644 docs/models/components/workflowrunwebhookresponse.md delete mode 100644 docs/models/webhooks/runupdaterequestbodywebhookpostresponse.md delete mode 100644 docs/sdks/comfydeploy/README.md delete mode 100644 src/comfydeploy/models/components/workflowrunstatus.py delete mode 100644 src/comfydeploy/models/components/workflowrunwebhookbody.py delete mode 100644 src/comfydeploy/models/components/workflowrunwebhookresponse.py create mode 100644 src/comfydeploy/models/errors/comfydeployerror.py create mode 100644 src/comfydeploy/models/errors/no_response_error.py create mode 100644 src/comfydeploy/models/errors/responsevalidationerror.py delete mode 100644 src/comfydeploy/models/webhooks/__init__.py delete mode 100644 src/comfydeploy/models/webhooks/run_update_request_body_webhook_post.py create mode 100644 src/comfydeploy/utils/datetimes.py create mode 100644 src/comfydeploy/utils/unmarshal_json_response.py diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index d505e47..11bc4ef 100644 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -7,6 +7,7 @@ curl -fsSL https://raw.githubusercontent.com/speakeasy-api/speakeasy/main/instal rmdir samples || true mkdir samples + python -m pip install --upgrade pip pip install -e . diff --git a/.gitignore b/.gitignore index f7aac94..0628f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +**/__pycache__/ +**/.speakeasy/temp/ +**/.speakeasy/logs/ +.env +.env.local .speakeasy/reports README-PYPI.md .venv/ diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 71239f4..c6b80c9 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,37 +1,412 @@ lockVersion: 2.0.0 id: 2c1d440e-25e4-46e2-a435-ebd1f72a7373 management: - docChecksum: a12e94397f17ad377cd82287f65fc0dd + docChecksum: abba088357b88be5b8ae2f2deae39ac4 docVersion: V2 - speakeasyVersion: 1.523.2 - generationVersion: 2.560.1 - releaseVersion: 0.7.0 - configChecksum: efd69310efe6f150238bffd349771dd8 + speakeasyVersion: 1.700.2 + generationVersion: 2.801.2 + releaseVersion: 0.8.0 + configChecksum: 9bf1a91721ade7c9afc48c6ddadf8391 repoURL: https://github.com/comfy-deploy/comfy-deploy-python.git installationURL: https://github.com/comfy-deploy/comfy-deploy-python.git published: true +persistentEdits: + generation_id: 3a2a002d-9750-4c9a-af04-d549fa45cdcf + pristine_commit_hash: cd46115cd893b3665d57d56fb41525093a635e8e + pristine_tree_hash: 4b28370e662051cd20ebd0baae42b3f26c066c91 features: python: additionalDependencies: 1.0.0 constsAndDefaults: 1.0.5 - core: 5.12.4 + core: 5.23.18 defaultEnabledRetries: 0.2.0 devContainers: 3.0.0 enumUnions: 0.1.0 envVarSecurityUsage: 0.3.2 flattening: 3.1.1 - globalSecurity: 3.0.3 + globalSecurity: 3.0.4 globalSecurityCallbacks: 1.0.0 globalSecurityFlattening: 1.0.0 - globalServerURLs: 3.1.0 - groups: 3.0.0 + globalServerURLs: 3.2.0 + groups: 3.0.1 nameOverrides: 3.0.1 - nullables: 1.0.1 + nullables: 1.0.2 responseFormat: 1.0.1 - retries: 3.0.2 - sdkHooks: 1.0.1 - unions: 3.0.4 - webhooks: 2.0.0 + retries: 3.0.3 + sdkHooks: 1.2.1 + unions: 3.1.2 +trackedFiles: + .devcontainer/README.md: + id: b170c0f184ac + last_write_checksum: sha1:13e7232fc4c3162f6c758d5ed5ce1cb424b757df + pristine_git_object: dc080ebd408ffb5e8dacbb55f64f4bcf6bf01f57 + .devcontainer/devcontainer.json: + id: b34062a34eb1 + last_write_checksum: sha1:fbbce5654b8990b172a9ffe841d7635cd51c495b + pristine_git_object: 7e67af9f5b5aeed56bab8dd84f75d3729df3ada1 + .devcontainer/setup.sh: + id: 5f1dfbfeb8eb + last_write_checksum: sha1:7f1b58df073363f88af7088c19956109f244dcfb + pristine_git_object: 11bc4efad90a23e056acb0e4fdd871a865c3c4d6 + .gitattributes: + id: 24139dae6567 + last_write_checksum: sha1:53134de3ada576f37c22276901e1b5b6d85cd2da + pristine_git_object: 4d75d59008e4d8609876d263419a9dc56c8d6f3a + .vscode/settings.json: + id: 89aa447020cd + last_write_checksum: sha1:f84632c81029fcdda8c3b0c768d02b836fc80526 + pristine_git_object: 8d79f0abb72526f1fb34a4c03e5bba612c6ba2ae + USAGE.md: + id: 3aed33ce6e6f + last_write_checksum: sha1:feeeb7d156cb1c940397a4e32846d61364c24402 + pristine_git_object: 6b4891818267e758054da1c559284150c0c17b6c + docs/models/components/createrunresponse.md: + id: 7616117e7b1f + last_write_checksum: sha1:5856ea6a443a67a5ceaf594892a715a04cf54858 + pristine_git_object: 50219001832e86ffe7e28bca624dbd5a7e8581cc + docs/models/components/data.md: + id: 3f89c52c2b17 + last_write_checksum: sha1:225f3c6070467f71c37cc0179f2ccaf639cb11a8 + pristine_git_object: 242b929e2ee1f38828faca5b55a7bfd43e76fc7a + docs/models/components/deploymentrunrequest.md: + id: ab656a0f1241 + last_write_checksum: sha1:679fe8d485a6ba3eb32bac5d97cda3fdde89d95b + pristine_git_object: 8ec6c817a44f605dc5d07b3573efe940787e90fb + docs/models/components/gpu.md: + id: 7da418721011 + last_write_checksum: sha1:711ab01e65516c519e18dd2b6fd6c6801c2225cc + pristine_git_object: 66df8fef745ec48f49a7af7a11aff13744d6d687 + docs/models/components/httpmetadata.md: + id: 728948c70168 + last_write_checksum: sha1:09d9c91cf41e3b9e867647becda4e437f4f3e3c8 + pristine_git_object: 2c187164ad5f4bf5cc49203cbb81b64b240247a6 + docs/models/components/inputs.md: + id: dd91830fb1c4 + last_write_checksum: sha1:8dcf82451bda39a1e833a9c80eff04e0028ceeef + pristine_git_object: 949a69368f3ee584090ba331b204111ec470a113 + docs/models/components/loc.md: + id: 18d5ad9b7905 + last_write_checksum: sha1:09a04749333ab50ae806c3ac6adcaa90d54df0f1 + pristine_git_object: d6094ac2c6e0326c039dad2f6b89158694ef6aa7 + docs/models/components/mediaitem.md: + id: d645b5382e16 + last_write_checksum: sha1:1535c71a59d3a28fbeca6433e84d6dcc883ec3e6 + pristine_git_object: 1b141ff9660f59bdb80c2ec9dc764c36d1aaa6be + docs/models/components/security.md: + id: 54906b49ea28 + last_write_checksum: sha1:bfebf2ebf88a6a1768d99c2705250630e80769de + pristine_git_object: 7cf6e695c302312633cb16875add305e473d5c98 + docs/models/components/validationerror.md: + id: 23910fc567b1 + last_write_checksum: sha1:259c32ece2a67dc59b4cfa58e0020894ecd7afc2 + pristine_git_object: fe535e36357b4f1f67c9a1229114a3fa249556a5 + docs/models/components/workflowrunmodel.md: + id: 88bbf89656d1 + last_write_checksum: sha1:a35ff6a0bdd679c0f7ff5ef6f02c60891ab4d1e2 + pristine_git_object: f52ca641f6a77c6ecee63f1fba9054c738ee28a8 + docs/models/components/workflowrunoutputmodel.md: + id: d0979b0cf456 + last_write_checksum: sha1:95f6930ad088dd4aa2e78a036e11bd5715e0d7d3 + pristine_git_object: 169d51e10015d9b49f6881822cc72a3efd807d51 + docs/models/errors/httpvalidationerror.md: + id: 9365601abbd7 + last_write_checksum: sha1:c569603480752cb4c870e0502971b5dadd839baf + pristine_git_object: 5cf28a6acddb114e74138e7056f719a3a35747a1 + docs/models/operations/cancelrunrunrunidcancelpostrequest.md: + id: 447a96ed716c + last_write_checksum: sha1:bef672711cd7115c1b20cee83eced665ca41915d + pristine_git_object: 289c4a01fcbad5797ad2285d90adc25b59640314 + docs/models/operations/cancelrunrunrunidcancelpostresponse.md: + id: 79891a596482 + last_write_checksum: sha1:6e7d3ae9c1e321c2f8416497fbb2b29bdd457833 + pristine_git_object: afa3e3542687a1c3577e08310ff43c1c8fde8dff + docs/models/operations/getrunrunrunidgetrequest.md: + id: 8e715af51248 + last_write_checksum: sha1:d7c38b1bd259e7ad26a5de4ef8dd23533d78b9c2 + pristine_git_object: 3cee973d2c377b4599bf9fd0403766b04167f396 + docs/models/operations/getrunrunrunidgetresponse.md: + id: 8e7ad6b11417 + last_write_checksum: sha1:29da211c6a98c9ebbc0e7651bb1aa274de8dee55 + pristine_git_object: 5f52900d6b2c86cede9978f17a03c5777ea21cef + docs/models/operations/queuedeploymentrunrundeploymentqueuepostresponse.md: + id: a1f83718f3a9 + last_write_checksum: sha1:e480aa70b64891e7755c00a1c1dd9dba1742f4c3 + pristine_git_object: f3c23da594ecadd75a811cea7e629a12e6088da6 + docs/models/utils/retryconfig.md: + id: 4343ac43161c + last_write_checksum: sha1:562c0f21e308ad10c27f85f75704c15592c6929d + pristine_git_object: 69dd549ec7f5f885101d08dd502e25748183aebf + docs/sdks/deployment/README.md: + id: 5cb544abed28 + last_write_checksum: sha1:a2a175097aeb414576061de7d1e7bbef6de318d6 + pristine_git_object: cd3f902e4c52ff3a94703e6741850f5504f9f477 + docs/sdks/run/README.md: + id: 43088858b794 + last_write_checksum: sha1:efe12fbdaededae3f77390fb285970bb167e7a78 + pristine_git_object: ecb83067a13348e1df747667897eee8801bdcc11 + poetry.toml: + id: a81ade82122a + last_write_checksum: sha1:2242305e29dc6921bdf5b200aea5d4bf67830230 + pristine_git_object: cd3492ac9dc870fdcf23dbd94fd1d40cc753cc8e + py.typed: + id: 258c3ed47ae4 + last_write_checksum: sha1:8efc425ffe830805ffcc0f3055871bdcdc542c60 + pristine_git_object: 3e38f1a929f7d6b1d6de74604aa87e3d8f010544 + pylintrc: + id: 7ce8b9f946e6 + last_write_checksum: sha1:db2aebd83e553dd59d3965e79104a3fb780c403a + pristine_git_object: e8cd3e85682cd3d489cf3c5be75a0563d0c9f4bf + pyproject.toml: + id: 5d07e7d72637 + last_write_checksum: sha1:d602389b9a53250751689cefdf3e5fcbc3a85878 + pristine_git_object: 9c96d54fa9548b3fb991f1e3f8a14a6b765137a5 + scripts/prepare_readme.py: + id: e0c5957a6035 + last_write_checksum: sha1:17e351800fa5736a976dc16bf7a3e0df5ea575bb + pristine_git_object: 3048971b09e8eaff9faaf414a5bf5b1c7a33fc19 + scripts/publish.sh: + id: fe273b08f514 + last_write_checksum: sha1:b31bafc19c15ab5ea925fdf8d5d4adce2b115a63 + pristine_git_object: 2a3ead70ccc6228cbae1c5c8a319b1399f3804ea + src/comfydeploy/__init__.py: + id: 1a545ebe94d7 + last_write_checksum: sha1:da077c0bdfcef64a4a5aea91a17292f72fa2b088 + pristine_git_object: 833c68cd526fe34aab2b7e7c45f974f7f4b9e120 + src/comfydeploy/_hooks/__init__.py: + id: 11f57e4a3a3a + last_write_checksum: sha1:45a380490e968d8d95994dd97554ac60eeb6ae60 + pristine_git_object: e763be4b171bbcf9c16caeaf381f8c92ce8d8e6e + src/comfydeploy/_hooks/sdkhooks.py: + id: 4593eada03c5 + last_write_checksum: sha1:b35348526dd2e95f81aa96948b5c2d1cfa497c04 + pristine_git_object: cb7dc1b650381c88001eb2a0ce17fa2331e419dd + src/comfydeploy/_hooks/types.py: + id: af5e33946cc2 + last_write_checksum: sha1:a00fce28abcea64741f6a28c8b213d0916e6b769 + pristine_git_object: 8311d1438f065727ecbe6bccd8d0b6a56c1d2c1d + src/comfydeploy/_version.py: + id: 67e5b67b4975 + last_write_checksum: sha1:acd0e73e02fb0f1c6c2df7023cf09806b8229cc1 + pristine_git_object: 34247dec54e0600d5c89d2cbb566868c59ff821c + src/comfydeploy/basesdk.py: + id: 719dbcb20392 + last_write_checksum: sha1:ad5d56b76de28edf38aa5578bc0753d4d4f112f3 + pristine_git_object: 3b7ec8ed0514be0e54f865749c339e402e1f4e13 + src/comfydeploy/deployment.py: + id: 5ac7810ce526 + last_write_checksum: sha1:8d7450626d68e35d9eec4ae47ea558c9547d9e20 + pristine_git_object: c2a7d346a65c2f414b83490fb74a61e886179c9d + src/comfydeploy/httpclient.py: + id: ad2135d282d4 + last_write_checksum: sha1:5e55338d6ee9f01ab648cad4380201a8a3da7dd7 + pristine_git_object: 89560b566073785535643e694c112bedbd3db13d + src/comfydeploy/models/__init__.py: + id: 2ac59c91c0a2 + last_write_checksum: sha1:fd931d5c2d58b5f9189cc897e038d6d78e362dab + pristine_git_object: 726fc5eb391035492b4bea383d91cb7e23da04d3 + src/comfydeploy/models/components/__init__.py: + id: 78aaa2aa749d + last_write_checksum: sha1:bee4fa25410bcefccf010ecd9909f5b56a1a1853 + pristine_git_object: b84a87dd2b32fffc85452224bf5263c5c734b69d + src/comfydeploy/models/components/createrunresponse.py: + id: 662ce976eab5 + last_write_checksum: sha1:53c343c04aad542776292c8b3b00a24590a3f799 + pristine_git_object: 9b36f5d2c55ec3c515fc0827e9d90a7e97454a86 + src/comfydeploy/models/components/deploymentrunrequest.py: + id: 306998ef0eb8 + last_write_checksum: sha1:20fd3ef9ac6951717d751ae76afeeac4092027c4 + pristine_git_object: 90a18e6b58f0139da18a11bdb12daf0fdf951c18 + src/comfydeploy/models/components/httpmetadata.py: + id: 9cd1a5f28bd9 + last_write_checksum: sha1:5b240993a5d32d0575416226bb4d5bc675a94595 + pristine_git_object: 00ac55873ef9d5c74ad3446c2b0a7e166b2fa370 + src/comfydeploy/models/components/mediaitem.py: + id: 48364f955210 + last_write_checksum: sha1:b35c6b319a1410ee87bd33aea920d90b10109251 + pristine_git_object: 61b19a6fe1d323629656fa451e4efb43b5d8afe3 + src/comfydeploy/models/components/security.py: + id: b7446ffbe893 + last_write_checksum: sha1:03d542b03e9bb91230dd7ce157aef77f7ae68051 + pristine_git_object: dac95b45ee87237d81203f518a453fbe032b4ad9 + src/comfydeploy/models/components/validationerror.py: + id: 6338d64128fb + last_write_checksum: sha1:896b694ecf231299e7633ae3f444c49320709185 + pristine_git_object: 4d1e9ddddc6330395ad102faa2684f5ceafba965 + src/comfydeploy/models/components/workflowrunmodel.py: + id: 54cd24bb650e + last_write_checksum: sha1:f67f16bf37a6cd3fcd276a2185640a21433dadf2 + pristine_git_object: 676ce0c2d2e3e63fcd2a960ea0b8d93e587d5201 + src/comfydeploy/models/components/workflowrunoutputmodel.py: + id: c1faef4e9e15 + last_write_checksum: sha1:9d023c09925afbdc43f9a450fbb90853959a8987 + pristine_git_object: 71433d8bee8489ec60be70e074e474732ccc6ca4 + src/comfydeploy/models/errors/__init__.py: + id: 382af20dfb0f + last_write_checksum: sha1:106ce6059f045891be9c47200348091f783d9be0 + pristine_git_object: e202785ed3d848336cd25b86efc0fd8b82b96381 + src/comfydeploy/models/errors/comfydeployerror.py: + id: 436ec35f1a47 + last_write_checksum: sha1:717f703a1481df4641f6a4f912ffaaddcbf84282 + pristine_git_object: 86408794622d505648d99aea9c37d8a2da0f46c8 + src/comfydeploy/models/errors/httpvalidationerror.py: + id: 8961298f3828 + last_write_checksum: sha1:2e2cb9e86916ba557762b9dd387efd2b20248452 + pristine_git_object: fd09b4d2a50605f487aafaf3a97e068b5a180ba7 + src/comfydeploy/models/errors/no_response_error.py: + id: fdf38b115c4e + last_write_checksum: sha1:7f326424a7d5ae1bcd5c89a0d6b3dbda9138942f + pristine_git_object: 1deab64bc43e1e65bf3c412d326a4032ce342366 + src/comfydeploy/models/errors/responsevalidationerror.py: + id: b89feac92bd5 + last_write_checksum: sha1:ba6b618dcb3169c1d4027297c988db50578b4005 + pristine_git_object: f6d8edbc53459a78a422f3e4ac6a8786e180109f + src/comfydeploy/models/errors/sdkerror.py: + id: fa6233721c94 + last_write_checksum: sha1:4f33058826f2fd6f08913295e75e5397c40affee + pristine_git_object: 831e0c7433a9876f45bd2caaedc5ef899c6fa0cf + src/comfydeploy/models/operations/__init__.py: + id: 3a2e2a70b25b + last_write_checksum: sha1:d69df7e5fcd0ed617f7ceaa0dac1cdf48a007524 + pristine_git_object: c6d4d77b7a192c7863dcac7a88fa7dfe3c25d2b5 + src/comfydeploy/models/operations/cancel_run_run_run_id_cancel_post.py: + id: 32931ea8b22b + last_write_checksum: sha1:d0afa2d36c0884b7a029766535ba7ba955fff175 + pristine_git_object: df02c72cce26cf1f6f20f42156ee5bffb169c21a + src/comfydeploy/models/operations/get_run_run_run_id_get.py: + id: 80c19ea0638c + last_write_checksum: sha1:e93119ddef5ef5f828ed704e8b4f07578d2a2faf + pristine_git_object: 70fa1c0d521d628e30b690fa9f77de9001b98399 + src/comfydeploy/models/operations/queue_deployment_run_run_deployment_queue_post.py: + id: 52f96a6ea591 + last_write_checksum: sha1:ad4a40cce8a83bd1faece6f1ae1d0eb139f911d0 + pristine_git_object: 943668c146308ee5f662193006fbaf70b69de687 + src/comfydeploy/py.typed: + id: 8b1245764230 + last_write_checksum: sha1:8efc425ffe830805ffcc0f3055871bdcdc542c60 + pristine_git_object: 3e38f1a929f7d6b1d6de74604aa87e3d8f010544 + src/comfydeploy/run.py: + id: 31417a2b86a7 + last_write_checksum: sha1:6d309b6639c3ca66fd5facd2d44e281e6f5e1268 + pristine_git_object: 216378cedf93da55224a975f9dada5cb344d6534 + src/comfydeploy/sdk.py: + id: 88e6efb43aec + last_write_checksum: sha1:fced7ec63b55dfd9c79389d821db17cdd4d2f196 + pristine_git_object: 8dc5c3a91deb1802db71c2d3f295e034f771887a + src/comfydeploy/sdkconfiguration.py: + id: a853e3fc5813 + last_write_checksum: sha1:d48da6f5003f7d6cf540952bc501c2a5c4709831 + pristine_git_object: 6f28b788ee9ee504a773b4c0610830eaebbb5a20 + src/comfydeploy/types/__init__.py: + id: 7a179196be4d + last_write_checksum: sha1:140ebdd01a46f92ffc710c52c958c4eba3cf68ed + pristine_git_object: fc76fe0c5505e29859b5d2bb707d48fd27661b8c + src/comfydeploy/types/basemodel.py: + id: 0ecbbb05ee55 + last_write_checksum: sha1:10d84aedeb9d35edfdadf2c3020caa1d24d8b584 + pristine_git_object: a9a640a1a7048736383f96c67c6290c86bf536ee + src/comfydeploy/utils/__init__.py: + id: 643b9259783a + last_write_checksum: sha1:398211d49b762a067ec6d10197a4b11dfd258ff5 + pristine_git_object: c906e1e0192e9017fc13851eb23fc0c2753429c8 + src/comfydeploy/utils/annotations.py: + id: bddf86890ce0 + last_write_checksum: sha1:a4824ad65f730303e4e1e3ec1febf87b4eb46dbc + pristine_git_object: 12e0aa4f1151bb52474cc02e88397329b90703f6 + src/comfydeploy/utils/datetimes.py: + id: 4168ab191bf2 + last_write_checksum: sha1:c721e4123000e7dc61ec52b28a739439d9e17341 + pristine_git_object: a6c52cd61bbe2d459046c940ce5e8c469f2f0664 + src/comfydeploy/utils/enums.py: + id: 03bbc4ebb085 + last_write_checksum: sha1:bc8c3c1285ae09ba8a094ee5c3d9c7f41fa1284d + pristine_git_object: 3324e1bc2668c54c4d5f5a1a845675319757a828 + src/comfydeploy/utils/eventstreaming.py: + id: cc7989ec95f7 + last_write_checksum: sha1:bababae5d54b7efc360db701daa49e18a92c2f3b + pristine_git_object: 0969899bfc491e5e408d05643525f347ea95e4fc + src/comfydeploy/utils/forms.py: + id: df6743c41f17 + last_write_checksum: sha1:15fa7e9ab1611e062a9984cf06cb20969713d295 + pristine_git_object: f961e76beaf0a8b1fe0dda44754a74eebd3608e7 + src/comfydeploy/utils/headers.py: + id: f500acbd10b2 + last_write_checksum: sha1:7c6df233ee006332b566a8afa9ce9a245941d935 + pristine_git_object: 37864cbbbc40d1a47112bbfdd3ba79568fc8818a + src/comfydeploy/utils/logger.py: + id: 6cd4e9e87a19 + last_write_checksum: sha1:f3fdb154a3f09b8cc43d74c7e9c02f899f8086e4 + pristine_git_object: b661aff65d38b77d035149699aea09b2785d2fc6 + src/comfydeploy/utils/metadata.py: + id: 92c53d72ecce + last_write_checksum: sha1:c6a560bd0c63ab158582f34dadb69433ea73b3d4 + pristine_git_object: 173b3e5ce658675c2f504222a56b3daaaa68107d + src/comfydeploy/utils/queryparams.py: + id: d6bf20aad3fb + last_write_checksum: sha1:b94c3f314fd3da0d1d215afc2731f48748e2aa59 + pristine_git_object: c04e0db82b68eca041f2cb2614d748fbac80fd41 + src/comfydeploy/utils/requestbodies.py: + id: 7902232a8c92 + last_write_checksum: sha1:41e2d2d2d3ecc394c8122ca4d4b85e1c3e03f054 + pristine_git_object: 1de32b6d26f46590232f398fdba6ce0072f1659c + src/comfydeploy/utils/retries.py: + id: c75d8ef6dc86 + last_write_checksum: sha1:5b97ac4f59357d70c2529975d50364c88bcad607 + pristine_git_object: 88a91b10cd2076b4a2c6cff2ac6bfaa5e3c5ad13 + src/comfydeploy/utils/security.py: + id: a65efdd0a3c1 + last_write_checksum: sha1:a17130ace2c0db6394f38dd941ad2b700cc755c8 + pristine_git_object: 295a3f40031dbb40073ad227fd4a355660f97ab2 + src/comfydeploy/utils/serializers.py: + id: 8e039c5d7ada + last_write_checksum: sha1:ce1d8d7f500a9ccba0aeca5057cee9c271f4dfd7 + pristine_git_object: 14321eb479de81d0d9580ec8291e0ff91bf29e57 + src/comfydeploy/utils/unmarshal_json_response.py: + id: 83ba4c3727c0 + last_write_checksum: sha1:f928ad99d34b9bbe6d20c16f6b4d58bd441e36ae + pristine_git_object: 6c39969dc4f77cacde3c2c91163eb44999173bea + src/comfydeploy/utils/url.py: + id: 6b6ebd0b30b5 + last_write_checksum: sha1:6479961baa90432ca25626f8e40a7bbc32e73b41 + pristine_git_object: c78ccbae426ce6d385709d97ce0b1c2813ea2418 + src/comfydeploy/utils/values.py: + id: 9185aaf9b78b + last_write_checksum: sha1:acaa178a7c41ddd000f58cc691e4632d925b2553 + pristine_git_object: dae01a44384ac3bc13ae07453a053bf6c898ebe3 +examples: + get_run_run__run_id__get: + speakeasy-default-get-run-run-run-id-get: + parameters: + path: + run_id: "faf49b3a-7b64-4687-95c8-58ca8a41dd73" + query: + queue_position: false + responses: + "200": + application/json: {"id": "46a99099-5b7a-4e10-8e06-6dacc98118bf", "workflow_version_id": "f69dd44f-f54c-41db-91a8-2526823f19f0", "workflow_inputs": "", "workflow_id": "39f9f100-52c4-4837-9d06-0278a672e1ec", "machine_id": "62fc8c79-bbed-4389-9157-5269d7cdf77e", "origin": "", "status": "", "created_at": "2026-04-25T23:18:08.058Z", "updated_at": "2026-04-08T06:46:17.964Z", "gpu_event_id": "", "gpu": "", "machine_version": "", "machine_type": "", "modal_function_call_id": "", "user_id": "", "org_id": "", "live_status": "", "progress": 0, "is_realtime": false, "webhook": "", "webhook_status": "", "webhook_intermediate_status": false} + "422": + application/json: {} + cancel_run_run__run_id__cancel_post: + speakeasy-default-cancel-run-run-run-id-cancel-post: + parameters: + path: + run_id: "" + responses: + "200": + application/json: "" + "422": + application/json: {} + queue_deployment_run_run_deployment_queue_post: + speakeasy-default-queue-deployment-run-run-deployment-queue-post: + requestBody: + application/json: {"inputs": {"num_inference_steps": 30, "prompt": "A futuristic cityscape", "seed": 123456}, "webhook": "https://myapp.com/webhook", "webhook_intermediate_status": true, "deployment_id": "12345678-1234-5678-1234-567812345678"} + responses: + "200": + application/json: {"run_id": "17cad37f-15e1-48dc-8b81-b4a67ba778ba"} + "422": + application/json: {} +examplesVersion: 1.0.2 +generatedTests: {} +releaseNotes: "## Python SDK Changes:\n* `comfy_deploy.run.get()`: \n * `request.queue_position` **Added**\n * `response` **Changed** **Breaking** ⚠️\n* `comfy_deploy.run.deployment.queue()`: `request` **Changed**\n" generatedFiles: - .devcontainer/README.md - .devcontainer/devcontainer.json @@ -123,44 +498,3 @@ generatedFiles: - src/comfydeploy/utils/serializers.py - src/comfydeploy/utils/url.py - src/comfydeploy/utils/values.py -examples: - get_run_run__run_id__get: - speakeasy-default-get-run-run-run-id-get: - parameters: - path: - run_id: "b888f774-3e7c-4135-a18c-6b985523c4bc" - responses: - "200": - application/json: {"id": "e50f7622-81da-484b-9c66-1c8a99c6b71b", "workflow_version_id": "ecd62b8f-7112-4aaf-90ab-4e43b4cca371", "workflow_inputs": "", "workflow_id": "f620807a-a1f8-4cf0-95d8-9d58ee381f6b", "workflow_api": "", "machine_id": "b885c09d-541c-4293-b683-64a15f8d7e75", "origin": "", "status": "", "created_at": "2024-02-02T12:01:38.576Z", "updated_at": "2023-04-16T15:25:27.950Z", "gpu_event_id": "", "gpu": "", "machine_version": "", "machine_type": "", "modal_function_call_id": "", "user_id": "", "org_id": "", "live_status": "", "progress": 0, "is_realtime": false, "webhook": "", "webhook_status": "", "webhook_intermediate_status": false, "number": 494415, "duration": 6188.69, "cold_start_duration": 9131.15, "cold_start_duration_total": 1865.92, "run_duration": 3787.04} - "422": - application/json: {} - cancel_run_run__run_id__cancel_post: - speakeasy-default-cancel-run-run-run-id-cancel-post: - parameters: - path: - run_id: "" - responses: - "200": - application/json: "" - "422": - application/json: {} - queue_deployment_run_run_deployment_queue_post: - speakeasy-default-queue-deployment-run-run-deployment-queue-post: - requestBody: - application/json: {"inputs": {"prompt": "A beautiful landscape", "seed": 42}, "webhook": "https://myapp.com/webhook", "webhook_intermediate_status": true, "deployment_id": "15e79589-12c9-453c-a41a-348fdd7de957"} - responses: - "200": - application/json: {"run_id": "03ec31b2-4d0d-4329-88ff-ac74003017b7"} - "422": - application/json: {} - run_update__request_body__webhook__post: - speakeasy-default-run-update-request-body-webhook-post: - requestBody: - application/json: {"run_id": "", "status": "success", "live_status": "", "progress": 0} - responses: - "200": - application/json: {"status": ""} - "422": - application/json: {} -examplesVersion: 1.0.0 -generatedTests: {} diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index f74460c..6d4dea9 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -7,6 +7,7 @@ generation: maintainOpenAPIOrder: true usageSnippets: optionalPropertyRendering: withExample + sdkInitStyle: constructor useClassNamesForArrayFields: true fixes: nameResolutionDec2023: true @@ -14,18 +15,35 @@ generation: parameterOrderingFeb2024: true requestResponseComponentNamesFeb2024: true securityFeb2025: false + sharedErrorComponentsApr2025: false + sharedNestedComponentsJan2026: false auth: oAuth2ClientCredentialsEnabled: false oAuth2PasswordEnabled: false + hoistGlobalSecurity: true + schemas: + allOfMergeStrategy: shallowMerge + requestBodyFieldName: "" + persistentEdits: {} + tests: + generateTests: true + generateNewTests: false + skipResponseBodyAssertions: false python: - version: 0.7.0 + version: 0.8.0 additionalDependencies: dev: {} main: {} + allowedRedefinedBuiltins: + - id + - object + asyncMode: both author: Speakeasy authors: - Speakeasy + baseErrorName: ComfyDeployError clientServerStatusCodesAsErrors: true + constFieldCasing: upper defaultErrorName: SDKError description: Python Client SDK Generated by Speakeasy enableCustomCodeRegions: false @@ -43,12 +61,21 @@ python: operations: models/operations shared: models/components webhooks: models/webhooks + inferUnionDiscriminators: true inputModelSuffix: input + license: "" maxMethodParams: 4 methodArguments: require-security-and-request + moduleName: "" + multipartArrayFormat: legacy outputModelSuffix: output + packageManager: poetry packageName: comfydeploy + preApplyUnionDiscriminators: false projectUrls: {} + pytestFilterWarnings: [] pytestTimeout: 0 responseFormat: envelope-http + sseFlatResponse: false templateVersion: v2 + useAsyncHooks: false diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index f70a5a8..f63dbea 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,21 +1,21 @@ -speakeasyVersion: 1.523.2 +speakeasyVersion: 1.700.2 sources: comfydeploy-api: sourceNamespace: comfydeploy-api - sourceRevisionDigest: sha256:88643a4de11cc9a90c5da0de3ecf6526f61237a30189707b7a6abf6192ad4cb2 - sourceBlobDigest: sha256:df07cbe29241948042d8f8f0dc662107805290842e5a6ed7623ccf270744fba1 + sourceRevisionDigest: sha256:2aaedaaafa9b1d33e83ac5670f3aced8eedb36817d7faa0ded9d685a7cc4cb10 + sourceBlobDigest: sha256:04e6ce3f75140ce358238261d8803b6dff6e955380b3e353eca99c7798681767 tags: - latest - - speakeasy-sdk-regen-1743002572 + - speakeasy-sdk-regen-1758759186 - V2 targets: comfy-deploy: source: comfydeploy-api sourceNamespace: comfydeploy-api - sourceRevisionDigest: sha256:88643a4de11cc9a90c5da0de3ecf6526f61237a30189707b7a6abf6192ad4cb2 - sourceBlobDigest: sha256:df07cbe29241948042d8f8f0dc662107805290842e5a6ed7623ccf270744fba1 + sourceRevisionDigest: sha256:2aaedaaafa9b1d33e83ac5670f3aced8eedb36817d7faa0ded9d685a7cc4cb10 + sourceBlobDigest: sha256:04e6ce3f75140ce358238261d8803b6dff6e955380b3e353eca99c7798681767 codeSamplesNamespace: code-samples-python-comfy-deploy - codeSamplesRevisionDigest: sha256:adf2885110445ea05f59d31a8a76e34c61bc6c030a258fc1ef5ae0c8836be768 + codeSamplesRevisionDigest: sha256:9d00406804ff49ae2c10709badad219304c49b09c44cdfe92c2f1946cd499c07 workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/README.md b/README.md index 3c8665b..d80fcae 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,15 @@ To authenticate your requests, include your API key in the `Authorization` heade > > Once a Python version reaches its [official end of life date](https://devguide.python.org/versions/), a 3-month grace period is provided for users to upgrade. Following this grace period, the minimum python version supported in the SDK will be updated. -The SDK can be installed with either *pip* or *poetry* package managers. +The SDK can be installed with *uv*, *pip*, or *poetry* package managers. + +### uv + +*uv* is a fast Python package installer and resolver, designed as a drop-in replacement for pip and pip-tools. It's recommended for its speed and modern Python tooling capabilities. + +```bash +uv add comfydeploy +``` ### PIP @@ -136,7 +144,7 @@ with ComfyDeploy( bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -146,7 +154,8 @@ with ComfyDeploy(
-The same SDK client can also be used to make asychronous requests by importing asyncio. +The same SDK client can also be used to make asynchronous requests by importing asyncio. + ```python # Asynchronous Example import asyncio @@ -158,7 +167,7 @@ async def main(): bearer="", ) as comfy_deploy: - res = await comfy_deploy.run.get_async(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = await comfy_deploy.run.get_async(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -189,7 +198,7 @@ with ComfyDeploy( bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -205,13 +214,12 @@ with ComfyDeploy(
Available methods - -### [run](docs/sdks/run/README.md) +### [Run](docs/sdks/run/README.md) * [get](docs/sdks/run/README.md#get) - Get Run * [cancel](docs/sdks/run/README.md#cancel) - Cancel Run -#### [run.deployment](docs/sdks/deployment/README.md) +### [Run.Deployment](docs/sdks/deployment/README.md) * [queue](docs/sdks/deployment/README.md#queue) - Queue Run @@ -233,7 +241,7 @@ with ComfyDeploy( bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc", + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False, RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False)) assert res.workflow_run_model is not None @@ -254,7 +262,7 @@ with ComfyDeploy( bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -267,26 +275,18 @@ with ComfyDeploy( ## Error Handling -Handling errors in this SDK should largely match your expectations. All operations return a response object or raise an exception. - -By default, an API error will raise a errors.SDKError exception, which has the following properties: - -| Property | Type | Description | -|-----------------|------------------|-----------------------| -| `.status_code` | *int* | The HTTP status code | -| `.message` | *str* | The error message | -| `.raw_response` | *httpx.Response* | The raw HTTP response | -| `.body` | *str* | The response content | +[`ComfyDeployError`](./src/comfydeploy/models/errors/comfydeployerror.py) is the base class for all HTTP error responses. It has the following properties: -When custom error responses are specified for an operation, the SDK may also raise their associated exceptions. You can refer to respective *Errors* tables in SDK docs for more details on possible exception types for each operation. For example, the `get_async` method may raise the following exceptions: - -| Error Type | Status Code | Content Type | -| -------------------------- | ----------- | ---------------- | -| errors.HTTPValidationError | 422 | application/json | -| errors.SDKError | 4XX, 5XX | \*/\* | +| Property | Type | Description | +| ------------------ | ---------------- | --------------------------------------------------------------------------------------- | +| `err.message` | `str` | Error message | +| `err.status_code` | `int` | HTTP response status code eg `404` | +| `err.headers` | `httpx.Headers` | HTTP response headers | +| `err.body` | `str` | HTTP body. Can be empty string if no body is returned. | +| `err.raw_response` | `httpx.Response` | Raw HTTP response | +| `err.data` | | Optional. Some errors may contain structured data. [See Error Classes](#error-classes). | ### Example - ```python from comfydeploy import ComfyDeploy from comfydeploy.models import errors @@ -298,20 +298,46 @@ with ComfyDeploy( res = None try: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None # Handle response print(res.workflow_run_model) - except errors.HTTPValidationError as e: - # handle e.data: errors.HTTPValidationErrorData - raise(e) - except errors.SDKError as e: - # handle exception - raise(e) + + except errors.ComfyDeployError as e: + # The base class for HTTP error responses + print(e.message) + print(e.status_code) + print(e.body) + print(e.headers) + print(e.raw_response) + + # Depending on the method different errors may be thrown + if isinstance(e, errors.HTTPValidationError): + print(e.data.detail) # Optional[List[components.ValidationError]] ``` + +### Error Classes +**Primary errors:** +* [`ComfyDeployError`](./src/comfydeploy/models/errors/comfydeployerror.py): The base class for HTTP error responses. + * [`HTTPValidationError`](./src/comfydeploy/models/errors/httpvalidationerror.py): Validation Error. Status code `422`. + +
Less common errors (5) + +
+ +**Network errors:** +* [`httpx.RequestError`](https://www.python-httpx.org/exceptions/#httpx.RequestError): Base class for request errors. + * [`httpx.ConnectError`](https://www.python-httpx.org/exceptions/#httpx.ConnectError): HTTP client was unable to make a request to a server. + * [`httpx.TimeoutException`](https://www.python-httpx.org/exceptions/#httpx.TimeoutException): HTTP request timed out. + + +**Inherit from [`ComfyDeployError`](./src/comfydeploy/models/errors/comfydeployerror.py)**: +* [`ResponseValidationError`](./src/comfydeploy/models/errors/responsevalidationerror.py): Type mismatch between the response data and the expected Pydantic model. Provides access to the Pydantic validation error via the `cause` attribute. + +
@@ -334,11 +360,11 @@ from comfydeploy import ComfyDeploy with ComfyDeploy( - server_idx=2, + server_idx=0, bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -355,11 +381,11 @@ from comfydeploy import ComfyDeploy with ComfyDeploy( - server_url="https://api.comfydeploy.com/api", + server_url="http://localhost:3011/api", bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None diff --git a/RELEASES.md b/RELEASES.md index 64dec55..2005a25 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -188,4 +188,14 @@ Based on: ### Generated - [python v0.7.0] . ### Releases -- [PyPI v0.7.0] https://pypi.org/project/comfydeploy/0.7.0 - . \ No newline at end of file +- [PyPI v0.7.0] https://pypi.org/project/comfydeploy/0.7.0 - . + +## 2026-02-02 00:19:19 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.700.2 (2.801.2) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v0.8.0] . +### Releases +- [PyPI v0.8.0] https://pypi.org/project/comfydeploy/0.8.0 - . \ No newline at end of file diff --git a/USAGE.md b/USAGE.md index 5b90bb2..6b48918 100644 --- a/USAGE.md +++ b/USAGE.md @@ -8,7 +8,7 @@ with ComfyDeploy( bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -18,7 +18,8 @@ with ComfyDeploy(
-The same SDK client can also be used to make asychronous requests by importing asyncio. +The same SDK client can also be used to make asynchronous requests by importing asyncio. + ```python # Asynchronous Example import asyncio @@ -30,7 +31,7 @@ async def main(): bearer="", ) as comfy_deploy: - res = await comfy_deploy.run.get_async(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = await comfy_deploy.run.get_async(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None diff --git a/codeSamples.yaml b/codeSamples.yaml index 4932e27..c0e9545 100644 --- a/codeSamples.yaml +++ b/codeSamples.yaml @@ -18,11 +18,13 @@ actions: res = comfy_deploy.run.deployment.queue(request={ "inputs": { - "prompt": "A beautiful landscape", - "seed": 42, + "num_inference_steps": 30, + "prompt": "A futuristic cityscape", + "seed": 123456, }, "webhook": "https://myapp.com/webhook", - "deployment_id": "15e79589-12c9-453c-a41a-348fdd7de957", + "webhook_intermediate_status": True, + "deployment_id": "12345678-1234-5678-1234-567812345678", }) assert res.create_run_response is not None @@ -42,7 +44,7 @@ actions: bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -67,25 +69,3 @@ actions: # Handle response print(res.any) - - target: $["paths"]["{$request.body#/webhook}"]["post"] - update: - x-codeSamples: - - lang: python - label: SDK (Python) - source: |- - from comfydeploy import ComfyDeploy - from comfydeploy.models import components - - - with ComfyDeploy() as comfy_deploy: - - res = comfy_deploy.callbacks.run_update_request_body_webhook_post(request={ - "run_id": "", - "status": components.WorkflowRunStatus.SUCCESS, - "live_status": "", - }) - - assert res.workflow_run_webhook_response is not None - - # Handle response - print(res.workflow_run_webhook_response) diff --git a/docs/models/components/deploymentrunrequest.md b/docs/models/components/deploymentrunrequest.md index 39bd2db..8ec6c81 100644 --- a/docs/models/components/deploymentrunrequest.md +++ b/docs/models/components/deploymentrunrequest.md @@ -5,8 +5,9 @@ | Field | Type | Required | Description | Example | | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | -| `deployment_id` | *str* | :heavy_check_mark: | N/A | 15e79589-12c9-453c-a41a-348fdd7de957 | | `inputs` | Dict[str, [components.Inputs](../../models/components/inputs.md)] | :heavy_minus_sign: | The inputs to the workflow | {
"prompt": "A beautiful landscape",
"seed": 42
} | | `webhook` | *Optional[str]* | :heavy_minus_sign: | N/A | | | `webhook_intermediate_status` | *Optional[bool]* | :heavy_minus_sign: | N/A | true | -| `gpu` | [Optional[components.Gpu]](../../models/components/gpu.md) | :heavy_minus_sign: | The GPU to override the machine's default GPU | | \ No newline at end of file +| `gpu` | [Optional[components.Gpu]](../../models/components/gpu.md) | :heavy_minus_sign: | The GPU to override the machine's default GPU | | +| `flags` | List[*str*] | :heavy_minus_sign: | Array of flag strings | [
"runpod_v2"
] | +| `deployment_id` | *str* | :heavy_check_mark: | N/A | 15e79589-12c9-453c-a41a-348fdd7de957 | \ No newline at end of file diff --git a/docs/models/components/gpu.md b/docs/models/components/gpu.md index 0bd9f60..66df8fe 100644 --- a/docs/models/components/gpu.md +++ b/docs/models/components/gpu.md @@ -7,10 +7,13 @@ The GPU to override the machine's default GPU | Name | Value | | ------------ | ------------ | +| `CPU` | CPU | | `T4` | T4 | | `L4` | L4 | | `A10_G` | A10G | | `L40_S` | L40S | | `A100` | A100 | | `A100_80_GB` | A100-80GB | -| `H100` | H100 | \ No newline at end of file +| `H100` | H100 | +| `H200` | H200 | +| `B200` | B200 | \ No newline at end of file diff --git a/docs/models/components/workflowrunmodel.md b/docs/models/components/workflowrunmodel.md index 9ab2095..f52ca64 100644 --- a/docs/models/components/workflowrunmodel.md +++ b/docs/models/components/workflowrunmodel.md @@ -9,12 +9,14 @@ | `workflow_version_id` | *Nullable[str]* | :heavy_check_mark: | N/A | | `workflow_inputs` | *Nullable[Any]* | :heavy_check_mark: | N/A | | `workflow_id` | *str* | :heavy_check_mark: | N/A | -| `workflow_api` | *Nullable[Any]* | :heavy_check_mark: | N/A | | `machine_id` | *Nullable[str]* | :heavy_check_mark: | N/A | | `origin` | *str* | :heavy_check_mark: | N/A | | `status` | *str* | :heavy_check_mark: | N/A | +| `ended_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_minus_sign: | N/A | | `created_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_check_mark: | N/A | | `updated_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_check_mark: | N/A | +| `queued_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_minus_sign: | N/A | +| `started_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_minus_sign: | N/A | | `gpu_event_id` | *Nullable[str]* | :heavy_check_mark: | N/A | | `gpu` | *Nullable[str]* | :heavy_check_mark: | N/A | | `machine_version` | *Nullable[str]* | :heavy_check_mark: | N/A | @@ -23,17 +25,15 @@ | `user_id` | *Nullable[str]* | :heavy_check_mark: | N/A | | `org_id` | *Nullable[str]* | :heavy_check_mark: | N/A | | `live_status` | *Nullable[str]* | :heavy_check_mark: | N/A | -| `webhook` | *Nullable[str]* | :heavy_check_mark: | N/A | -| `webhook_status` | *Nullable[str]* | :heavy_check_mark: | N/A | -| `number` | *int* | :heavy_check_mark: | N/A | -| `duration` | *Nullable[float]* | :heavy_check_mark: | N/A | -| `cold_start_duration` | *Nullable[float]* | :heavy_check_mark: | N/A | -| `cold_start_duration_total` | *Nullable[float]* | :heavy_check_mark: | N/A | -| `run_duration` | *Nullable[float]* | :heavy_check_mark: | N/A | -| `ended_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_minus_sign: | N/A | -| `queued_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_minus_sign: | N/A | -| `started_at` | [date](https://docs.python.org/3/library/datetime.html#date-objects) | :heavy_minus_sign: | N/A | | `progress` | *Optional[float]* | :heavy_minus_sign: | N/A | | `is_realtime` | *Optional[bool]* | :heavy_minus_sign: | N/A | +| `webhook` | *Nullable[str]* | :heavy_check_mark: | N/A | +| `webhook_status` | *Nullable[str]* | :heavy_check_mark: | N/A | | `webhook_intermediate_status` | *Optional[bool]* | :heavy_minus_sign: | N/A | -| `outputs` | List[[components.WorkflowRunOutputModel](../../models/components/workflowrunoutputmodel.md)] | :heavy_minus_sign: | N/A | \ No newline at end of file +| `outputs` | List[[components.WorkflowRunOutputModel](../../models/components/workflowrunoutputmodel.md)] | :heavy_minus_sign: | N/A | +| `number` | *OptionalNullable[int]* | :heavy_minus_sign: | N/A | +| `duration` | *OptionalNullable[float]* | :heavy_minus_sign: | N/A | +| `cold_start_duration` | *OptionalNullable[float]* | :heavy_minus_sign: | N/A | +| `cold_start_duration_total` | *OptionalNullable[float]* | :heavy_minus_sign: | N/A | +| `run_duration` | *OptionalNullable[float]* | :heavy_minus_sign: | N/A | +| `queue_position` | *OptionalNullable[int]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/models/components/workflowrunoutputmodel.md b/docs/models/components/workflowrunoutputmodel.md index 05ffade..169d51e 100644 --- a/docs/models/components/workflowrunoutputmodel.md +++ b/docs/models/components/workflowrunoutputmodel.md @@ -6,6 +6,7 @@ | Field | Type | Required | Description | | -------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | | `id` | *str* | :heavy_check_mark: | N/A | +| `output_id` | *OptionalNullable[str]* | :heavy_minus_sign: | N/A | | `run_id` | *str* | :heavy_check_mark: | N/A | | `data` | Dict[str, List[[components.Data](../../models/components/data.md)]] | :heavy_check_mark: | N/A | | `node_meta` | *Nullable[Any]* | :heavy_check_mark: | N/A | diff --git a/docs/models/components/workflowrunstatus.md b/docs/models/components/workflowrunstatus.md deleted file mode 100644 index 10b05ec..0000000 --- a/docs/models/components/workflowrunstatus.md +++ /dev/null @@ -1,16 +0,0 @@ -# WorkflowRunStatus - - -## Values - -| Name | Value | -| ------------- | ------------- | -| `NOT_STARTED` | not-started | -| `RUNNING` | running | -| `UPLOADING` | uploading | -| `SUCCESS` | success | -| `FAILED` | failed | -| `STARTED` | started | -| `QUEUED` | queued | -| `TIMEOUT` | timeout | -| `CANCELLED` | cancelled | \ No newline at end of file diff --git a/docs/models/components/workflowrunwebhookbody.md b/docs/models/components/workflowrunwebhookbody.md deleted file mode 100644 index 0a09bb9..0000000 --- a/docs/models/components/workflowrunwebhookbody.md +++ /dev/null @@ -1,12 +0,0 @@ -# WorkflowRunWebhookBody - - -## Fields - -| Field | Type | Required | Description | -| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| `run_id` | *str* | :heavy_check_mark: | N/A | -| `status` | [components.WorkflowRunStatus](../../models/components/workflowrunstatus.md) | :heavy_check_mark: | N/A | -| `live_status` | *Nullable[str]* | :heavy_check_mark: | N/A | -| `progress` | *Optional[float]* | :heavy_minus_sign: | N/A | -| `outputs` | List[[components.WorkflowRunOutputModel](../../models/components/workflowrunoutputmodel.md)] | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/models/components/workflowrunwebhookresponse.md b/docs/models/components/workflowrunwebhookresponse.md deleted file mode 100644 index 1a8cbeb..0000000 --- a/docs/models/components/workflowrunwebhookresponse.md +++ /dev/null @@ -1,8 +0,0 @@ -# WorkflowRunWebhookResponse - - -## Fields - -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `status` | *str* | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/docs/models/operations/getrunrunrunidgetrequest.md b/docs/models/operations/getrunrunrunidgetrequest.md index c9858a8..3cee973 100644 --- a/docs/models/operations/getrunrunrunidgetrequest.md +++ b/docs/models/operations/getrunrunrunidgetrequest.md @@ -5,4 +5,5 @@ | Field | Type | Required | Description | | ------------------ | ------------------ | ------------------ | ------------------ | -| `run_id` | *str* | :heavy_check_mark: | N/A | \ No newline at end of file +| `run_id` | *str* | :heavy_check_mark: | N/A | +| `queue_position` | *Optional[bool]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/models/webhooks/runupdaterequestbodywebhookpostresponse.md b/docs/models/webhooks/runupdaterequestbodywebhookpostresponse.md deleted file mode 100644 index 47c30e3..0000000 --- a/docs/models/webhooks/runupdaterequestbodywebhookpostresponse.md +++ /dev/null @@ -1,9 +0,0 @@ -# RunUpdateRequestBodyWebhookPostResponse - - -## Fields - -| Field | Type | Required | Description | -| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| `http_meta` | [components.HTTPMetadata](../../models/components/httpmetadata.md) | :heavy_check_mark: | N/A | -| `workflow_run_webhook_response` | [Optional[components.WorkflowRunWebhookResponse]](../../models/components/workflowrunwebhookresponse.md) | :heavy_minus_sign: | Successful Response | \ No newline at end of file diff --git a/docs/sdks/comfydeploy/README.md b/docs/sdks/comfydeploy/README.md deleted file mode 100644 index f8de388..0000000 --- a/docs/sdks/comfydeploy/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# ComfyDeploy SDK - -## Overview - -ComfyDeploy API: -### Overview - -Welcome to the ComfyDeploy API! - -To create a run thru the API, use the [queue run endpoint](#tag/run/POST/run/deployment/queue). - -Check out the [get run endpoint](#tag/run/GET/run/{run_id}), for getting the status and output of a run. - -### Authentication - -To authenticate your requests, include your API key in the `Authorization` header as a bearer token. Make sure to generate an API key in the [API Keys section of your ComfyDeploy account](https://www.comfydeploy.com/api-keys). - -### - - - -### Available Operations diff --git a/docs/sdks/deployment/README.md b/docs/sdks/deployment/README.md index 79f982a..cd3f902 100644 --- a/docs/sdks/deployment/README.md +++ b/docs/sdks/deployment/README.md @@ -1,5 +1,4 @@ -# Deployment -(*run.deployment*) +# Run.Deployment ## Overview @@ -13,6 +12,7 @@ Create a new deployment run with the given parameters. ### Example Usage + ```python from comfydeploy import ComfyDeploy @@ -22,12 +22,14 @@ with ComfyDeploy( ) as comfy_deploy: res = comfy_deploy.run.deployment.queue(request={ - "deployment_id": "15e79589-12c9-453c-a41a-348fdd7de957", "inputs": { - "prompt": "A beautiful landscape", - "seed": 42, + "num_inference_steps": 30, + "prompt": "A futuristic cityscape", + "seed": 123456, }, "webhook": "https://myapp.com/webhook", + "webhook_intermediate_status": True, + "deployment_id": "12345678-1234-5678-1234-567812345678", }) assert res.create_run_response is not None diff --git a/docs/sdks/run/README.md b/docs/sdks/run/README.md index cf0d79e..ecb8306 100644 --- a/docs/sdks/run/README.md +++ b/docs/sdks/run/README.md @@ -1,5 +1,4 @@ # Run -(*run*) ## Overview @@ -14,6 +13,7 @@ Get Run ### Example Usage + ```python from comfydeploy import ComfyDeploy @@ -22,7 +22,7 @@ with ComfyDeploy( bearer="", ) as comfy_deploy: - res = comfy_deploy.run.get(run_id="b888f774-3e7c-4135-a18c-6b985523c4bc") + res = comfy_deploy.run.get(run_id="faf49b3a-7b64-4687-95c8-58ca8a41dd73", queue_position=False) assert res.workflow_run_model is not None @@ -36,6 +36,7 @@ with ComfyDeploy( | Parameter | Type | Required | Description | | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | | `run_id` | *str* | :heavy_check_mark: | N/A | +| `queue_position` | *Optional[bool]* | :heavy_minus_sign: | N/A | | `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.md) | :heavy_minus_sign: | Configuration to override the default retry behavior of the client. | ### Response @@ -55,6 +56,7 @@ Cancel Run ### Example Usage + ```python from comfydeploy import ComfyDeploy diff --git a/poetry.lock b/poetry.lock index 8e729f7..5e4bb53 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -91,21 +91,6 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] -[[package]] -name = "eval-type-backport" -version = "0.2.0" -description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "eval_type_backport-0.2.0-py3-none-any.whl", hash = "sha256:ac2f73d30d40c5a30a80b8739a789d6bb5e49fdffa66d7912667e2015d9c9933"}, - {file = "eval_type_backport-0.2.0.tar.gz", hash = "sha256:68796cfbc7371ebf923f03bdf7bef415f3ec098aeced24e054b253a0e78f7b37"}, -] - -[package.extras] -tests = ["pytest"] - [[package]] name = "exceptiongroup" version = "1.2.2" @@ -124,37 +109,37 @@ test = ["pytest (>=6)"] [[package]] name = "h11" -version = "0.14.0" +version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] [[package]] name = "httpcore" -version = "1.0.5" +version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, ] [package.dependencies] certifi = "*" -h11 = ">=0.13,<0.15" +h11 = ">=0.16" [package.extras] asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] +trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" @@ -222,50 +207,44 @@ files = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.15.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, + {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, + {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, + {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, + {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, + {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, + {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, + {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, + {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, + {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, + {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, + {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, + {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, ] [package.dependencies] @@ -292,6 +271,18 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"}, + {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, +] + [[package]] name = "platformdirs" version = "4.2.2" @@ -311,20 +302,21 @@ type = ["mypy (>=1.8)"] [[package]] name = "pydantic" -version = "2.10.5" +version = "2.11.10" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, - {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, + {file = "pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a"}, + {file = "pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" +pydantic-core = "2.33.2" typing-extensions = ">=4.12.2" +typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -332,112 +324,111 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.33.2" description = "Core functionality for Pydantic validation and serialization" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, ] [package.dependencies] @@ -475,31 +466,25 @@ spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] [[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" +name = "pyright" +version = "1.1.398" +description = "Command line wrapper for pyright" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] +python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, + {file = "pyright-1.1.398-py3-none-any.whl", hash = "sha256:0a70bfd007d9ea7de1cf9740e1ad1a40a122592cfe22a3f6791b06162ad08753"}, + {file = "pyright-1.1.398.tar.gz", hash = "sha256:357a13edd9be8082dc73be51190913e475fa41a6efb6ec0d4b7aab3bc11638d8"}, ] [package.dependencies] -six = ">=1.5" +nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["main"] -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] +[package.extras] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "sniffio" @@ -538,18 +523,6 @@ files = [ {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, ] -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20240316" -description = "Typing stubs for python-dateutil" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, -] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -579,5 +552,5 @@ typing-extensions = ">=4.12.0" [metadata] lock-version = "2.1" -python-versions = ">=3.9" -content-hash = "a287b0496a874e000c887363ddbaaa37fd0b896491785c7e9eccaee92e0bf43b" +python-versions = ">=3.9.2" +content-hash = "c0dd101f07fb15e54d4562dfdf249e128fb9507759d84012283e316c226a3b8c" diff --git a/poetry.toml b/poetry.toml index ab1033b..cd3492a 100644 --- a/poetry.toml +++ b/poetry.toml @@ -1,2 +1,3 @@ + [virtualenvs] in-project = true diff --git a/pyproject.toml b/pyproject.toml index 01cda0a..9c96d54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,15 @@ + [project] name = "comfydeploy" -version = "0.7.0" +version = "0.8.0" description = "Python Client SDK Generated by Speakeasy" authors = [{ name = "Speakeasy" },] readme = "README-PYPI.md" -requires-python = ">=3.9" +requires-python = ">=3.9.2" dependencies = [ - "eval-type-backport >=0.2.0", + "httpcore >=1.0.9", "httpx >=0.28.1", - "pydantic >=2.10.3", - "python-dateutil >=2.8.2", - "typing-inspection >=0.4.0", + "pydantic >=2.11.2", ] [tool.poetry] @@ -27,9 +26,9 @@ include = ["py.typed", "src/comfydeploy/py.typed"] in-project = true [tool.poetry.group.dev.dependencies] -mypy = "==1.14.1" +mypy = "==1.15.0" pylint = "==3.2.3" -types-python-dateutil = "^2.9.0.20240316" +pyright = "==1.1.398" [build-system] requires = ["poetry-core"] @@ -41,6 +40,8 @@ pythonpath = ["src"] [tool.mypy] disable_error_code = "misc" +explicit_package_bases = true +mypy_path = "src" [[tool.mypy.overrides]] module = "typing_inspect" diff --git a/scripts/prepare_readme.py b/scripts/prepare_readme.py index afb323a..3048971 100644 --- a/scripts/prepare_readme.py +++ b/scripts/prepare_readme.py @@ -10,12 +10,14 @@ GITHUB_URL = ( GITHUB_URL[: -len(".git")] if GITHUB_URL.endswith(".git") else GITHUB_URL ) + REPO_SUBDIR = "" # links on PyPI should have absolute URLs readme_contents = re.sub( r"(\[[^\]]+\]\()((?!https?:)[^\)]+)(\))", lambda m: m.group(1) + GITHUB_URL + "/blob/master/" + + REPO_SUBDIR + m.group(2) + m.group(3), readme_contents, diff --git a/scripts/publish.sh b/scripts/publish.sh index f2f2cf2..2a3ead7 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - export POETRY_PYPI_TOKEN_PYPI=${PYPI_TOKEN} poetry run python scripts/prepare_readme.py diff --git a/src/comfydeploy/_hooks/__init__.py b/src/comfydeploy/_hooks/__init__.py index 2ee66cd..e763be4 100644 --- a/src/comfydeploy/_hooks/__init__.py +++ b/src/comfydeploy/_hooks/__init__.py @@ -2,4 +2,3 @@ from .sdkhooks import * from .types import * -from .registration import * diff --git a/src/comfydeploy/_hooks/sdkhooks.py b/src/comfydeploy/_hooks/sdkhooks.py index 78fb71c..cb7dc1b 100644 --- a/src/comfydeploy/_hooks/sdkhooks.py +++ b/src/comfydeploy/_hooks/sdkhooks.py @@ -11,7 +11,6 @@ AfterErrorHook, Hooks, ) -from .registration import init_hooks from typing import List, Optional, Tuple from comfydeploy.httpclient import HttpClient @@ -22,7 +21,6 @@ def __init__(self) -> None: self.before_request_hooks: List[BeforeRequestHook] = [] self.after_success_hooks: List[AfterSuccessHook] = [] self.after_error_hooks: List[AfterErrorHook] = [] - init_hooks(self) def register_sdk_init_hook(self, hook: SDKInitHook) -> None: self.sdk_init_hooks.append(hook) diff --git a/src/comfydeploy/_hooks/types.py b/src/comfydeploy/_hooks/types.py index 104f35d..8311d14 100644 --- a/src/comfydeploy/_hooks/types.py +++ b/src/comfydeploy/_hooks/types.py @@ -2,11 +2,13 @@ from abc import ABC, abstractmethod from comfydeploy.httpclient import HttpClient +from comfydeploy.sdkconfiguration import SDKConfiguration import httpx from typing import Any, Callable, List, Optional, Tuple, Union class HookContext: + config: SDKConfiguration base_url: str operation_id: str oauth2_scopes: Optional[List[str]] = None @@ -14,11 +16,13 @@ class HookContext: def __init__( self, + config: SDKConfiguration, base_url: str, operation_id: str, oauth2_scopes: Optional[List[str]], security_source: Optional[Union[Any, Callable[[], Any]]], ): + self.config = config self.base_url = base_url self.operation_id = operation_id self.oauth2_scopes = oauth2_scopes @@ -28,6 +32,7 @@ def __init__( class BeforeRequestContext(HookContext): def __init__(self, hook_ctx: HookContext): super().__init__( + hook_ctx.config, hook_ctx.base_url, hook_ctx.operation_id, hook_ctx.oauth2_scopes, @@ -38,6 +43,7 @@ def __init__(self, hook_ctx: HookContext): class AfterSuccessContext(HookContext): def __init__(self, hook_ctx: HookContext): super().__init__( + hook_ctx.config, hook_ctx.base_url, hook_ctx.operation_id, hook_ctx.oauth2_scopes, @@ -48,6 +54,7 @@ def __init__(self, hook_ctx: HookContext): class AfterErrorContext(HookContext): def __init__(self, hook_ctx: HookContext): super().__init__( + hook_ctx.config, hook_ctx.base_url, hook_ctx.operation_id, hook_ctx.oauth2_scopes, diff --git a/src/comfydeploy/_version.py b/src/comfydeploy/_version.py index cd7e8b2..34247de 100644 --- a/src/comfydeploy/_version.py +++ b/src/comfydeploy/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "comfydeploy" -__version__: str = "0.7.0" +__version__: str = "0.8.0" __openapi_doc_version__: str = "V2" -__gen_version__: str = "2.560.1" -__user_agent__: str = "speakeasy-sdk/python 0.7.0 2.560.1 V2 comfydeploy" +__gen_version__: str = "2.801.2" +__user_agent__: str = "speakeasy-sdk/python 0.8.0 2.801.2 V2 comfydeploy" try: if __package__ is not None: diff --git a/src/comfydeploy/basesdk.py b/src/comfydeploy/basesdk.py index 1dcb5e5..3b7ec8e 100644 --- a/src/comfydeploy/basesdk.py +++ b/src/comfydeploy/basesdk.py @@ -8,7 +8,12 @@ BeforeRequestContext, ) from comfydeploy.models import errors -from comfydeploy.utils import RetryConfig, SerializedRequestBody, get_body_content +from comfydeploy.utils import ( + RetryConfig, + SerializedRequestBody, + get_body_content, + run_sync_in_thread, +) import httpx from typing import Callable, List, Mapping, Optional, Tuple from urllib.parse import parse_qs, urlparse @@ -16,9 +21,19 @@ class BaseSDK: sdk_configuration: SDKConfiguration + parent_ref: Optional[object] = None + """ + Reference to the root SDK instance, if any. This will prevent it from + being garbage collected while there are active streams. + """ - def __init__(self, sdk_config: SDKConfiguration) -> None: + def __init__( + self, + sdk_config: SDKConfiguration, + parent_ref: Optional[object] = None, + ) -> None: self.sdk_configuration = sdk_config + self.parent_ref = parent_ref def _get_url(self, base_url, url_variables): sdk_url, sdk_variables = self.sdk_configuration.get_server_details() @@ -51,6 +66,7 @@ def _build_request_async( ] = None, url_override: Optional[str] = None, http_headers: Optional[Mapping[str, str]] = None, + allow_empty_value: Optional[List[str]] = None, ) -> httpx.Request: client = self.sdk_configuration.async_client return self._build_request_with_client( @@ -71,6 +87,7 @@ def _build_request_async( get_serialized_body, url_override, http_headers, + allow_empty_value, ) def _build_request( @@ -93,6 +110,7 @@ def _build_request( ] = None, url_override: Optional[str] = None, http_headers: Optional[Mapping[str, str]] = None, + allow_empty_value: Optional[List[str]] = None, ) -> httpx.Request: client = self.sdk_configuration.client return self._build_request_with_client( @@ -113,6 +131,7 @@ def _build_request( get_serialized_body, url_override, http_headers, + allow_empty_value, ) def _build_request_with_client( @@ -136,6 +155,7 @@ def _build_request_with_client( ] = None, url_override: Optional[str] = None, http_headers: Optional[Mapping[str, str]] = None, + allow_empty_value: Optional[List[str]] = None, ) -> httpx.Request: query_params = {} @@ -151,6 +171,7 @@ def _build_request_with_client( query_params = utils.get_query_params( request if request_has_query_params else None, _globals if request_has_query_params else None, + allow_empty_value, ) else: # Pick up the query parameter from the override so they can be @@ -219,12 +240,12 @@ def do_request( client = self.sdk_configuration.client logger = self.sdk_configuration.debug_logger + hooks = self.sdk_configuration.__dict__["_hooks"] + def do(): http_res = None try: - req = self.sdk_configuration.get_hooks().before_request( - BeforeRequestContext(hook_ctx), request - ) + req = hooks.before_request(BeforeRequestContext(hook_ctx), request) logger.debug( "Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s", req.method, @@ -238,16 +259,14 @@ def do(): http_res = client.send(req, stream=stream) except Exception as e: - _, e = self.sdk_configuration.get_hooks().after_error( - AfterErrorContext(hook_ctx), None, e - ) + _, e = hooks.after_error(AfterErrorContext(hook_ctx), None, e) if e is not None: logger.debug("Request Exception", exc_info=True) raise e if http_res is None: logger.debug("Raising no response SDK error") - raise errors.SDKError("No response received") + raise errors.NoResponseError("No response received") logger.debug( "Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s", @@ -258,7 +277,7 @@ def do(): ) if utils.match_status_codes(error_status_codes, http_res.status_code): - result, err = self.sdk_configuration.get_hooks().after_error( + result, err = hooks.after_error( AfterErrorContext(hook_ctx), http_res, None ) if err is not None: @@ -268,7 +287,7 @@ def do(): http_res = result else: logger.debug("Raising unexpected SDK error") - raise errors.SDKError("Unexpected error occurred") + raise errors.SDKError("Unexpected error occurred", http_res) return http_res @@ -278,9 +297,7 @@ def do(): http_res = do() if not utils.match_status_codes(error_status_codes, http_res.status_code): - http_res = self.sdk_configuration.get_hooks().after_success( - AfterSuccessContext(hook_ctx), http_res - ) + http_res = hooks.after_success(AfterSuccessContext(hook_ctx), http_res) return http_res @@ -295,12 +312,15 @@ async def do_request_async( client = self.sdk_configuration.async_client logger = self.sdk_configuration.debug_logger + hooks = self.sdk_configuration.__dict__["_hooks"] + async def do(): http_res = None try: - req = self.sdk_configuration.get_hooks().before_request( - BeforeRequestContext(hook_ctx), request + req = await run_sync_in_thread( + hooks.before_request, BeforeRequestContext(hook_ctx), request ) + logger.debug( "Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s", req.method, @@ -314,16 +334,17 @@ async def do(): http_res = await client.send(req, stream=stream) except Exception as e: - _, e = self.sdk_configuration.get_hooks().after_error( - AfterErrorContext(hook_ctx), None, e + _, e = await run_sync_in_thread( + hooks.after_error, AfterErrorContext(hook_ctx), None, e ) + if e is not None: logger.debug("Request Exception", exc_info=True) raise e if http_res is None: logger.debug("Raising no response SDK error") - raise errors.SDKError("No response received") + raise errors.NoResponseError("No response received") logger.debug( "Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s", @@ -334,9 +355,10 @@ async def do(): ) if utils.match_status_codes(error_status_codes, http_res.status_code): - result, err = self.sdk_configuration.get_hooks().after_error( - AfterErrorContext(hook_ctx), http_res, None + result, err = await run_sync_in_thread( + hooks.after_error, AfterErrorContext(hook_ctx), http_res, None ) + if err is not None: logger.debug("Request Exception", exc_info=True) raise err @@ -344,7 +366,7 @@ async def do(): http_res = result else: logger.debug("Raising unexpected SDK error") - raise errors.SDKError("Unexpected error occurred") + raise errors.SDKError("Unexpected error occurred", http_res) return http_res @@ -356,8 +378,8 @@ async def do(): http_res = await do() if not utils.match_status_codes(error_status_codes, http_res.status_code): - http_res = self.sdk_configuration.get_hooks().after_success( - AfterSuccessContext(hook_ctx), http_res + http_res = await run_sync_in_thread( + hooks.after_success, AfterSuccessContext(hook_ctx), http_res ) return http_res diff --git a/src/comfydeploy/deployment.py b/src/comfydeploy/deployment.py index 9c35fff..c2a7d34 100644 --- a/src/comfydeploy/deployment.py +++ b/src/comfydeploy/deployment.py @@ -5,6 +5,7 @@ from comfydeploy._hooks import HookContext from comfydeploy.models import components, errors, operations from comfydeploy.types import BaseModel, OptionalNullable, UNSET +from comfydeploy.utils.unmarshal_json_response import unmarshal_json_response from typing import Any, Mapping, Optional, Union, cast @@ -60,6 +61,7 @@ def queue( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.DeploymentRunRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -73,9 +75,10 @@ def queue( http_res = self.do_request( hook_ctx=HookContext( + config=self.sdk_configuration, base_url=base_url or "", operation_id="queue_deployment_run_run_deployment_queue_post", - oauth2_scopes=[], + oauth2_scopes=None, security_source=self.sdk_configuration.security, ), request=req, @@ -86,35 +89,24 @@ def queue( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return operations.QueueDeploymentRunRunDeploymentQueuePostResponse( - create_run_response=utils.unmarshal_json( - http_res.text, Optional[components.CreateRunResponse] + create_run_response=unmarshal_json_response( + Optional[components.CreateRunResponse], http_res ), http_meta=components.HTTPMetadata(request=req, response=http_res), ) if utils.match_response(http_res, "422", "application/json"): - response_data = utils.unmarshal_json( - http_res.text, errors.HTTPValidationErrorData + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res ) - raise errors.HTTPValidationError(data=response_data) + raise errors.HTTPValidationError(response_data, http_res) if utils.match_response(http_res, "4XX", "*"): http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) if utils.match_response(http_res, "5XX", "*"): http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) - content_type = http_res.headers.get("Content-Type") - http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", - http_res.status_code, - http_res_text, - http_res, - ) + raise errors.SDKError("Unexpected response received", http_res) async def queue_async( self, @@ -167,6 +159,7 @@ async def queue_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.DeploymentRunRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -180,9 +173,10 @@ async def queue_async( http_res = await self.do_request_async( hook_ctx=HookContext( + config=self.sdk_configuration, base_url=base_url or "", operation_id="queue_deployment_run_run_deployment_queue_post", - oauth2_scopes=[], + oauth2_scopes=None, security_source=self.sdk_configuration.security, ), request=req, @@ -193,32 +187,21 @@ async def queue_async( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return operations.QueueDeploymentRunRunDeploymentQueuePostResponse( - create_run_response=utils.unmarshal_json( - http_res.text, Optional[components.CreateRunResponse] + create_run_response=unmarshal_json_response( + Optional[components.CreateRunResponse], http_res ), http_meta=components.HTTPMetadata(request=req, response=http_res), ) if utils.match_response(http_res, "422", "application/json"): - response_data = utils.unmarshal_json( - http_res.text, errors.HTTPValidationErrorData + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res ) - raise errors.HTTPValidationError(data=response_data) + raise errors.HTTPValidationError(response_data, http_res) if utils.match_response(http_res, "4XX", "*"): http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) if utils.match_response(http_res, "5XX", "*"): http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) - content_type = http_res.headers.get("Content-Type") - http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", - http_res.status_code, - http_res_text, - http_res, - ) + raise errors.SDKError("Unexpected response received", http_res) diff --git a/src/comfydeploy/httpclient.py b/src/comfydeploy/httpclient.py index 1e42635..89560b5 100644 --- a/src/comfydeploy/httpclient.py +++ b/src/comfydeploy/httpclient.py @@ -2,7 +2,6 @@ # pyright: reportReturnType = false import asyncio -from concurrent.futures import ThreadPoolExecutor from typing_extensions import Protocol, runtime_checkable import httpx from typing import Any, Optional, Union @@ -108,7 +107,6 @@ def close_clients( # to them from the owning SDK instance and they can be reaped. owner.client = None owner.async_client = None - if sync_client is not None and not sync_client_supplied: try: sync_client.close() @@ -116,21 +114,12 @@ def close_clients( pass if async_client is not None and not async_client_supplied: - is_async = False try: - asyncio.get_running_loop() - is_async = True + loop = asyncio.get_running_loop() + asyncio.run_coroutine_threadsafe(async_client.aclose(), loop) except RuntimeError: - pass - - try: - # If this function is called in an async loop then start another - # loop in a separate thread to close the async http client. - if is_async: - with ThreadPoolExecutor(max_workers=1) as executor: - future = executor.submit(asyncio.run, async_client.aclose()) - future.result() - else: + try: asyncio.run(async_client.aclose()) - except Exception: - pass + except RuntimeError: + # best effort + pass diff --git a/src/comfydeploy/models/__init__.py b/src/comfydeploy/models/__init__.py index cc53976..726fc5e 100644 --- a/src/comfydeploy/models/__init__.py +++ b/src/comfydeploy/models/__init__.py @@ -1,4 +1,3 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" # package - diff --git a/src/comfydeploy/models/components/__init__.py b/src/comfydeploy/models/components/__init__.py index 07c981b..b84a87d 100644 --- a/src/comfydeploy/models/components/__init__.py +++ b/src/comfydeploy/models/components/__init__.py @@ -1,39 +1,35 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from .createrunresponse import CreateRunResponse, CreateRunResponseTypedDict -from .deploymentrunrequest import ( - DeploymentRunRequest, - DeploymentRunRequestTypedDict, - Gpu, - Inputs, - InputsTypedDict, -) -from .httpmetadata import HTTPMetadata, HTTPMetadataTypedDict -from .mediaitem import MediaItem, MediaItemTypedDict -from .security import Security, SecurityTypedDict -from .validationerror import ( - Loc, - LocTypedDict, - ValidationError, - ValidationErrorTypedDict, -) -from .workflowrunmodel import WorkflowRunModel, WorkflowRunModelTypedDict -from .workflowrunoutputmodel import ( - Data, - DataTypedDict, - WorkflowRunOutputModel, - WorkflowRunOutputModelTypedDict, -) -from .workflowrunstatus import WorkflowRunStatus -from .workflowrunwebhookbody import ( - WorkflowRunWebhookBody, - WorkflowRunWebhookBodyTypedDict, -) -from .workflowrunwebhookresponse import ( - WorkflowRunWebhookResponse, - WorkflowRunWebhookResponseTypedDict, -) +from typing import TYPE_CHECKING +from importlib import import_module +import builtins +import sys +if TYPE_CHECKING: + from .createrunresponse import CreateRunResponse, CreateRunResponseTypedDict + from .deploymentrunrequest import ( + DeploymentRunRequest, + DeploymentRunRequestTypedDict, + Gpu, + Inputs, + InputsTypedDict, + ) + from .httpmetadata import HTTPMetadata, HTTPMetadataTypedDict + from .mediaitem import MediaItem, MediaItemTypedDict + from .security import Security, SecurityTypedDict + from .validationerror import ( + Loc, + LocTypedDict, + ValidationError, + ValidationErrorTypedDict, + ) + from .workflowrunmodel import WorkflowRunModel, WorkflowRunModelTypedDict + from .workflowrunoutputmodel import ( + Data, + DataTypedDict, + WorkflowRunOutputModel, + WorkflowRunOutputModelTypedDict, + ) __all__ = [ "CreateRunResponse", @@ -59,9 +55,68 @@ "WorkflowRunModelTypedDict", "WorkflowRunOutputModel", "WorkflowRunOutputModelTypedDict", - "WorkflowRunStatus", - "WorkflowRunWebhookBody", - "WorkflowRunWebhookBodyTypedDict", - "WorkflowRunWebhookResponse", - "WorkflowRunWebhookResponseTypedDict", ] + +_dynamic_imports: dict[str, str] = { + "CreateRunResponse": ".createrunresponse", + "CreateRunResponseTypedDict": ".createrunresponse", + "DeploymentRunRequest": ".deploymentrunrequest", + "DeploymentRunRequestTypedDict": ".deploymentrunrequest", + "Gpu": ".deploymentrunrequest", + "Inputs": ".deploymentrunrequest", + "InputsTypedDict": ".deploymentrunrequest", + "HTTPMetadata": ".httpmetadata", + "HTTPMetadataTypedDict": ".httpmetadata", + "MediaItem": ".mediaitem", + "MediaItemTypedDict": ".mediaitem", + "Security": ".security", + "SecurityTypedDict": ".security", + "Loc": ".validationerror", + "LocTypedDict": ".validationerror", + "ValidationError": ".validationerror", + "ValidationErrorTypedDict": ".validationerror", + "WorkflowRunModel": ".workflowrunmodel", + "WorkflowRunModelTypedDict": ".workflowrunmodel", + "Data": ".workflowrunoutputmodel", + "DataTypedDict": ".workflowrunoutputmodel", + "WorkflowRunOutputModel": ".workflowrunoutputmodel", + "WorkflowRunOutputModelTypedDict": ".workflowrunoutputmodel", +} + + +def dynamic_import(modname, retries=3): + for attempt in range(retries): + try: + return import_module(modname, __package__) + except KeyError: + # Clear any half-initialized module and retry + sys.modules.pop(modname, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + +def __getattr__(attr_name: str) -> object: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__} " + ) + + try: + module = dynamic_import(module_name) + result = getattr(module, attr_name) + return result + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = builtins.list(_dynamic_imports.keys()) + return builtins.sorted(lazy_attrs) diff --git a/src/comfydeploy/models/components/deploymentrunrequest.py b/src/comfydeploy/models/components/deploymentrunrequest.py index 65e03b4..90a18e6 100644 --- a/src/comfydeploy/models/components/deploymentrunrequest.py +++ b/src/comfydeploy/models/components/deploymentrunrequest.py @@ -1,8 +1,15 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from __future__ import annotations -from comfydeploy.types import BaseModel +from comfydeploy.types import ( + BaseModel, + Nullable, + OptionalNullable, + UNSET, + UNSET_SENTINEL, +) from enum import Enum +from pydantic import model_serializer from typing import Any, Dict, List, Optional, Union from typing_extensions import NotRequired, TypeAliasType, TypedDict @@ -18,6 +25,7 @@ class Gpu(str, Enum): r"""The GPU to override the machine's default GPU""" + CPU = "CPU" T4 = "T4" L4 = "L4" A10_G = "A10G" @@ -25,6 +33,8 @@ class Gpu(str, Enum): A100 = "A100" A100_80_GB = "A100-80GB" H100 = "H100" + H200 = "H200" + B200 = "B200" class DeploymentRunRequestTypedDict(TypedDict): @@ -35,6 +45,8 @@ class DeploymentRunRequestTypedDict(TypedDict): webhook_intermediate_status: NotRequired[bool] gpu: NotRequired[Gpu] r"""The GPU to override the machine's default GPU""" + flags: NotRequired[Nullable[List[str]]] + r"""Array of flag strings""" class DeploymentRunRequest(BaseModel): @@ -49,3 +61,33 @@ class DeploymentRunRequest(BaseModel): gpu: Optional[Gpu] = None r"""The GPU to override the machine's default GPU""" + + flags: OptionalNullable[List[str]] = UNSET + r"""Array of flag strings""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set( + ["inputs", "webhook", "webhook_intermediate_status", "gpu", "flags"] + ) + nullable_fields = set(["flags"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k) + is_nullable_and_explicitly_set = ( + k in nullable_fields + and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member + ) + + if val != UNSET_SENTINEL: + if ( + val is not None + or k not in optional_fields + or is_nullable_and_explicitly_set + ): + m[k] = val + + return m diff --git a/src/comfydeploy/models/components/mediaitem.py b/src/comfydeploy/models/components/mediaitem.py index 21ef41e..61b19a6 100644 --- a/src/comfydeploy/models/components/mediaitem.py +++ b/src/comfydeploy/models/components/mediaitem.py @@ -36,30 +36,25 @@ class MediaItem(BaseModel): @model_serializer(mode="wrap") def serialize_model(self, handler): - optional_fields = ["is_public", "subfolder", "upload_duration"] - nullable_fields = ["is_public", "subfolder", "upload_duration"] - null_default_fields = [] - + optional_fields = set(["is_public", "subfolder", "upload_duration"]) + nullable_fields = set(["is_public", "subfolder", "upload_duration"]) serialized = handler(self) - m = {} - for n, f in self.model_fields.items(): + for n, f in type(self).model_fields.items(): k = f.alias or n val = serialized.get(k) - serialized.pop(k, None) - - optional_nullable = k in optional_fields and k in nullable_fields - is_set = ( - self.__pydantic_fields_set__.intersection({n}) - or k in null_default_fields - ) # pylint: disable=no-member - - if val is not None and val != UNSET_SENTINEL: - m[k] = val - elif val != UNSET_SENTINEL and ( - not k in optional_fields or (optional_nullable and is_set) - ): - m[k] = val + is_nullable_and_explicitly_set = ( + k in nullable_fields + and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member + ) + + if val != UNSET_SENTINEL: + if ( + val is not None + or k not in optional_fields + or is_nullable_and_explicitly_set + ): + m[k] = val return m diff --git a/src/comfydeploy/models/components/workflowrunmodel.py b/src/comfydeploy/models/components/workflowrunmodel.py index 17b6d68..676ce0c 100644 --- a/src/comfydeploy/models/components/workflowrunmodel.py +++ b/src/comfydeploy/models/components/workflowrunmodel.py @@ -23,7 +23,6 @@ class WorkflowRunModelTypedDict(TypedDict): workflow_version_id: Nullable[str] workflow_inputs: Nullable[Any] workflow_id: str - workflow_api: Nullable[Any] machine_id: Nullable[str] origin: str status: str @@ -39,11 +38,6 @@ class WorkflowRunModelTypedDict(TypedDict): live_status: Nullable[str] webhook: Nullable[str] webhook_status: Nullable[str] - number: int - duration: Nullable[float] - cold_start_duration: Nullable[float] - cold_start_duration_total: Nullable[float] - run_duration: Nullable[float] ended_at: NotRequired[Nullable[datetime]] queued_at: NotRequired[Nullable[datetime]] started_at: NotRequired[Nullable[datetime]] @@ -51,6 +45,12 @@ class WorkflowRunModelTypedDict(TypedDict): is_realtime: NotRequired[bool] webhook_intermediate_status: NotRequired[bool] outputs: NotRequired[List[WorkflowRunOutputModelTypedDict]] + number: NotRequired[Nullable[int]] + duration: NotRequired[Nullable[float]] + cold_start_duration: NotRequired[Nullable[float]] + cold_start_duration_total: NotRequired[Nullable[float]] + run_duration: NotRequired[Nullable[float]] + queue_position: NotRequired[Nullable[int]] class WorkflowRunModel(BaseModel): @@ -62,8 +62,6 @@ class WorkflowRunModel(BaseModel): workflow_id: str - workflow_api: Nullable[Any] - machine_id: Nullable[str] origin: str @@ -94,16 +92,6 @@ class WorkflowRunModel(BaseModel): webhook_status: Nullable[str] - number: int - - duration: Nullable[float] - - cold_start_duration: Nullable[float] - - cold_start_duration_total: Nullable[float] - - run_duration: Nullable[float] - ended_at: OptionalNullable[datetime] = UNSET queued_at: OptionalNullable[datetime] = UNSET @@ -118,62 +106,80 @@ class WorkflowRunModel(BaseModel): outputs: Optional[List[WorkflowRunOutputModel]] = None + number: OptionalNullable[int] = UNSET + + duration: OptionalNullable[float] = UNSET + + cold_start_duration: OptionalNullable[float] = UNSET + + cold_start_duration_total: OptionalNullable[float] = UNSET + + run_duration: OptionalNullable[float] = UNSET + + queue_position: OptionalNullable[int] = UNSET + @model_serializer(mode="wrap") def serialize_model(self, handler): - optional_fields = [ - "ended_at", - "queued_at", - "started_at", - "progress", - "is_realtime", - "webhook_intermediate_status", - "outputs", - ] - nullable_fields = [ - "workflow_version_id", - "workflow_inputs", - "workflow_api", - "machine_id", - "gpu_event_id", - "gpu", - "machine_version", - "machine_type", - "modal_function_call_id", - "user_id", - "org_id", - "live_status", - "webhook", - "webhook_status", - "duration", - "cold_start_duration", - "cold_start_duration_total", - "run_duration", - "ended_at", - "queued_at", - "started_at", - ] - null_default_fields = [] - + optional_fields = set( + [ + "ended_at", + "queued_at", + "started_at", + "progress", + "is_realtime", + "webhook_intermediate_status", + "outputs", + "number", + "duration", + "cold_start_duration", + "cold_start_duration_total", + "run_duration", + "queue_position", + ] + ) + nullable_fields = set( + [ + "workflow_version_id", + "workflow_inputs", + "machine_id", + "ended_at", + "queued_at", + "started_at", + "gpu_event_id", + "gpu", + "machine_version", + "machine_type", + "modal_function_call_id", + "user_id", + "org_id", + "live_status", + "webhook", + "webhook_status", + "number", + "duration", + "cold_start_duration", + "cold_start_duration_total", + "run_duration", + "queue_position", + ] + ) serialized = handler(self) - m = {} - for n, f in self.model_fields.items(): + for n, f in type(self).model_fields.items(): k = f.alias or n val = serialized.get(k) - serialized.pop(k, None) - - optional_nullable = k in optional_fields and k in nullable_fields - is_set = ( - self.__pydantic_fields_set__.intersection({n}) - or k in null_default_fields - ) # pylint: disable=no-member - - if val is not None and val != UNSET_SENTINEL: - m[k] = val - elif val != UNSET_SENTINEL and ( - not k in optional_fields or (optional_nullable and is_set) - ): - m[k] = val + is_nullable_and_explicitly_set = ( + k in nullable_fields + and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member + ) + + if val != UNSET_SENTINEL: + if ( + val is not None + or k not in optional_fields + or is_nullable_and_explicitly_set + ): + m[k] = val return m diff --git a/src/comfydeploy/models/components/workflowrunoutputmodel.py b/src/comfydeploy/models/components/workflowrunoutputmodel.py index 2be4b41..71433d8 100644 --- a/src/comfydeploy/models/components/workflowrunoutputmodel.py +++ b/src/comfydeploy/models/components/workflowrunoutputmodel.py @@ -28,6 +28,7 @@ class WorkflowRunOutputModelTypedDict(TypedDict): node_meta: Nullable[Any] created_at: datetime updated_at: datetime + output_id: NotRequired[Nullable[str]] type: NotRequired[Nullable[str]] node_id: NotRequired[Nullable[str]] @@ -45,36 +46,33 @@ class WorkflowRunOutputModel(BaseModel): updated_at: datetime + output_id: OptionalNullable[str] = UNSET + type: OptionalNullable[str] = UNSET node_id: OptionalNullable[str] = UNSET @model_serializer(mode="wrap") def serialize_model(self, handler): - optional_fields = ["type", "node_id"] - nullable_fields = ["node_meta", "type", "node_id"] - null_default_fields = [] - + optional_fields = set(["output_id", "type", "node_id"]) + nullable_fields = set(["output_id", "node_meta", "type", "node_id"]) serialized = handler(self) - m = {} - for n, f in self.model_fields.items(): + for n, f in type(self).model_fields.items(): k = f.alias or n val = serialized.get(k) - serialized.pop(k, None) - - optional_nullable = k in optional_fields and k in nullable_fields - is_set = ( - self.__pydantic_fields_set__.intersection({n}) - or k in null_default_fields - ) # pylint: disable=no-member - - if val is not None and val != UNSET_SENTINEL: - m[k] = val - elif val != UNSET_SENTINEL and ( - not k in optional_fields or (optional_nullable and is_set) - ): - m[k] = val + is_nullable_and_explicitly_set = ( + k in nullable_fields + and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member + ) + + if val != UNSET_SENTINEL: + if ( + val is not None + or k not in optional_fields + or is_nullable_and_explicitly_set + ): + m[k] = val return m diff --git a/src/comfydeploy/models/components/workflowrunstatus.py b/src/comfydeploy/models/components/workflowrunstatus.py deleted file mode 100644 index 297ced9..0000000 --- a/src/comfydeploy/models/components/workflowrunstatus.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" - -from __future__ import annotations -from enum import Enum - - -class WorkflowRunStatus(str, Enum): - NOT_STARTED = "not-started" - RUNNING = "running" - UPLOADING = "uploading" - SUCCESS = "success" - FAILED = "failed" - STARTED = "started" - QUEUED = "queued" - TIMEOUT = "timeout" - CANCELLED = "cancelled" diff --git a/src/comfydeploy/models/components/workflowrunwebhookbody.py b/src/comfydeploy/models/components/workflowrunwebhookbody.py deleted file mode 100644 index 460c808..0000000 --- a/src/comfydeploy/models/components/workflowrunwebhookbody.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" - -from __future__ import annotations -from .workflowrunoutputmodel import ( - WorkflowRunOutputModel, - WorkflowRunOutputModelTypedDict, -) -from .workflowrunstatus import WorkflowRunStatus -from comfydeploy.types import BaseModel, Nullable, UNSET_SENTINEL -from pydantic import model_serializer -from typing import List, Optional -from typing_extensions import NotRequired, TypedDict - - -class WorkflowRunWebhookBodyTypedDict(TypedDict): - run_id: str - status: WorkflowRunStatus - live_status: Nullable[str] - progress: NotRequired[float] - outputs: NotRequired[List[WorkflowRunOutputModelTypedDict]] - - -class WorkflowRunWebhookBody(BaseModel): - run_id: str - - status: WorkflowRunStatus - - live_status: Nullable[str] - - progress: Optional[float] = 0 - - outputs: Optional[List[WorkflowRunOutputModel]] = None - - @model_serializer(mode="wrap") - def serialize_model(self, handler): - optional_fields = ["progress", "outputs"] - nullable_fields = ["live_status"] - null_default_fields = [] - - serialized = handler(self) - - m = {} - - for n, f in self.model_fields.items(): - k = f.alias or n - val = serialized.get(k) - serialized.pop(k, None) - - optional_nullable = k in optional_fields and k in nullable_fields - is_set = ( - self.__pydantic_fields_set__.intersection({n}) - or k in null_default_fields - ) # pylint: disable=no-member - - if val is not None and val != UNSET_SENTINEL: - m[k] = val - elif val != UNSET_SENTINEL and ( - not k in optional_fields or (optional_nullable and is_set) - ): - m[k] = val - - return m diff --git a/src/comfydeploy/models/components/workflowrunwebhookresponse.py b/src/comfydeploy/models/components/workflowrunwebhookresponse.py deleted file mode 100644 index 1e7172b..0000000 --- a/src/comfydeploy/models/components/workflowrunwebhookresponse.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" - -from __future__ import annotations -from comfydeploy.types import BaseModel -from typing_extensions import TypedDict - - -class WorkflowRunWebhookResponseTypedDict(TypedDict): - status: str - - -class WorkflowRunWebhookResponse(BaseModel): - status: str diff --git a/src/comfydeploy/models/errors/__init__.py b/src/comfydeploy/models/errors/__init__.py index 611acae..e202785 100644 --- a/src/comfydeploy/models/errors/__init__.py +++ b/src/comfydeploy/models/errors/__init__.py @@ -1,7 +1,68 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from .httpvalidationerror import HTTPValidationError, HTTPValidationErrorData -from .sdkerror import SDKError +from .comfydeployerror import ComfyDeployError +from typing import TYPE_CHECKING +from importlib import import_module +import builtins +import sys +if TYPE_CHECKING: + from .httpvalidationerror import HTTPValidationError, HTTPValidationErrorData + from .no_response_error import NoResponseError + from .responsevalidationerror import ResponseValidationError + from .sdkerror import SDKError -__all__ = ["HTTPValidationError", "HTTPValidationErrorData", "SDKError"] +__all__ = [ + "ComfyDeployError", + "HTTPValidationError", + "HTTPValidationErrorData", + "NoResponseError", + "ResponseValidationError", + "SDKError", +] + +_dynamic_imports: dict[str, str] = { + "HTTPValidationError": ".httpvalidationerror", + "HTTPValidationErrorData": ".httpvalidationerror", + "NoResponseError": ".no_response_error", + "ResponseValidationError": ".responsevalidationerror", + "SDKError": ".sdkerror", +} + + +def dynamic_import(modname, retries=3): + for attempt in range(retries): + try: + return import_module(modname, __package__) + except KeyError: + # Clear any half-initialized module and retry + sys.modules.pop(modname, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + +def __getattr__(attr_name: str) -> object: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__} " + ) + + try: + module = dynamic_import(module_name) + result = getattr(module, attr_name) + return result + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = builtins.list(_dynamic_imports.keys()) + return builtins.sorted(lazy_attrs) diff --git a/src/comfydeploy/models/errors/comfydeployerror.py b/src/comfydeploy/models/errors/comfydeployerror.py new file mode 100644 index 0000000..8640879 --- /dev/null +++ b/src/comfydeploy/models/errors/comfydeployerror.py @@ -0,0 +1,30 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +import httpx +from typing import Optional +from dataclasses import dataclass, field + + +@dataclass(unsafe_hash=True) +class ComfyDeployError(Exception): + """The base class for all HTTP error responses.""" + + message: str + status_code: int + body: str + headers: httpx.Headers = field(hash=False) + raw_response: httpx.Response = field(hash=False) + + def __init__( + self, message: str, raw_response: httpx.Response, body: Optional[str] = None + ): + object.__setattr__(self, "message", message) + object.__setattr__(self, "status_code", raw_response.status_code) + object.__setattr__( + self, "body", body if body is not None else raw_response.text + ) + object.__setattr__(self, "headers", raw_response.headers) + object.__setattr__(self, "raw_response", raw_response) + + def __str__(self): + return self.message diff --git a/src/comfydeploy/models/errors/httpvalidationerror.py b/src/comfydeploy/models/errors/httpvalidationerror.py index 7b38ba1..fd09b4d 100644 --- a/src/comfydeploy/models/errors/httpvalidationerror.py +++ b/src/comfydeploy/models/errors/httpvalidationerror.py @@ -1,9 +1,11 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from __future__ import annotations -from comfydeploy import utils from comfydeploy.models.components import validationerror as components_validationerror +from comfydeploy.models.errors import ComfyDeployError from comfydeploy.types import BaseModel +from dataclasses import dataclass, field +import httpx from typing import List, Optional @@ -11,11 +13,16 @@ class HTTPValidationErrorData(BaseModel): detail: Optional[List[components_validationerror.ValidationError]] = None -class HTTPValidationError(Exception): - data: HTTPValidationErrorData +@dataclass(unsafe_hash=True) +class HTTPValidationError(ComfyDeployError): + data: HTTPValidationErrorData = field(hash=False) - def __init__(self, data: HTTPValidationErrorData): - self.data = data - - def __str__(self) -> str: - return utils.marshal_json(self.data, HTTPValidationErrorData) + def __init__( + self, + data: HTTPValidationErrorData, + raw_response: httpx.Response, + body: Optional[str] = None, + ): + message = body or raw_response.text + super().__init__(message, raw_response, body) + object.__setattr__(self, "data", data) diff --git a/src/comfydeploy/models/errors/no_response_error.py b/src/comfydeploy/models/errors/no_response_error.py new file mode 100644 index 0000000..1deab64 --- /dev/null +++ b/src/comfydeploy/models/errors/no_response_error.py @@ -0,0 +1,17 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from dataclasses import dataclass + + +@dataclass(unsafe_hash=True) +class NoResponseError(Exception): + """Error raised when no HTTP response is received from the server.""" + + message: str + + def __init__(self, message: str = "No response received"): + object.__setattr__(self, "message", message) + super().__init__(message) + + def __str__(self): + return self.message diff --git a/src/comfydeploy/models/errors/responsevalidationerror.py b/src/comfydeploy/models/errors/responsevalidationerror.py new file mode 100644 index 0000000..f6d8edb --- /dev/null +++ b/src/comfydeploy/models/errors/responsevalidationerror.py @@ -0,0 +1,27 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +import httpx +from typing import Optional +from dataclasses import dataclass + +from comfydeploy.models.errors import ComfyDeployError + + +@dataclass(unsafe_hash=True) +class ResponseValidationError(ComfyDeployError): + """Error raised when there is a type mismatch between the response data and the expected Pydantic model.""" + + def __init__( + self, + message: str, + raw_response: httpx.Response, + cause: Exception, + body: Optional[str] = None, + ): + message = f"{message}: {cause}" + super().__init__(message, raw_response, body) + + @property + def cause(self): + """Normally the Pydantic ValidationError""" + return self.__cause__ diff --git a/src/comfydeploy/models/errors/sdkerror.py b/src/comfydeploy/models/errors/sdkerror.py index 03216cb..831e0c7 100644 --- a/src/comfydeploy/models/errors/sdkerror.py +++ b/src/comfydeploy/models/errors/sdkerror.py @@ -1,22 +1,40 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from dataclasses import dataclass -from typing import Optional import httpx +from typing import Optional +from dataclasses import dataclass + +from comfydeploy.models.errors import ComfyDeployError + +MAX_MESSAGE_LEN = 10_000 + + +@dataclass(unsafe_hash=True) +class SDKError(ComfyDeployError): + """The fallback error class if no more specific error class is matched.""" + + def __init__( + self, message: str, raw_response: httpx.Response, body: Optional[str] = None + ): + body_display = body or raw_response.text or '""' + if message: + message += ": " + message += f"Status {raw_response.status_code}" -@dataclass -class SDKError(Exception): - """Represents an error returned by the API.""" + headers = raw_response.headers + content_type = headers.get("content-type", '""') + if content_type != "application/json": + if " " in content_type: + content_type = f'"{content_type}"' + message += f" Content-Type {content_type}" - message: str - status_code: int = -1 - body: str = "" - raw_response: Optional[httpx.Response] = None + if len(body_display) > MAX_MESSAGE_LEN: + truncated = body_display[:MAX_MESSAGE_LEN] + remaining = len(body_display) - MAX_MESSAGE_LEN + body_display = f"{truncated}...and {remaining} more chars" - def __str__(self): - body = "" - if len(self.body) > 0: - body = f"\n{self.body}" + message += f". Body: {body_display}" + message = message.strip() - return f"{self.message}: Status {self.status_code}{body}" + super().__init__(message, raw_response, body) diff --git a/src/comfydeploy/models/operations/__init__.py b/src/comfydeploy/models/operations/__init__.py index 1d257d8..c6d4d77 100644 --- a/src/comfydeploy/models/operations/__init__.py +++ b/src/comfydeploy/models/operations/__init__.py @@ -1,22 +1,27 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from .cancel_run_run_run_id_cancel_post import ( - CancelRunRunRunIDCancelPostRequest, - CancelRunRunRunIDCancelPostRequestTypedDict, - CancelRunRunRunIDCancelPostResponse, - CancelRunRunRunIDCancelPostResponseTypedDict, -) -from .get_run_run_run_id_get import ( - GetRunRunRunIDGetRequest, - GetRunRunRunIDGetRequestTypedDict, - GetRunRunRunIDGetResponse, - GetRunRunRunIDGetResponseTypedDict, -) -from .queue_deployment_run_run_deployment_queue_post import ( - QueueDeploymentRunRunDeploymentQueuePostResponse, - QueueDeploymentRunRunDeploymentQueuePostResponseTypedDict, -) +from typing import TYPE_CHECKING +from importlib import import_module +import builtins +import sys +if TYPE_CHECKING: + from .cancel_run_run_run_id_cancel_post import ( + CancelRunRunRunIDCancelPostRequest, + CancelRunRunRunIDCancelPostRequestTypedDict, + CancelRunRunRunIDCancelPostResponse, + CancelRunRunRunIDCancelPostResponseTypedDict, + ) + from .get_run_run_run_id_get import ( + GetRunRunRunIDGetRequest, + GetRunRunRunIDGetRequestTypedDict, + GetRunRunRunIDGetResponse, + GetRunRunRunIDGetResponseTypedDict, + ) + from .queue_deployment_run_run_deployment_queue_post import ( + QueueDeploymentRunRunDeploymentQueuePostResponse, + QueueDeploymentRunRunDeploymentQueuePostResponseTypedDict, + ) __all__ = [ "CancelRunRunRunIDCancelPostRequest", @@ -30,3 +35,54 @@ "QueueDeploymentRunRunDeploymentQueuePostResponse", "QueueDeploymentRunRunDeploymentQueuePostResponseTypedDict", ] + +_dynamic_imports: dict[str, str] = { + "CancelRunRunRunIDCancelPostRequest": ".cancel_run_run_run_id_cancel_post", + "CancelRunRunRunIDCancelPostRequestTypedDict": ".cancel_run_run_run_id_cancel_post", + "CancelRunRunRunIDCancelPostResponse": ".cancel_run_run_run_id_cancel_post", + "CancelRunRunRunIDCancelPostResponseTypedDict": ".cancel_run_run_run_id_cancel_post", + "GetRunRunRunIDGetRequest": ".get_run_run_run_id_get", + "GetRunRunRunIDGetRequestTypedDict": ".get_run_run_run_id_get", + "GetRunRunRunIDGetResponse": ".get_run_run_run_id_get", + "GetRunRunRunIDGetResponseTypedDict": ".get_run_run_run_id_get", + "QueueDeploymentRunRunDeploymentQueuePostResponse": ".queue_deployment_run_run_deployment_queue_post", + "QueueDeploymentRunRunDeploymentQueuePostResponseTypedDict": ".queue_deployment_run_run_deployment_queue_post", +} + + +def dynamic_import(modname, retries=3): + for attempt in range(retries): + try: + return import_module(modname, __package__) + except KeyError: + # Clear any half-initialized module and retry + sys.modules.pop(modname, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + +def __getattr__(attr_name: str) -> object: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__} " + ) + + try: + module = dynamic_import(module_name) + result = getattr(module, attr_name) + return result + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = builtins.list(_dynamic_imports.keys()) + return builtins.sorted(lazy_attrs) diff --git a/src/comfydeploy/models/operations/cancel_run_run_run_id_cancel_post.py b/src/comfydeploy/models/operations/cancel_run_run_run_id_cancel_post.py index 2669ddc..df02c72 100644 --- a/src/comfydeploy/models/operations/cancel_run_run_run_id_cancel_post.py +++ b/src/comfydeploy/models/operations/cancel_run_run_run_id_cancel_post.py @@ -2,9 +2,10 @@ from __future__ import annotations from comfydeploy.models.components import httpmetadata as components_httpmetadata -from comfydeploy.types import BaseModel +from comfydeploy.types import BaseModel, UNSET_SENTINEL from comfydeploy.utils import FieldMetadata, PathParamMetadata import pydantic +from pydantic import model_serializer from typing import Any, Optional from typing_extensions import Annotated, NotRequired, TypedDict @@ -32,3 +33,19 @@ class CancelRunRunRunIDCancelPostResponse(BaseModel): any: Optional[Any] = None r"""Successful Response""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["any"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/src/comfydeploy/models/operations/get_run_run_run_id_get.py b/src/comfydeploy/models/operations/get_run_run_run_id_get.py index 3d4a981..70fa1c0 100644 --- a/src/comfydeploy/models/operations/get_run_run_run_id_get.py +++ b/src/comfydeploy/models/operations/get_run_run_run_id_get.py @@ -5,15 +5,17 @@ httpmetadata as components_httpmetadata, workflowrunmodel as components_workflowrunmodel, ) -from comfydeploy.types import BaseModel -from comfydeploy.utils import FieldMetadata, PathParamMetadata +from comfydeploy.types import BaseModel, UNSET_SENTINEL +from comfydeploy.utils import FieldMetadata, PathParamMetadata, QueryParamMetadata import pydantic +from pydantic import model_serializer from typing import Optional from typing_extensions import Annotated, NotRequired, TypedDict class GetRunRunRunIDGetRequestTypedDict(TypedDict): run_id: str + queue_position: NotRequired[bool] class GetRunRunRunIDGetRequest(BaseModel): @@ -21,6 +23,27 @@ class GetRunRunRunIDGetRequest(BaseModel): str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False)) ] + queue_position: Annotated[ + Optional[bool], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = False + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["queue_position"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m + class GetRunRunRunIDGetResponseTypedDict(TypedDict): http_meta: components_httpmetadata.HTTPMetadataTypedDict @@ -37,3 +60,19 @@ class GetRunRunRunIDGetResponse(BaseModel): workflow_run_model: Optional[components_workflowrunmodel.WorkflowRunModel] = None r"""Successful Response""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["WorkflowRunModel"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/src/comfydeploy/models/operations/queue_deployment_run_run_deployment_queue_post.py b/src/comfydeploy/models/operations/queue_deployment_run_run_deployment_queue_post.py index cbf1a7f..943668c 100644 --- a/src/comfydeploy/models/operations/queue_deployment_run_run_deployment_queue_post.py +++ b/src/comfydeploy/models/operations/queue_deployment_run_run_deployment_queue_post.py @@ -5,8 +5,9 @@ createrunresponse as components_createrunresponse, httpmetadata as components_httpmetadata, ) -from comfydeploy.types import BaseModel +from comfydeploy.types import BaseModel, UNSET_SENTINEL import pydantic +from pydantic import model_serializer from typing import Optional from typing_extensions import Annotated, NotRequired, TypedDict @@ -26,3 +27,19 @@ class QueueDeploymentRunRunDeploymentQueuePostResponse(BaseModel): create_run_response: Optional[components_createrunresponse.CreateRunResponse] = None r"""Successful Response""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["CreateRunResponse"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/src/comfydeploy/models/webhooks/__init__.py b/src/comfydeploy/models/webhooks/__init__.py deleted file mode 100644 index 065c657..0000000 --- a/src/comfydeploy/models/webhooks/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" - -from .run_update_request_body_webhook_post import ( - RunUpdateRequestBodyWebhookPostResponse, - RunUpdateRequestBodyWebhookPostResponseTypedDict, -) - - -__all__ = [ - "RunUpdateRequestBodyWebhookPostResponse", - "RunUpdateRequestBodyWebhookPostResponseTypedDict", -] diff --git a/src/comfydeploy/models/webhooks/run_update_request_body_webhook_post.py b/src/comfydeploy/models/webhooks/run_update_request_body_webhook_post.py deleted file mode 100644 index 1c3f155..0000000 --- a/src/comfydeploy/models/webhooks/run_update_request_body_webhook_post.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" - -from __future__ import annotations -from comfydeploy.models.components import ( - httpmetadata as components_httpmetadata, - workflowrunwebhookresponse as components_workflowrunwebhookresponse, -) -from comfydeploy.types import BaseModel -import pydantic -from typing import Optional -from typing_extensions import Annotated, NotRequired, TypedDict - - -class RunUpdateRequestBodyWebhookPostResponseTypedDict(TypedDict): - http_meta: components_httpmetadata.HTTPMetadataTypedDict - workflow_run_webhook_response: NotRequired[ - components_workflowrunwebhookresponse.WorkflowRunWebhookResponseTypedDict - ] - r"""Successful Response""" - - -class RunUpdateRequestBodyWebhookPostResponse(BaseModel): - http_meta: Annotated[ - Optional[components_httpmetadata.HTTPMetadata], pydantic.Field(exclude=True) - ] = None - - workflow_run_webhook_response: Optional[ - components_workflowrunwebhookresponse.WorkflowRunWebhookResponse - ] = None - r"""Successful Response""" diff --git a/src/comfydeploy/run.py b/src/comfydeploy/run.py index 9a1facf..216378c 100644 --- a/src/comfydeploy/run.py +++ b/src/comfydeploy/run.py @@ -7,24 +7,28 @@ from comfydeploy.deployment import Deployment from comfydeploy.models import components, errors, operations from comfydeploy.types import OptionalNullable, UNSET +from comfydeploy.utils.unmarshal_json_response import unmarshal_json_response from typing import Any, Mapping, Optional class Run(BaseSDK): deployment: Deployment - def __init__(self, sdk_config: SDKConfiguration) -> None: - BaseSDK.__init__(self, sdk_config) + def __init__( + self, sdk_config: SDKConfiguration, parent_ref: Optional[object] = None + ) -> None: + BaseSDK.__init__(self, sdk_config, parent_ref=parent_ref) self.sdk_configuration = sdk_config self._init_sdks() def _init_sdks(self): - self.deployment = Deployment(self.sdk_configuration) + self.deployment = Deployment(self.sdk_configuration, parent_ref=self.parent_ref) def get( self, *, run_id: str, + queue_position: Optional[bool] = False, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -33,6 +37,7 @@ def get( r"""Get Run :param run_id: + :param queue_position: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -50,6 +55,7 @@ def get( request = operations.GetRunRunRunIDGetRequest( run_id=run_id, + queue_position=queue_position, ) req = self._build_request( @@ -65,6 +71,7 @@ def get( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -78,9 +85,10 @@ def get( http_res = self.do_request( hook_ctx=HookContext( + config=self.sdk_configuration, base_url=base_url or "", operation_id="get_run_run__run_id__get", - oauth2_scopes=[], + oauth2_scopes=None, security_source=self.sdk_configuration.security, ), request=req, @@ -91,40 +99,30 @@ def get( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return operations.GetRunRunRunIDGetResponse( - workflow_run_model=utils.unmarshal_json( - http_res.text, Optional[components.WorkflowRunModel] + workflow_run_model=unmarshal_json_response( + Optional[components.WorkflowRunModel], http_res ), http_meta=components.HTTPMetadata(request=req, response=http_res), ) if utils.match_response(http_res, "422", "application/json"): - response_data = utils.unmarshal_json( - http_res.text, errors.HTTPValidationErrorData + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res ) - raise errors.HTTPValidationError(data=response_data) + raise errors.HTTPValidationError(response_data, http_res) if utils.match_response(http_res, "4XX", "*"): http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) if utils.match_response(http_res, "5XX", "*"): http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) - content_type = http_res.headers.get("Content-Type") - http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", - http_res.status_code, - http_res_text, - http_res, - ) + raise errors.SDKError("Unexpected response received", http_res) async def get_async( self, *, run_id: str, + queue_position: Optional[bool] = False, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -133,6 +131,7 @@ async def get_async( r"""Get Run :param run_id: + :param queue_position: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -150,6 +149,7 @@ async def get_async( request = operations.GetRunRunRunIDGetRequest( run_id=run_id, + queue_position=queue_position, ) req = self._build_request_async( @@ -165,6 +165,7 @@ async def get_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -178,9 +179,10 @@ async def get_async( http_res = await self.do_request_async( hook_ctx=HookContext( + config=self.sdk_configuration, base_url=base_url or "", operation_id="get_run_run__run_id__get", - oauth2_scopes=[], + oauth2_scopes=None, security_source=self.sdk_configuration.security, ), request=req, @@ -191,35 +193,24 @@ async def get_async( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return operations.GetRunRunRunIDGetResponse( - workflow_run_model=utils.unmarshal_json( - http_res.text, Optional[components.WorkflowRunModel] + workflow_run_model=unmarshal_json_response( + Optional[components.WorkflowRunModel], http_res ), http_meta=components.HTTPMetadata(request=req, response=http_res), ) if utils.match_response(http_res, "422", "application/json"): - response_data = utils.unmarshal_json( - http_res.text, errors.HTTPValidationErrorData + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res ) - raise errors.HTTPValidationError(data=response_data) + raise errors.HTTPValidationError(response_data, http_res) if utils.match_response(http_res, "4XX", "*"): http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) if utils.match_response(http_res, "5XX", "*"): http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) - content_type = http_res.headers.get("Content-Type") - http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", - http_res.status_code, - http_res_text, - http_res, - ) + raise errors.SDKError("Unexpected response received", http_res) def cancel( self, @@ -265,6 +256,7 @@ def cancel( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -278,9 +270,10 @@ def cancel( http_res = self.do_request( hook_ctx=HookContext( + config=self.sdk_configuration, base_url=base_url or "", operation_id="cancel_run_run__run_id__cancel_post", - oauth2_scopes=[], + oauth2_scopes=None, security_source=self.sdk_configuration.security, ), request=req, @@ -291,33 +284,22 @@ def cancel( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return operations.CancelRunRunRunIDCancelPostResponse( - any=utils.unmarshal_json(http_res.text, Optional[Any]), + any=unmarshal_json_response(Optional[Any], http_res), http_meta=components.HTTPMetadata(request=req, response=http_res), ) if utils.match_response(http_res, "422", "application/json"): - response_data = utils.unmarshal_json( - http_res.text, errors.HTTPValidationErrorData + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res ) - raise errors.HTTPValidationError(data=response_data) + raise errors.HTTPValidationError(response_data, http_res) if utils.match_response(http_res, "4XX", "*"): http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) if utils.match_response(http_res, "5XX", "*"): http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) - content_type = http_res.headers.get("Content-Type") - http_res_text = utils.stream_to_text(http_res) - raise errors.SDKError( - f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", - http_res.status_code, - http_res_text, - http_res, - ) + raise errors.SDKError("Unexpected response received", http_res) async def cancel_async( self, @@ -363,6 +345,7 @@ async def cancel_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -376,9 +359,10 @@ async def cancel_async( http_res = await self.do_request_async( hook_ctx=HookContext( + config=self.sdk_configuration, base_url=base_url or "", operation_id="cancel_run_run__run_id__cancel_post", - oauth2_scopes=[], + oauth2_scopes=None, security_source=self.sdk_configuration.security, ), request=req, @@ -389,30 +373,19 @@ async def cancel_async( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return operations.CancelRunRunRunIDCancelPostResponse( - any=utils.unmarshal_json(http_res.text, Optional[Any]), + any=unmarshal_json_response(Optional[Any], http_res), http_meta=components.HTTPMetadata(request=req, response=http_res), ) if utils.match_response(http_res, "422", "application/json"): - response_data = utils.unmarshal_json( - http_res.text, errors.HTTPValidationErrorData + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res ) - raise errors.HTTPValidationError(data=response_data) + raise errors.HTTPValidationError(response_data, http_res) if utils.match_response(http_res, "4XX", "*"): http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) if utils.match_response(http_res, "5XX", "*"): http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - "API error occurred", http_res.status_code, http_res_text, http_res - ) + raise errors.SDKError("API error occurred", http_res, http_res_text) - content_type = http_res.headers.get("Content-Type") - http_res_text = await utils.stream_to_text_async(http_res) - raise errors.SDKError( - f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", - http_res.status_code, - http_res_text, - http_res, - ) + raise errors.SDKError("Unexpected response received", http_res) diff --git a/src/comfydeploy/sdk.py b/src/comfydeploy/sdk.py index 577c61d..8dc5c3a 100644 --- a/src/comfydeploy/sdk.py +++ b/src/comfydeploy/sdk.py @@ -8,12 +8,16 @@ from comfydeploy import utils from comfydeploy._hooks import SDKHooks from comfydeploy.models import components -from comfydeploy.run import Run from comfydeploy.types import OptionalNullable, UNSET import httpx -from typing import Any, Callable, Dict, Optional, Union, cast +import importlib +import sys +from typing import Any, Callable, Dict, Optional, TYPE_CHECKING, Union, cast import weakref +if TYPE_CHECKING: + from comfydeploy.run import Run + class ComfyDeploy(BaseSDK): r"""ComfyDeploy API: @@ -34,7 +38,10 @@ class ComfyDeploy(BaseSDK): """ - run: Run + run: "Run" + _sub_sdk_map = { + "run": ("comfydeploy.run", "Run"), + } def __init__( self, @@ -61,7 +68,7 @@ def __init__( """ client_supplied = True if client is None: - client = httpx.Client() + client = httpx.Client(follow_redirects=True) client_supplied = False assert issubclass( @@ -70,7 +77,7 @@ def __init__( async_client_supplied = True if async_client is None: - async_client = httpx.AsyncClient() + async_client = httpx.AsyncClient(follow_redirects=True) async_client_supplied = False if debug_logger is None: @@ -105,10 +112,14 @@ def __init__( timeout_ms=timeout_ms, debug_logger=debug_logger, ), + parent_ref=self, ) hooks = SDKHooks() + # pylint: disable=protected-access + self.sdk_configuration.__dict__["_hooks"] = hooks + current_server_url, *_ = self.sdk_configuration.get_server_details() server_url, self.sdk_configuration.client = hooks.sdk_init( current_server_url, client @@ -116,9 +127,6 @@ def __init__( if current_server_url != server_url: self.sdk_configuration.server_url = server_url - # pylint: disable=protected-access - self.sdk_configuration.__dict__["_hooks"] = hooks - weakref.finalize( self, close_clients, @@ -129,10 +137,43 @@ def __init__( self.sdk_configuration.async_client_supplied, ) - self._init_sdks() + def dynamic_import(self, modname, retries=3): + for attempt in range(retries): + try: + return importlib.import_module(modname) + except KeyError: + # Clear any half-initialized module and retry + sys.modules.pop(modname, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + def __getattr__(self, name: str): + if name in self._sub_sdk_map: + module_path, class_name = self._sub_sdk_map[name] + try: + module = self.dynamic_import(module_path) + klass = getattr(module, class_name) + instance = klass(self.sdk_configuration, parent_ref=self) + setattr(self, name, instance) + return instance + except ImportError as e: + raise AttributeError( + f"Failed to import module {module_path} for attribute {name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to find class {class_name} in module {module_path} for attribute {name}: {e}" + ) from e + + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{name}'" + ) - def _init_sdks(self): - self.run = Run(self.sdk_configuration) + def __dir__(self): + default_attrs = list(super().__dir__()) + lazy_attrs = list(self._sub_sdk_map.keys()) + return sorted(list(set(default_attrs + lazy_attrs))) def __enter__(self): return self diff --git a/src/comfydeploy/sdkconfiguration.py b/src/comfydeploy/sdkconfiguration.py index 7f3cf81..6f28b78 100644 --- a/src/comfydeploy/sdkconfiguration.py +++ b/src/comfydeploy/sdkconfiguration.py @@ -1,6 +1,5 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from ._hooks import SDKHooks from ._version import ( __gen_version__, __openapi_doc_version__, @@ -47,9 +46,6 @@ class SDKConfiguration: retry_config: OptionalNullable[RetryConfig] = Field(default_factory=lambda: UNSET) timeout_ms: Optional[int] = None - def __post_init__(self): - self._hooks = SDKHooks() - def get_server_details(self) -> Tuple[str, Dict[str, str]]: if self.server_url is not None and self.server_url: return remove_suffix(self.server_url, "/"), {} @@ -57,6 +53,3 @@ def get_server_details(self) -> Tuple[str, Dict[str, str]]: self.server_idx = 0 return SERVERS[self.server_idx], {} - - def get_hooks(self) -> SDKHooks: - return self._hooks diff --git a/src/comfydeploy/types/basemodel.py b/src/comfydeploy/types/basemodel.py index a6187ef..a9a640a 100644 --- a/src/comfydeploy/types/basemodel.py +++ b/src/comfydeploy/types/basemodel.py @@ -2,7 +2,8 @@ from pydantic import ConfigDict, model_serializer from pydantic import BaseModel as PydanticBaseModel -from typing import TYPE_CHECKING, Literal, Optional, TypeVar, Union, NewType +from pydantic_core import core_schema +from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union from typing_extensions import TypeAliasType, TypeAlias @@ -35,5 +36,42 @@ def __bool__(self) -> Literal[False]: "OptionalNullable", Union[Optional[Nullable[T]], Unset], type_params=(T,) ) -UnrecognizedInt = NewType("UnrecognizedInt", int) -UnrecognizedStr = NewType("UnrecognizedStr", str) + +class UnrecognizedStr(str): + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema: + # Make UnrecognizedStr only work in lax mode, not strict mode + # This makes it a "fallback" option when more specific types (like Literals) don't match + def validate_lax(v: Any) -> 'UnrecognizedStr': + if isinstance(v, cls): + return v + return cls(str(v)) + + # Use lax_or_strict_schema where strict always fails + # This forces Pydantic to prefer other union members in strict mode + # and only fall back to UnrecognizedStr in lax mode + return core_schema.lax_or_strict_schema( + lax_schema=core_schema.chain_schema([ + core_schema.str_schema(), + core_schema.no_info_plain_validator_function(validate_lax) + ]), + strict_schema=core_schema.none_schema(), # Always fails in strict mode + ) + + +class UnrecognizedInt(int): + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema: + # Make UnrecognizedInt only work in lax mode, not strict mode + # This makes it a "fallback" option when more specific types (like Literals) don't match + def validate_lax(v: Any) -> 'UnrecognizedInt': + if isinstance(v, cls): + return v + return cls(int(v)) + return core_schema.lax_or_strict_schema( + lax_schema=core_schema.chain_schema([ + core_schema.int_schema(), + core_schema.no_info_plain_validator_function(validate_lax) + ]), + strict_schema=core_schema.none_schema(), # Always fails in strict mode + ) diff --git a/src/comfydeploy/utils/__init__.py b/src/comfydeploy/utils/__init__.py index 3cded8f..c906e1e 100644 --- a/src/comfydeploy/utils/__init__.py +++ b/src/comfydeploy/utils/__init__.py @@ -1,50 +1,68 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from .annotations import get_discriminator -from .enums import OpenEnumMeta -from .headers import get_headers, get_response_headers -from .metadata import ( - FieldMetadata, - find_metadata, - FormMetadata, - HeaderMetadata, - MultipartFormMetadata, - PathParamMetadata, - QueryParamMetadata, - RequestMetadata, - SecurityMetadata, -) -from .queryparams import get_query_params -from .retries import BackoffStrategy, Retries, retry, retry_async, RetryConfig -from .requestbodies import serialize_request_body, SerializedRequestBody -from .security import get_security -from .serializers import ( - get_pydantic_model, - marshal_json, - unmarshal, - unmarshal_json, - serialize_decimal, - serialize_float, - serialize_int, - stream_to_text, - stream_to_text_async, - stream_to_bytes, - stream_to_bytes_async, - validate_const, - validate_decimal, - validate_float, - validate_int, - validate_open_enum, -) -from .url import generate_url, template_url, remove_suffix -from .values import ( - get_global_from_env, - match_content_type, - match_status_codes, - match_response, - cast_partial, -) -from .logger import Logger, get_body_content, get_default_logger +from typing import TYPE_CHECKING, Callable, TypeVar +from importlib import import_module +import asyncio +import builtins +import sys + +_T = TypeVar("_T") + + +async def run_sync_in_thread(func: Callable[..., _T], *args) -> _T: + """Run a synchronous function in a thread pool to avoid blocking the event loop.""" + if sys.version_info >= (3, 9): + return await asyncio.to_thread(func, *args) + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, func, *args) + + +if TYPE_CHECKING: + from .annotations import get_discriminator + from .datetimes import parse_datetime + from .enums import OpenEnumMeta + from .headers import get_headers, get_response_headers + from .metadata import ( + FieldMetadata, + find_metadata, + FormMetadata, + HeaderMetadata, + MultipartFormMetadata, + PathParamMetadata, + QueryParamMetadata, + RequestMetadata, + SecurityMetadata, + ) + from .queryparams import get_query_params + from .retries import BackoffStrategy, Retries, retry, retry_async, RetryConfig + from .requestbodies import serialize_request_body, SerializedRequestBody + from .security import get_security + from .serializers import ( + get_pydantic_model, + marshal_json, + unmarshal, + unmarshal_json, + serialize_decimal, + serialize_float, + serialize_int, + stream_to_text, + stream_to_text_async, + stream_to_bytes, + stream_to_bytes_async, + validate_const, + validate_decimal, + validate_float, + validate_int, + ) + from .url import generate_url, template_url, remove_suffix + from .values import ( + get_global_from_env, + match_content_type, + match_status_codes, + match_response, + cast_partial, + ) + from .logger import Logger, get_body_content, get_default_logger __all__ = [ "BackoffStrategy", @@ -55,6 +73,7 @@ "get_body_content", "get_default_logger", "get_discriminator", + "parse_datetime", "get_global_from_env", "get_headers", "get_pydantic_model", @@ -94,6 +113,94 @@ "validate_const", "validate_float", "validate_int", - "validate_open_enum", "cast_partial", ] + +_dynamic_imports: dict[str, str] = { + "BackoffStrategy": ".retries", + "FieldMetadata": ".metadata", + "find_metadata": ".metadata", + "FormMetadata": ".metadata", + "generate_url": ".url", + "get_body_content": ".logger", + "get_default_logger": ".logger", + "get_discriminator": ".annotations", + "parse_datetime": ".datetimes", + "get_global_from_env": ".values", + "get_headers": ".headers", + "get_pydantic_model": ".serializers", + "get_query_params": ".queryparams", + "get_response_headers": ".headers", + "get_security": ".security", + "HeaderMetadata": ".metadata", + "Logger": ".logger", + "marshal_json": ".serializers", + "match_content_type": ".values", + "match_status_codes": ".values", + "match_response": ".values", + "MultipartFormMetadata": ".metadata", + "OpenEnumMeta": ".enums", + "PathParamMetadata": ".metadata", + "QueryParamMetadata": ".metadata", + "remove_suffix": ".url", + "Retries": ".retries", + "retry": ".retries", + "retry_async": ".retries", + "RetryConfig": ".retries", + "RequestMetadata": ".metadata", + "SecurityMetadata": ".metadata", + "serialize_decimal": ".serializers", + "serialize_float": ".serializers", + "serialize_int": ".serializers", + "serialize_request_body": ".requestbodies", + "SerializedRequestBody": ".requestbodies", + "stream_to_text": ".serializers", + "stream_to_text_async": ".serializers", + "stream_to_bytes": ".serializers", + "stream_to_bytes_async": ".serializers", + "template_url": ".url", + "unmarshal": ".serializers", + "unmarshal_json": ".serializers", + "validate_decimal": ".serializers", + "validate_const": ".serializers", + "validate_float": ".serializers", + "validate_int": ".serializers", + "cast_partial": ".values", +} + + +def dynamic_import(modname, retries=3): + for attempt in range(retries): + try: + return import_module(modname, __package__) + except KeyError: + # Clear any half-initialized module and retry + sys.modules.pop(modname, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + +def __getattr__(attr_name: str) -> object: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"no {attr_name} found in _dynamic_imports, module name -> {__name__} " + ) + + try: + module = dynamic_import(module_name) + return getattr(module, attr_name) + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = builtins.list(_dynamic_imports.keys()) + return builtins.sorted(lazy_attrs) diff --git a/src/comfydeploy/utils/annotations.py b/src/comfydeploy/utils/annotations.py index 387874e..12e0aa4 100644 --- a/src/comfydeploy/utils/annotations.py +++ b/src/comfydeploy/utils/annotations.py @@ -3,6 +3,7 @@ from enum import Enum from typing import Any, Optional + def get_discriminator(model: Any, fieldname: str, key: str) -> str: """ Recursively search for the discriminator attribute in a model. @@ -25,31 +26,54 @@ def get_field_discriminator(field: Any) -> Optional[str]: if isinstance(field, dict): if key in field: - return f'{field[key]}' + return f"{field[key]}" if hasattr(field, fieldname): attr = getattr(field, fieldname) if isinstance(attr, Enum): - return f'{attr.value}' - return f'{attr}' + return f"{attr.value}" + return f"{attr}" if hasattr(field, upper_fieldname): attr = getattr(field, upper_fieldname) if isinstance(attr, Enum): - return f'{attr.value}' - return f'{attr}' + return f"{attr.value}" + return f"{attr}" return None + def search_nested_discriminator(obj: Any) -> Optional[str]: + """Recursively search for discriminator in nested structures.""" + # First try direct field lookup + discriminator = get_field_discriminator(obj) + if discriminator is not None: + return discriminator + + # If it's a dict, search in nested values + if isinstance(obj, dict): + for value in obj.values(): + if isinstance(value, list): + # Search in list items + for item in value: + nested_discriminator = search_nested_discriminator(item) + if nested_discriminator is not None: + return nested_discriminator + elif isinstance(value, dict): + # Search in nested dict + nested_discriminator = search_nested_discriminator(value) + if nested_discriminator is not None: + return nested_discriminator + + return None if isinstance(model, list): for field in model: - discriminator = get_field_discriminator(field) + discriminator = search_nested_discriminator(field) if discriminator is not None: return discriminator - discriminator = get_field_discriminator(model) + discriminator = search_nested_discriminator(model) if discriminator is not None: return discriminator - raise ValueError(f'Could not find discriminator field {fieldname} in {model}') + raise ValueError(f"Could not find discriminator field {fieldname} in {model}") diff --git a/src/comfydeploy/utils/datetimes.py b/src/comfydeploy/utils/datetimes.py new file mode 100644 index 0000000..a6c52cd --- /dev/null +++ b/src/comfydeploy/utils/datetimes.py @@ -0,0 +1,23 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from datetime import datetime +import sys + + +def parse_datetime(datetime_string: str) -> datetime: + """ + Convert a RFC 3339 / ISO 8601 formatted string into a datetime object. + Python versions 3.11 and later support parsing RFC 3339 directly with + datetime.fromisoformat(), but for earlier versions, this function + encapsulates the necessary extra logic. + """ + # Python 3.11 and later can parse RFC 3339 directly + if sys.version_info >= (3, 11): + return datetime.fromisoformat(datetime_string) + + # For Python 3.10 and earlier, a common ValueError is trailing 'Z' suffix, + # so fix that upfront. + if datetime_string.endswith("Z"): + datetime_string = datetime_string[:-1] + "+00:00" + + return datetime.fromisoformat(datetime_string) diff --git a/src/comfydeploy/utils/enums.py b/src/comfydeploy/utils/enums.py index c650b10..3324e1b 100644 --- a/src/comfydeploy/utils/enums.py +++ b/src/comfydeploy/utils/enums.py @@ -1,34 +1,134 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" import enum +import sys +from typing import Any + +from pydantic_core import core_schema class OpenEnumMeta(enum.EnumMeta): - def __call__( - cls, value, names=None, *, module=None, qualname=None, type=None, start=1 - ): - # The `type` kwarg also happens to be a built-in that pylint flags as - # redeclared. Safe to ignore this lint rule with this scope. - # pylint: disable=redefined-builtin - - if names is not None: - return super().__call__( - value, - names=names, - module=module, - qualname=qualname, - type=type, - start=start, + # The __call__ method `boundary` kwarg was added in 3.11 and must be present + # for pyright. Refer also: https://github.com/pylint-dev/pylint/issues/9622 + # pylint: disable=unexpected-keyword-arg + # The __call__ method `values` varg must be named for pyright. + # pylint: disable=keyword-arg-before-vararg + + if sys.version_info >= (3, 11): + def __call__( + cls, value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None + ): + # The `type` kwarg also happens to be a built-in that pylint flags as + # redeclared. Safe to ignore this lint rule with this scope. + # pylint: disable=redefined-builtin + + if names is not None: + return super().__call__( + value, + names=names, + *values, + module=module, + qualname=qualname, + type=type, + start=start, + boundary=boundary, + ) + + try: + return super().__call__( + value, + names=names, # pyright: ignore[reportArgumentType] + *values, + module=module, + qualname=qualname, + type=type, + start=start, + boundary=boundary, + ) + except ValueError: + return value + else: + def __call__( + cls, value, names=None, *, module=None, qualname=None, type=None, start=1 + ): + # The `type` kwarg also happens to be a built-in that pylint flags as + # redeclared. Safe to ignore this lint rule with this scope. + # pylint: disable=redefined-builtin + + if names is not None: + return super().__call__( + value, + names=names, + module=module, + qualname=qualname, + type=type, + start=start, + ) + + try: + return super().__call__( + value, + names=names, # pyright: ignore[reportArgumentType] + module=module, + qualname=qualname, + type=type, + start=start, + ) + except ValueError: + return value + + def __new__(mcs, name, bases, namespace, **kwargs): + cls = super().__new__(mcs, name, bases, namespace, **kwargs) + + # Add __get_pydantic_core_schema__ to make open enums work correctly + # in union discrimination. In strict mode (used by Pydantic for unions), + # only known enum values match. In lax mode, unknown values are accepted. + def __get_pydantic_core_schema__( + cls_inner: Any, _source_type: Any, _handler: Any + ) -> core_schema.CoreSchema: + # Create a validator that only accepts known enum values (for strict mode) + def validate_strict(v: Any) -> Any: + if isinstance(v, cls_inner): + return v + # Use the parent EnumMeta's __call__ which raises ValueError for unknown values + return enum.EnumMeta.__call__(cls_inner, v) + + # Create a lax validator that accepts unknown values + def validate_lax(v: Any) -> Any: + if isinstance(v, cls_inner): + return v + try: + return enum.EnumMeta.__call__(cls_inner, v) + except ValueError: + # Return the raw value for unknown enum values + return v + + # Determine the base type schema (str or int) + is_int_enum = False + for base in cls_inner.__mro__: + if base is int: + is_int_enum = True + break + if base is str: + break + + base_schema = ( + core_schema.int_schema() + if is_int_enum + else core_schema.str_schema() ) - try: - return super().__call__( - value, - names=names, # pyright: ignore[reportArgumentType] - module=module, - qualname=qualname, - type=type, - start=start, + # Use lax_or_strict_schema: + # - strict mode: only known enum values match (raises ValueError for unknown) + # - lax mode: accept any value, return enum member or raw value + return core_schema.lax_or_strict_schema( + lax_schema=core_schema.chain_schema( + [base_schema, core_schema.no_info_plain_validator_function(validate_lax)] + ), + strict_schema=core_schema.chain_schema( + [base_schema, core_schema.no_info_plain_validator_function(validate_strict)] + ), ) - except ValueError: - return value + + setattr(cls, "__get_pydantic_core_schema__", classmethod(__get_pydantic_core_schema__)) + return cls diff --git a/src/comfydeploy/utils/eventstreaming.py b/src/comfydeploy/utils/eventstreaming.py index 74a63f7..0969899 100644 --- a/src/comfydeploy/utils/eventstreaming.py +++ b/src/comfydeploy/utils/eventstreaming.py @@ -17,6 +17,9 @@ class EventStream(Generic[T]): + # Holds a reference to the SDK client to avoid it being garbage collected + # and cause termination of the underlying httpx client. + client_ref: Optional[object] response: httpx.Response generator: Generator[T, None, None] @@ -25,9 +28,11 @@ def __init__( response: httpx.Response, decoder: Callable[[str], T], sentinel: Optional[str] = None, + client_ref: Optional[object] = None, ): self.response = response self.generator = stream_events(response, decoder, sentinel) + self.client_ref = client_ref def __iter__(self): return self @@ -43,6 +48,9 @@ def __exit__(self, exc_type, exc_val, exc_tb): class EventStreamAsync(Generic[T]): + # Holds a reference to the SDK client to avoid it being garbage collected + # and cause termination of the underlying httpx client. + client_ref: Optional[object] response: httpx.Response generator: AsyncGenerator[T, None] @@ -51,9 +59,11 @@ def __init__( response: httpx.Response, decoder: Callable[[str], T], sentinel: Optional[str] = None, + client_ref: Optional[object] = None, ): self.response = response self.generator = stream_events_async(response, decoder, sentinel) + self.client_ref = client_ref def __aiter__(self): return self diff --git a/src/comfydeploy/utils/forms.py b/src/comfydeploy/utils/forms.py index 0472aba..f961e76 100644 --- a/src/comfydeploy/utils/forms.py +++ b/src/comfydeploy/utils/forms.py @@ -86,11 +86,39 @@ def _populate_form( return form +def _extract_file_properties(file_obj: Any) -> Tuple[str, Any, Any]: + """Extract file name, content, and content type from a file object.""" + file_fields: Dict[str, FieldInfo] = file_obj.__class__.model_fields + + file_name = "" + content = None + content_type = None + + for file_field_name in file_fields: + file_field = file_fields[file_field_name] + + file_metadata = find_field_metadata(file_field, MultipartFormMetadata) + if file_metadata is None: + continue + + if file_metadata.content: + content = getattr(file_obj, file_field_name, None) + elif file_field_name == "content_type": + content_type = getattr(file_obj, file_field_name, None) + else: + file_name = getattr(file_obj, file_field_name) + + if file_name == "" or content is None: + raise ValueError("invalid multipart/form-data file") + + return file_name, content, content_type + + def serialize_multipart_form( media_type: str, request: Any -) -> Tuple[str, Dict[str, Any], Dict[str, Any]]: +) -> Tuple[str, Dict[str, Any], List[Tuple[str, Any]]]: form: Dict[str, Any] = {} - files: Dict[str, Any] = {} + files: List[Tuple[str, Any]] = [] if not isinstance(request, BaseModel): raise TypeError("invalid request body type") @@ -112,38 +140,41 @@ def serialize_multipart_form( f_name = field.alias if field.alias else name if field_metadata.file: - file_fields: Dict[str, FieldInfo] = val.__class__.model_fields - - file_name = "" - content = None - content_type = None + if isinstance(val, List): + # Handle array of files + array_field_name = f_name + "[]" + for file_obj in val: + if not _is_set(file_obj): + continue - for file_field_name in file_fields: - file_field = file_fields[file_field_name] + file_name, content, content_type = _extract_file_properties( + file_obj + ) - file_metadata = find_field_metadata(file_field, MultipartFormMetadata) - if file_metadata is None: - continue + if content_type is not None: + files.append( + (array_field_name, (file_name, content, content_type)) + ) + else: + files.append((array_field_name, (file_name, content))) + else: + # Handle single file + file_name, content, content_type = _extract_file_properties(val) - if file_metadata.content: - content = getattr(val, file_field_name, None) - elif file_field_name == "content_type": - content_type = getattr(val, file_field_name, None) + if content_type is not None: + files.append((f_name, (file_name, content, content_type))) else: - file_name = getattr(val, file_field_name) - - if file_name == "" or content is None: - raise ValueError("invalid multipart/form-data file") - - if content_type is not None: - files[f_name] = (file_name, content, content_type) - else: - files[f_name] = (file_name, content) + files.append((f_name, (file_name, content))) elif field_metadata.json: - files[f_name] = ( - None, - marshal_json(val, request_field_types[name]), - "application/json", + files.append( + ( + f_name, + ( + None, + marshal_json(val, request_field_types[name]), + "application/json", + ), + ) ) else: if isinstance(val, List): @@ -154,7 +185,8 @@ def serialize_multipart_form( continue values.append(_val_to_string(value)) - form[f_name + "[]"] = values + array_field_name = f_name + "[]" + form[array_field_name] = values else: form[f_name] = _val_to_string(val) return media_type, form, files diff --git a/src/comfydeploy/utils/queryparams.py b/src/comfydeploy/utils/queryparams.py index 37a6e7f..c04e0db 100644 --- a/src/comfydeploy/utils/queryparams.py +++ b/src/comfydeploy/utils/queryparams.py @@ -27,12 +27,13 @@ def get_query_params( query_params: Any, gbls: Optional[Any] = None, + allow_empty_value: Optional[List[str]] = None, ) -> Dict[str, List[str]]: params: Dict[str, List[str]] = {} - globals_already_populated = _populate_query_params(query_params, gbls, params, []) + globals_already_populated = _populate_query_params(query_params, gbls, params, [], allow_empty_value) if _is_set(gbls): - _populate_query_params(gbls, None, params, globals_already_populated) + _populate_query_params(gbls, None, params, globals_already_populated, allow_empty_value) return params @@ -42,6 +43,7 @@ def _populate_query_params( gbls: Any, query_param_values: Dict[str, List[str]], skip_fields: List[str], + allow_empty_value: Optional[List[str]] = None, ) -> List[str]: globals_already_populated: List[str] = [] @@ -69,6 +71,16 @@ def _populate_query_params( globals_already_populated.append(name) f_name = field.alias if field.alias is not None else name + + allow_empty_set = set(allow_empty_value or []) + should_include_empty = f_name in allow_empty_set and ( + value is None or value == [] or value == "" + ) + + if should_include_empty: + query_param_values[f_name] = [""] + continue + serialization = metadata.serialization if serialization is not None: serialized_parms = _get_serialized_params( diff --git a/src/comfydeploy/utils/requestbodies.py b/src/comfydeploy/utils/requestbodies.py index d5240dd..1de32b6 100644 --- a/src/comfydeploy/utils/requestbodies.py +++ b/src/comfydeploy/utils/requestbodies.py @@ -44,15 +44,15 @@ def serialize_request_body( serialized_request_body = SerializedRequestBody(media_type) - if re.match(r"(application|text)\/.*?\+*json.*", media_type) is not None: + if re.match(r"^(application|text)\/([^+]+\+)*json.*", media_type) is not None: serialized_request_body.content = marshal_json(request_body, request_body_type) - elif re.match(r"multipart\/.*", media_type) is not None: + elif re.match(r"^multipart\/.*", media_type) is not None: ( serialized_request_body.media_type, serialized_request_body.data, serialized_request_body.files, ) = serialize_multipart_form(media_type, request_body) - elif re.match(r"application\/x-www-form-urlencoded.*", media_type) is not None: + elif re.match(r"^application\/x-www-form-urlencoded.*", media_type) is not None: serialized_request_body.data = serialize_form_data(request_body) elif isinstance(request_body, (bytes, bytearray, io.BytesIO, io.BufferedReader)): serialized_request_body.content = request_body diff --git a/src/comfydeploy/utils/retries.py b/src/comfydeploy/utils/retries.py index 4d60867..88a91b1 100644 --- a/src/comfydeploy/utils/retries.py +++ b/src/comfydeploy/utils/retries.py @@ -3,7 +3,9 @@ import asyncio import random import time -from typing import List +from datetime import datetime +from email.utils import parsedate_to_datetime +from typing import List, Optional import httpx @@ -51,9 +53,11 @@ def __init__(self, config: RetryConfig, status_codes: List[str]): class TemporaryError(Exception): response: httpx.Response + retry_after: Optional[int] def __init__(self, response: httpx.Response): self.response = response + self.retry_after = _parse_retry_after_header(response) class PermanentError(Exception): @@ -63,6 +67,62 @@ def __init__(self, inner: Exception): self.inner = inner +def _parse_retry_after_header(response: httpx.Response) -> Optional[int]: + """Parse Retry-After header from response. + + Returns: + Retry interval in milliseconds, or None if header is missing or invalid. + """ + retry_after_header = response.headers.get("retry-after") + if not retry_after_header: + return None + + try: + seconds = float(retry_after_header) + return round(seconds * 1000) + except ValueError: + pass + + try: + retry_date = parsedate_to_datetime(retry_after_header) + delta = (retry_date - datetime.now(retry_date.tzinfo)).total_seconds() + return round(max(0, delta) * 1000) + except (ValueError, TypeError): + pass + + return None + + +def _get_sleep_interval( + exception: Exception, + initial_interval: int, + max_interval: int, + exponent: float, + retries: int, +) -> float: + """Get sleep interval for retry with exponential backoff. + + Args: + exception: The exception that triggered the retry. + initial_interval: Initial retry interval in milliseconds. + max_interval: Maximum retry interval in milliseconds. + exponent: Base for exponential backoff calculation. + retries: Current retry attempt count. + + Returns: + Sleep interval in seconds. + """ + if ( + isinstance(exception, TemporaryError) + and exception.retry_after is not None + and exception.retry_after > 0 + ): + return exception.retry_after / 1000 + + sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) + return min(sleep, max_interval / 1000) + + def retry(func, retries: Retries): if retries.config.strategy == "backoff": @@ -183,8 +243,10 @@ def retry_with_backoff( return exception.response raise - sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) - sleep = min(sleep, max_interval / 1000) + + sleep = _get_sleep_interval( + exception, initial_interval, max_interval, exponent, retries + ) time.sleep(sleep) retries += 1 @@ -211,7 +273,9 @@ async def retry_with_backoff_async( return exception.response raise - sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) - sleep = min(sleep, max_interval / 1000) + + sleep = _get_sleep_interval( + exception, initial_interval, max_interval, exponent, retries + ) await asyncio.sleep(sleep) retries += 1 diff --git a/src/comfydeploy/utils/serializers.py b/src/comfydeploy/utils/serializers.py index baa41fb..14321eb 100644 --- a/src/comfydeploy/utils/serializers.py +++ b/src/comfydeploy/utils/serializers.py @@ -1,13 +1,16 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from decimal import Decimal +import functools import json -from typing import Any, Dict, List, Union, get_args -import httpx +import typing +from typing import Any, Dict, List, Tuple, Union, get_args +import typing_extensions from typing_extensions import get_origin + +import httpx from pydantic import ConfigDict, create_model from pydantic_core import from_json -from typing_inspection.typing_objects import is_union from ..types.basemodel import BaseModel, Nullable, OptionalNullable, Unset @@ -99,26 +102,6 @@ def validate_int(b): return int(b) -def validate_open_enum(is_int: bool): - def validate(e): - if e is None: - return None - - if isinstance(e, Unset): - return e - - if is_int: - if not isinstance(e, int): - raise ValueError("Expected int") - else: - if not isinstance(e, str): - raise ValueError("Expected string") - - return e - - return validate - - def validate_const(v): def validate(c): # Optional[T] is a Union[T, None] @@ -185,6 +168,15 @@ def is_nullable(field): return False +def is_union(obj: object) -> bool: + """ + Returns True if the given object is a typing.Union or typing_extensions.Union. + """ + return any( + obj is typing_obj for typing_obj in _get_typing_objects_by_name_of("Union") + ) + + def stream_to_text(stream: httpx.Response) -> str: return "".join(stream.iter_text()) @@ -217,3 +209,21 @@ def _contains_pydantic_model(data: Any) -> bool: return any(_contains_pydantic_model(value) for value in data.values()) return False + + +@functools.cache +def _get_typing_objects_by_name_of(name: str) -> Tuple[Any, ...]: + """ + Get typing objects by name from typing and typing_extensions. + Reference: https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types + """ + result = tuple( + getattr(module, name) + for module in (typing, typing_extensions) + if hasattr(module, name) + ) + if not result: + raise ValueError( + f"Neither typing nor typing_extensions has an object called {name!r}" + ) + return result diff --git a/src/comfydeploy/utils/unmarshal_json_response.py b/src/comfydeploy/utils/unmarshal_json_response.py new file mode 100644 index 0000000..6c39969 --- /dev/null +++ b/src/comfydeploy/utils/unmarshal_json_response.py @@ -0,0 +1,38 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from typing import Any, Optional, Type, TypeVar, overload + +import httpx + +from .serializers import unmarshal_json +from comfydeploy.models import errors + +T = TypeVar("T") + + +@overload +def unmarshal_json_response( + typ: Type[T], http_res: httpx.Response, body: Optional[str] = None +) -> T: ... + + +@overload +def unmarshal_json_response( + typ: Any, http_res: httpx.Response, body: Optional[str] = None +) -> Any: ... + + +def unmarshal_json_response( + typ: Any, http_res: httpx.Response, body: Optional[str] = None +) -> Any: + if body is None: + body = http_res.text + try: + return unmarshal_json(body, typ) + except Exception as e: + raise errors.ResponseValidationError( + "Response validation failed", + http_res, + e, + body, + ) from e From 56d7f6f70d522a1f6a89266eaede20ea66d1504a Mon Sep 17 00:00:00 2001 From: "speakeasy-github[bot]" <128539517+speakeasy-github[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 00:20:39 +0000 Subject: [PATCH 2/2] empty commit to trigger [run-tests] workflow