From 36600b64efbe4a8bb3ff5a892819b04dbab2486a Mon Sep 17 00:00:00 2001 From: christian-byrne <72887196+christian-byrne@users.noreply.github.com> Date: Thu, 3 Sep 2026 20:38:54 +0000 Subject: [PATCH] chore: sync Comfy API v2 specification and Comfy Router reference from cloud@e57bc53 --- comfy-router-limitations.mdx | 157 +++++ comfy-router-quickstart.mdx | 534 ++++++++++++++++++ comfy-router-reference.mdx | 339 +++++++++++ openapi-v2.yaml | 167 +++++- router-schemas/anthropic/claude-fable-5.json | 1 + .../anthropic/claude-haiku-4-5-20251001.json | 1 + router-schemas/anthropic/claude-opus-4-6.json | 1 + router-schemas/anthropic/claude-opus-4-7.json | 1 + router-schemas/anthropic/claude-opus-4-8.json | 1 + router-schemas/anthropic/claude-opus-5.json | 1 + .../anthropic/claude-sonnet-4-5-20250929.json | 1 + .../anthropic/claude-sonnet-4-6.json | 1 + router-schemas/anthropic/claude-sonnet-5.json | 1 + router-schemas/beeble/switchx.json | 1 + router-schemas/bfl/erase-v1.json | 1 + router-schemas/bfl/flux-2-max.json | 1 + router-schemas/bfl/flux-2-pro.json | 1 + router-schemas/bfl/flux-3-video.json | 1 + router-schemas/bfl/flux-kontext-max.json | 1 + router-schemas/bfl/flux-kontext-pro.json | 1 + router-schemas/bfl/flux-pro-1.0-canny.json | 1 + router-schemas/bfl/flux-pro-1.0-depth.json | 1 + router-schemas/bfl/flux-pro-1.0-expand.json | 1 + router-schemas/bfl/flux-pro-1.0-fill.json | 1 + router-schemas/bfl/flux-pro-1.1-ultra.json | 1 + router-schemas/bfl/flux-pro-1.1.json | 1 + router-schemas/bfl/video-upscale-v1.json | 1 + router-schemas/bfl/vto-v1.json | 1 + router-schemas/bria/fibo.json | 1 + router-schemas/bria/image-edit-erase.json | 1 + router-schemas/bria/image-edit-expand.json | 1 + router-schemas/bria/image-edit-gen-fill.json | 1 + .../bria/image-edit-remove-background.json | 1 + .../dreamina-seedance-2-0-260128.json | 1 + .../dreamina-seedance-2-0-fast-260128.json | 1 + .../byteplus/dreamina-seedance-2-0-mini.json | 1 + .../dreamina-seedance-2-5-260628.json | 1 + .../byteplus/seed-audio-1.0-multilingual.json | 1 + router-schemas/byteplus/seed-audio-1.0.json | 1 + .../seedance-1-0-lite-i2v-250428.json | 1 + .../seedance-1-0-lite-t2v-250428.json | 1 + .../byteplus/seedance-1-0-pro-250528.json | 1 + .../seedance-1-0-pro-fast-251015.json | 1 + .../byteplus/seedance-1-5-pro-251215.json | 1 + .../byteplus/seededit-3-0-i2i-250628.json | 1 + .../byteplus/seedream-3-0-t2i-250415.json | 1 + .../byteplus/seedream-4-0-250828.json | 1 + .../byteplus/seedream-4-5-251128.json | 1 + .../byteplus/seedream-5-0-260128.json | 1 + .../byteplus/seedream-5-0-pro-260628.json | 1 + .../gemini-omni-1.1-flash.json | 1 + .../gemini-omni-flash-preview.json | 1 + router-schemas/ideogram/ideogram-v4.json | 1 + router-schemas/kling/kling-3.0-turbo.json | 1 + router-schemas/kling/kling-image-o1.json | 1 + router-schemas/kling/kling-v1-5.json | 1 + router-schemas/kling/kling-v1-6.json | 1 + router-schemas/kling/kling-v1.json | 1 + router-schemas/kling/kling-v2-1-master.json | 1 + router-schemas/kling/kling-v2-1.json | 1 + router-schemas/kling/kling-v2-5-turbo.json | 1 + router-schemas/kling/kling-v2-6.json | 1 + router-schemas/kling/kling-v2-master.json | 1 + router-schemas/kling/kling-v3-omni.json | 1 + router-schemas/kling/kling-v3.json | 1 + router-schemas/kling/kling-video-o1.json | 1 + router-schemas/krea/krea-2-large.json | 1 + router-schemas/krea/krea-2-medium-turbo.json | 1 + router-schemas/krea/krea-2-medium.json | 1 + router-schemas/krea/krea-2.json | 1 + router-schemas/ltx/ltx-2-5-fast.json | 1 + router-schemas/ltx/ltx-2-5-pro.json | 1 + router-schemas/luma/photon-1.json | 1 + router-schemas/luma/photon-flash-1.json | 1 + router-schemas/luma/ray-1-6.json | 1 + router-schemas/luma/ray-2.json | 1 + router-schemas/luma/ray-flash-2.json | 1 + router-schemas/luma_2/uni-1-max.json | 1 + router-schemas/luma_2/uni-1.json | 1 + router-schemas/meshy/meshy-5.json | 1 + router-schemas/meshy/meshy-6.json | 1 + router-schemas/meshy/meshy-7.json | 1 + router-schemas/minimax/minimax-h3.json | 1 + router-schemas/openai/gpt-4.1-mini.json | 1 + router-schemas/openai/gpt-4.1-nano.json | 1 + router-schemas/openai/gpt-4.1.json | 1 + router-schemas/openai/gpt-4o.json | 1 + router-schemas/openai/gpt-5-mini.json | 1 + router-schemas/openai/gpt-5-nano.json | 1 + router-schemas/openai/gpt-5.5-pro.json | 1 + router-schemas/openai/gpt-5.5.json | 1 + router-schemas/openai/gpt-5.6-luna.json | 1 + router-schemas/openai/gpt-5.6-sol.json | 1 + router-schemas/openai/gpt-5.6-terra.json | 1 + router-schemas/openai/gpt-5.json | 1 + router-schemas/openai/gpt-image-1.5.json | 1 + router-schemas/openai/gpt-image-1.json | 1 + router-schemas/openai/gpt-image-2.json | 1 + router-schemas/openai/o1-pro.json | 1 + router-schemas/openai/o1.json | 1 + router-schemas/openai/o3.json | 1 + router-schemas/openai/o4-mini.json | 1 + router-schemas/qwen/qwen-image-3.0-pro.json | 1 + router-schemas/qwen/qwen-image-3.0.json | 1 + router-schemas/recraft/recraftv2.json | 1 + router-schemas/recraft/recraftv3.json | 1 + router-schemas/recraft/recraftv4.json | 1 + router-schemas/recraft/recraftv4_1.json | 1 + router-schemas/recraft/recraftv4_1_pro.json | 1 + .../recraft/recraftv4_1_pro_vector.json | 1 + .../recraft/recraftv4_1_utility.json | 1 + .../recraft/recraftv4_1_utility_pro.json | 1 + .../recraftv4_1_utility_pro_vector.json | 1 + .../recraft/recraftv4_1_utility_vector.json | 1 + .../recraft/recraftv4_1_vector.json | 1 + router-schemas/recraft/recraftv4_pro.json | 1 + router-schemas/recraft/recraftv4_styles.json | 1 + .../recraft/recraftv4_styles_pro.json | 1 + .../recraft/recraftv4_styles_pro_vector.json | 1 + .../recraft/recraftv4_styles_vector.json | 1 + router-schemas/runway/aleph2.json | 1 + router-schemas/runway/gen4_image.json | 1 + router-schemas/runway/gen4_turbo.json | 1 + router-schemas/veo/veo-2.0-generate-001.json | 1 + .../veo/veo-3.0-fast-generate-001.json | 1 + router-schemas/veo/veo-3.0-generate-001.json | 1 + .../veo/veo-3.1-fast-generate-001.json | 1 + router-schemas/veo/veo-3.1-generate-001.json | 1 + .../veo/veo-3.1-lite-generate-001.json | 1 + .../vertexai/gemini-2.5-flash-image.json | 1 + router-schemas/vertexai/gemini-2.5-flash.json | 1 + router-schemas/vertexai/gemini-2.5-pro.json | 1 + .../vertexai/gemini-3-pro-image.json | 1 + .../vertexai/gemini-3.1-flash-image.json | 1 + .../vertexai/gemini-3.1-flash-lite-image.json | 1 + .../vertexai/gemini-3.1-flash-lite.json | 1 + .../vertexai/gemini-3.1-pro-preview.json | 1 + router-schemas/vertexai/gemini-3.5-flash.json | 1 + router-schemas/vertexai/gemini-3.7-flash.json | 1 + router-schemas/wan/happyhorse-1.0-i2v.json | 1 + router-schemas/wan/happyhorse-1.0-r2v.json | 1 + router-schemas/wan/happyhorse-1.0-t2v.json | 1 + .../wan/happyhorse-1.0-video-edit.json | 1 + router-schemas/wan/happyhorse-1.1-i2v.json | 1 + router-schemas/wan/happyhorse-1.1-r2v.json | 1 + router-schemas/wan/happyhorse-1.1-t2v.json | 1 + router-schemas/wan/wan2.5-i2i-preview.json | 1 + router-schemas/wan/wan2.5-i2v-preview.json | 1 + router-schemas/wan/wan2.5-t2i-preview.json | 1 + router-schemas/wan/wan2.5-t2v-preview.json | 1 + router-schemas/wan/wan2.6-i2v.json | 1 + router-schemas/wan/wan2.6-r2v.json | 1 + router-schemas/wan/wan2.6-t2v.json | 1 + router-schemas/wan/wan2.7-i2v.json | 1 + router-schemas/wan/wan2.7-r2v.json | 1 + router-schemas/wan/wan2.7-t2v.json | 1 + router-schemas/wan/wan2.7-videoedit.json | 1 + router-schemas/wan/wan3.0-video-prime.json | 1 + router-schemas/wan/wan3.0-video.json | 1 + .../xai/grok-imagine-image-2.0.json | 1 + .../xai/grok-imagine-image-pro.json | 1 + .../xai/grok-imagine-image-quality.json | 1 + router-schemas/xai/grok-imagine-image.json | 1 + .../xai/grok-imagine-video-1.5-preview.json | 1 + .../xai/grok-imagine-video-1.5.json | 1 + router-schemas/xai/grok-imagine-video.json | 1 + 166 files changed, 1346 insertions(+), 13 deletions(-) create mode 100644 comfy-router-limitations.mdx create mode 100644 comfy-router-quickstart.mdx create mode 100644 comfy-router-reference.mdx create mode 100644 router-schemas/anthropic/claude-fable-5.json create mode 100644 router-schemas/anthropic/claude-haiku-4-5-20251001.json create mode 100644 router-schemas/anthropic/claude-opus-4-6.json create mode 100644 router-schemas/anthropic/claude-opus-4-7.json create mode 100644 router-schemas/anthropic/claude-opus-4-8.json create mode 100644 router-schemas/anthropic/claude-opus-5.json create mode 100644 router-schemas/anthropic/claude-sonnet-4-5-20250929.json create mode 100644 router-schemas/anthropic/claude-sonnet-4-6.json create mode 100644 router-schemas/anthropic/claude-sonnet-5.json create mode 100644 router-schemas/beeble/switchx.json create mode 100644 router-schemas/bfl/erase-v1.json create mode 100644 router-schemas/bfl/flux-2-max.json create mode 100644 router-schemas/bfl/flux-2-pro.json create mode 100644 router-schemas/bfl/flux-3-video.json create mode 100644 router-schemas/bfl/flux-kontext-max.json create mode 100644 router-schemas/bfl/flux-kontext-pro.json create mode 100644 router-schemas/bfl/flux-pro-1.0-canny.json create mode 100644 router-schemas/bfl/flux-pro-1.0-depth.json create mode 100644 router-schemas/bfl/flux-pro-1.0-expand.json create mode 100644 router-schemas/bfl/flux-pro-1.0-fill.json create mode 100644 router-schemas/bfl/flux-pro-1.1-ultra.json create mode 100644 router-schemas/bfl/flux-pro-1.1.json create mode 100644 router-schemas/bfl/video-upscale-v1.json create mode 100644 router-schemas/bfl/vto-v1.json create mode 100644 router-schemas/bria/fibo.json create mode 100644 router-schemas/bria/image-edit-erase.json create mode 100644 router-schemas/bria/image-edit-expand.json create mode 100644 router-schemas/bria/image-edit-gen-fill.json create mode 100644 router-schemas/bria/image-edit-remove-background.json create mode 100644 router-schemas/byteplus/dreamina-seedance-2-0-260128.json create mode 100644 router-schemas/byteplus/dreamina-seedance-2-0-fast-260128.json create mode 100644 router-schemas/byteplus/dreamina-seedance-2-0-mini.json create mode 100644 router-schemas/byteplus/dreamina-seedance-2-5-260628.json create mode 100644 router-schemas/byteplus/seed-audio-1.0-multilingual.json create mode 100644 router-schemas/byteplus/seed-audio-1.0.json create mode 100644 router-schemas/byteplus/seedance-1-0-lite-i2v-250428.json create mode 100644 router-schemas/byteplus/seedance-1-0-lite-t2v-250428.json create mode 100644 router-schemas/byteplus/seedance-1-0-pro-250528.json create mode 100644 router-schemas/byteplus/seedance-1-0-pro-fast-251015.json create mode 100644 router-schemas/byteplus/seedance-1-5-pro-251215.json create mode 100644 router-schemas/byteplus/seededit-3-0-i2i-250628.json create mode 100644 router-schemas/byteplus/seedream-3-0-t2i-250415.json create mode 100644 router-schemas/byteplus/seedream-4-0-250828.json create mode 100644 router-schemas/byteplus/seedream-4-5-251128.json create mode 100644 router-schemas/byteplus/seedream-5-0-260128.json create mode 100644 router-schemas/byteplus/seedream-5-0-pro-260628.json create mode 100644 router-schemas/gemini-interactions/gemini-omni-1.1-flash.json create mode 100644 router-schemas/gemini-interactions/gemini-omni-flash-preview.json create mode 100644 router-schemas/ideogram/ideogram-v4.json create mode 100644 router-schemas/kling/kling-3.0-turbo.json create mode 100644 router-schemas/kling/kling-image-o1.json create mode 100644 router-schemas/kling/kling-v1-5.json create mode 100644 router-schemas/kling/kling-v1-6.json create mode 100644 router-schemas/kling/kling-v1.json create mode 100644 router-schemas/kling/kling-v2-1-master.json create mode 100644 router-schemas/kling/kling-v2-1.json create mode 100644 router-schemas/kling/kling-v2-5-turbo.json create mode 100644 router-schemas/kling/kling-v2-6.json create mode 100644 router-schemas/kling/kling-v2-master.json create mode 100644 router-schemas/kling/kling-v3-omni.json create mode 100644 router-schemas/kling/kling-v3.json create mode 100644 router-schemas/kling/kling-video-o1.json create mode 100644 router-schemas/krea/krea-2-large.json create mode 100644 router-schemas/krea/krea-2-medium-turbo.json create mode 100644 router-schemas/krea/krea-2-medium.json create mode 100644 router-schemas/krea/krea-2.json create mode 100644 router-schemas/ltx/ltx-2-5-fast.json create mode 100644 router-schemas/ltx/ltx-2-5-pro.json create mode 100644 router-schemas/luma/photon-1.json create mode 100644 router-schemas/luma/photon-flash-1.json create mode 100644 router-schemas/luma/ray-1-6.json create mode 100644 router-schemas/luma/ray-2.json create mode 100644 router-schemas/luma/ray-flash-2.json create mode 100644 router-schemas/luma_2/uni-1-max.json create mode 100644 router-schemas/luma_2/uni-1.json create mode 100644 router-schemas/meshy/meshy-5.json create mode 100644 router-schemas/meshy/meshy-6.json create mode 100644 router-schemas/meshy/meshy-7.json create mode 100644 router-schemas/minimax/minimax-h3.json create mode 100644 router-schemas/openai/gpt-4.1-mini.json create mode 100644 router-schemas/openai/gpt-4.1-nano.json create mode 100644 router-schemas/openai/gpt-4.1.json create mode 100644 router-schemas/openai/gpt-4o.json create mode 100644 router-schemas/openai/gpt-5-mini.json create mode 100644 router-schemas/openai/gpt-5-nano.json create mode 100644 router-schemas/openai/gpt-5.5-pro.json create mode 100644 router-schemas/openai/gpt-5.5.json create mode 100644 router-schemas/openai/gpt-5.6-luna.json create mode 100644 router-schemas/openai/gpt-5.6-sol.json create mode 100644 router-schemas/openai/gpt-5.6-terra.json create mode 100644 router-schemas/openai/gpt-5.json create mode 100644 router-schemas/openai/gpt-image-1.5.json create mode 100644 router-schemas/openai/gpt-image-1.json create mode 100644 router-schemas/openai/gpt-image-2.json create mode 100644 router-schemas/openai/o1-pro.json create mode 100644 router-schemas/openai/o1.json create mode 100644 router-schemas/openai/o3.json create mode 100644 router-schemas/openai/o4-mini.json create mode 100644 router-schemas/qwen/qwen-image-3.0-pro.json create mode 100644 router-schemas/qwen/qwen-image-3.0.json create mode 100644 router-schemas/recraft/recraftv2.json create mode 100644 router-schemas/recraft/recraftv3.json create mode 100644 router-schemas/recraft/recraftv4.json create mode 100644 router-schemas/recraft/recraftv4_1.json create mode 100644 router-schemas/recraft/recraftv4_1_pro.json create mode 100644 router-schemas/recraft/recraftv4_1_pro_vector.json create mode 100644 router-schemas/recraft/recraftv4_1_utility.json create mode 100644 router-schemas/recraft/recraftv4_1_utility_pro.json create mode 100644 router-schemas/recraft/recraftv4_1_utility_pro_vector.json create mode 100644 router-schemas/recraft/recraftv4_1_utility_vector.json create mode 100644 router-schemas/recraft/recraftv4_1_vector.json create mode 100644 router-schemas/recraft/recraftv4_pro.json create mode 100644 router-schemas/recraft/recraftv4_styles.json create mode 100644 router-schemas/recraft/recraftv4_styles_pro.json create mode 100644 router-schemas/recraft/recraftv4_styles_pro_vector.json create mode 100644 router-schemas/recraft/recraftv4_styles_vector.json create mode 100644 router-schemas/runway/aleph2.json create mode 100644 router-schemas/runway/gen4_image.json create mode 100644 router-schemas/runway/gen4_turbo.json create mode 100644 router-schemas/veo/veo-2.0-generate-001.json create mode 100644 router-schemas/veo/veo-3.0-fast-generate-001.json create mode 100644 router-schemas/veo/veo-3.0-generate-001.json create mode 100644 router-schemas/veo/veo-3.1-fast-generate-001.json create mode 100644 router-schemas/veo/veo-3.1-generate-001.json create mode 100644 router-schemas/veo/veo-3.1-lite-generate-001.json create mode 100644 router-schemas/vertexai/gemini-2.5-flash-image.json create mode 100644 router-schemas/vertexai/gemini-2.5-flash.json create mode 100644 router-schemas/vertexai/gemini-2.5-pro.json create mode 100644 router-schemas/vertexai/gemini-3-pro-image.json create mode 100644 router-schemas/vertexai/gemini-3.1-flash-image.json create mode 100644 router-schemas/vertexai/gemini-3.1-flash-lite-image.json create mode 100644 router-schemas/vertexai/gemini-3.1-flash-lite.json create mode 100644 router-schemas/vertexai/gemini-3.1-pro-preview.json create mode 100644 router-schemas/vertexai/gemini-3.5-flash.json create mode 100644 router-schemas/vertexai/gemini-3.7-flash.json create mode 100644 router-schemas/wan/happyhorse-1.0-i2v.json create mode 100644 router-schemas/wan/happyhorse-1.0-r2v.json create mode 100644 router-schemas/wan/happyhorse-1.0-t2v.json create mode 100644 router-schemas/wan/happyhorse-1.0-video-edit.json create mode 100644 router-schemas/wan/happyhorse-1.1-i2v.json create mode 100644 router-schemas/wan/happyhorse-1.1-r2v.json create mode 100644 router-schemas/wan/happyhorse-1.1-t2v.json create mode 100644 router-schemas/wan/wan2.5-i2i-preview.json create mode 100644 router-schemas/wan/wan2.5-i2v-preview.json create mode 100644 router-schemas/wan/wan2.5-t2i-preview.json create mode 100644 router-schemas/wan/wan2.5-t2v-preview.json create mode 100644 router-schemas/wan/wan2.6-i2v.json create mode 100644 router-schemas/wan/wan2.6-r2v.json create mode 100644 router-schemas/wan/wan2.6-t2v.json create mode 100644 router-schemas/wan/wan2.7-i2v.json create mode 100644 router-schemas/wan/wan2.7-r2v.json create mode 100644 router-schemas/wan/wan2.7-t2v.json create mode 100644 router-schemas/wan/wan2.7-videoedit.json create mode 100644 router-schemas/wan/wan3.0-video-prime.json create mode 100644 router-schemas/wan/wan3.0-video.json create mode 100644 router-schemas/xai/grok-imagine-image-2.0.json create mode 100644 router-schemas/xai/grok-imagine-image-pro.json create mode 100644 router-schemas/xai/grok-imagine-image-quality.json create mode 100644 router-schemas/xai/grok-imagine-image.json create mode 100644 router-schemas/xai/grok-imagine-video-1.5-preview.json create mode 100644 router-schemas/xai/grok-imagine-video-1.5.json create mode 100644 router-schemas/xai/grok-imagine-video.json diff --git a/comfy-router-limitations.mdx b/comfy-router-limitations.mdx new file mode 100644 index 000000000..558643627 --- /dev/null +++ b/comfy-router-limitations.mdx @@ -0,0 +1,157 @@ +--- +title: "Comfy Router limitations" +description: "What Comfy Router does not do today, what to use instead where an alternative exists, and which of those limits are expected to change." +--- + + +**Comfy Router is not generally available yet.** The routes referenced below — +`POST /v2/models/{provider}/{model}` and its catalog and schema siblings — are +not serving requests yet: an authenticated call answers `404` today. This page +describes the contract they will serve, published ahead of that rollout so an +integration can be written against a known shape. Everything below is a +statement about that contract, not about behaviour you can exercise right now. + + +Comfy Router is one synchronous call: you send a partner model's native input to one host with one credential, the connection stays open, and a `200` carries that model's native output. That shape is what makes the first integration short, and it is also where every limit on this page comes from. Read this page before you design around Router, not after — most of what follows has a straightforward alternative, and the ones that do not are worth knowing before you build on an assumption Router does not hold. + +## At a glance + +Each row links to the section that explains it. **Deliberate** means the limit is part of how Router works and is not waiting on anything; **not yet** means Router is expected to gain the capability, though this page makes no commitment about when. + +| Limitation | Use instead | Status | +| --- | --- | --- | +| [No queued submission — the call is synchronous](#no-queued-submission) | Hold the connection open, or use a partner-proxy route that submits and polls | Not yet | +| [No cost or credit figures on a response](#no-cost-or-credit-figures-on-a-response) | Read your balance and usage on the Comfy platform; check `billing` on the model's catalog entry before calling | Not yet | +| [No way to resume a call you lost](#no-way-to-resume-a-call-you-lost) | Send `Idempotency-Key` on every call and keep it: re-sending it collects the original generation for 24h, or starts a fresh run when there is nothing to collect | Not yet | +| [Calls are cut off at a server deadline](#calls-are-cut-off-at-a-server-deadline) | Give your client a timeout above the deadline; re-send the same `Idempotency-Key` to collect the generation that kept running; split work that cannot finish inside it | Deliberate | +| [Requests are rate limited per caller](#requests-are-rate-limited-per-caller) | Back off for `Retry-After` on a `429` `rate_limited`; cache the catalog reads rather than re-fetching them per call | Deliberate | +| [No progress while a call runs](#no-progress-while-a-call-runs) | Nothing on Router today; a partner-proxy route may expose its own progress | Not yet | +| [For submit-and-poll models, the success body is the partner's final poll document](#the-success-body-is-the-partners-final-poll-document) | Read the model's published output schema at `GET /v2/models/{provider}/{model}/openapi.json`; do not port a `/proxy` helper's return value | Deliberate | +| [Three forecast buckets are not in the vocabulary](#three-forecast-buckets-are-not-in-the-vocabulary) | Handle the fifteen buckets Router publishes; treat anything unrecognized as `internal_error` | Not yet | +| [Router does not cover every partner operation](#router-does-not-cover-every-partner-operation) | The partner-proxy routes under `/proxy/…` on the same host | Deliberate | + +## No queued submission + +There is one way to run a model: `POST /v2/models/{provider}/{model}`, which holds the connection until the generation finishes and returns the result in the response. There is no endpoint that accepts a job, hands you an identifier and lets you collect the result later, and no callback or webhook on completion. + +**What to do instead.** For most models this is a non-issue: keep the connection open and read the result. A fast image model returns in a few seconds; a long video generation can run for minutes, and Router will hold the connection for it. Set a generous client read timeout — above [Router's own deadline](#calls-are-cut-off-at-a-server-deadline) — and treat the call as long-running rather than as a fast request. If your architecture genuinely cannot hold a connection open — a serverless function with a short execution ceiling, a browser tab you expect the user to close — then run the call from a worker you control that can, or use a partner-proxy route for a provider that exposes its own submit-and-poll pair. See [the last section](#router-does-not-cover-every-partner-operation). + +**Status: not yet.** The queued path is expected; nothing on this page commits to when. + +## No cost or credit figures on a response + +A Router response tells you what the model produced, and its contract says nothing about what it cost. There is no charge amount, no credit balance and no usage figure in the body, and the route declares no cost header. One caveat, so it does not surprise you: Router shares a billing path with the partner-proxy routes, and that path stamps `X-Comfy-Credits-Used` on a billed response for an allowlist of providers, so the header can appear on a Router call to one of them. It is not part of Router's contract — it is absent for every provider outside that allowlist, and it is deliberately *not* replayed on an idempotent retry, precisely so a client summing it cannot double-count a call that was only paid for once. Do not build reconciliation on it. The model catalog is the same: it carries billing *facts* a caller needs before invoking, never prices. So you cannot reconcile spend from a Router response alone, and you cannot show a user "this call cost X" without getting X from somewhere else. + +**What to do instead.** Your balance, your usage and your invoices live on the Comfy platform at [platform.comfy.org](https://platform.comfy.org) — that is the source of truth for what you have spent and what you have left, and it is unaffected by anything on this page. Two things Router does tell you at call time are worth using: a call refused for lack of credit comes back as `insufficient_credits`, so you can handle exhaustion as a typed error rather than by pre-checking a balance; and each model's catalog entry carries `billing.charges_on_policy_rejection`, which says whether that specific model charges you for a generation it then refuses on content-policy grounds. It is a **string with three values**, not a boolean: `yes`, `no` and `unknown`. Read `unknown` as "this might charge you" — it means nobody has established that model's behaviour yet, and it exists precisely so an unchecked model is not published as a `no`, which is a claim. The field is deliberately not an `enum`, so treat any value you do not recognize as `unknown` too, and do not write a truthiness check over it: the string `"no"` is truthy in most languages, and that check gets backwards the one case it exists to catch. Providers differ on that, the difference is invisible at call time, and reading it before you call is how you avoid a charge you cannot explain afterwards. + +**Status: not yet** for per-call figures. Note that the *catalog* deliberately carries no prices — pricing belongs where pricing is maintained, not duplicated into a model listing that would drift from it. + +## No way to resume a call you lost + +Router does not keep a *resumable* record of an in-flight call. There is no status route, no job identifier, and nothing to reconnect to: if the connection drops mid-call — a client crash, a network partition, a deploy that restarts your process — the response you were waiting for is gone. What Router keeps instead is keyed on something you supply, which is why the whole recovery story depends on your having supplied it. + +**What to do instead.** Send an `Idempotency-Key` header on every call, and keep the value. Router reserves the key for the duration of the call and holds it for **24 hours from the first request**; re-sending the same request — same model path, same body — under the same key is answered rather than re-run, and which answer you get depends on what Router is holding: + +- **The finished response** — replayed byte for byte, marked `Idempotent-Replayed: true`, and not charged a second time. The charge settled when the original call completed. +- **A generation the provider is still running** — the `504` `deadline_exceeded` or the lost connection left Router holding the provider's handle, so your resend polls that same generation instead of submitting another. You get `504` again with `Retry-After` until it finishes, then the result. +- **The original call, still in flight** — `409` `concurrency_limit_exceeded` with `Retry-After`. Exactly one resend collects at a time; wait and ask again. +- **Nothing it can answer with** — no response was ever committed to you, or the call ended in a `5xx` or one of the explicitly transient refusals (`408`, `425`, `429`). None of those charged you, so Router releases the key and your resend is a genuine fresh call — the right default for an outcome nobody paid for. + +The key is what makes all four of those work, and Router never gives it back to you: **no Router response carries the `Idempotency-Key`** — not the `200`, not the `504`, not any error body. It is yours to mint, so mint it *before* the call and store it for as long as you would care about the result. A caller who generated one inline and did not keep it has no handle on a generation they may already have been billed for. The [quickstart](/comfy-router-quickstart) has the collect loop in Python and TypeScript, and the official SDKs mint and reuse the key for you — the Python SDK also surfaces it on every exception it raises; in TypeScript, keep it yourself. + +Two rules, because both are easy to get wrong and neither fails loudly: + +**One key per logical call, reused across every attempt of that call.** The same key with a *different* request — a different body, but also a different model path, query string or method — is refused `409` `invalid_input` rather than quietly replacing the first call — so a request you corrected and want to re-send needs a NEW key. That includes correcting a `422`: the validation failure is itself recorded against the key, so the fixed body under the old key is a `409`. + +**A `409` `invalid_input` is terminal for that key, whatever its `detail` says.** Beyond the different-body case it also covers a call that completed but whose response Router could not keep a faithful copy of — a response past the replay cap, a handler that failed after answering, a write to you that came up short, or a recorded body encoded in a way your resend did not accept. Do not go hunting for a size problem when you see it, and do not retry it: there is no `Retry-After` on any of them because waiting changes nothing, and the answer in every case is a **new** key. In the recorded-but-unservable cases the original call completed and, if it succeeded, was charged, so Router will neither invent its response nor re-run it under the old key. (The different-body case is the one exception to that reading: it only tells you the key belongs to another request, not how that request turned out.) + + +**In the contract, with one gap.** The `Idempotency-Key` request header, the +`409` response and the `Idempotent-Replayed` and `Retry-After` response headers +described here are declared on `POST /v2/models/{provider}/{model}`, so they +appear in the generated [API reference](/comfy-router-reference) and in the +specification the SDKs vendor, so an SDK picks them up when it regenerates. The +gap: `Retry-After` is +declared on the `409` and the `504` but **not** on the `rate_limited` `429` +described [below](#requests-are-rate-limited-per-caller), which sends it too — +read it there without waiting for the contract to say so. + + +**Status: not yet.** Durable, resumable execution — a status route you can poll for a call you never held a key for — is expected to arrive with the queued path, which is where a request record has somewhere to live. Idempotent retry is the answer today and is not a stopgap: it is worth wiring in regardless, because it is also what stops an ordinary retry from being billed twice. + +## Calls are cut off at a server deadline + +One Router call may hold its connection for **10 minutes**. That is the default; it is a server-side configuration value rather than a fixed constant, so treat it as the number to design against rather than a guarantee etched into the contract. Past it, Router stops waiting, cancels its own in-flight request to the provider and answers `504` with `X-Comfy-Error-Type: deadline_exceeded`. + +**The deadline bounds the connection, not the charge.** Cancelling ends Router's own wait; it does not recall a generation a provider has already accepted. For the partners Router drives by submitting a job and polling it, that job keeps running after the `504` and **is billed if it completes** — you pay for work that was done, not for having been present to receive it. The one thing cancellation also cannot do is un-send an answer: if the handler wins the race and commits a response just as the bound expires, you keep that response rather than the `504`. + +**What to do instead.** Two things, and the second is the one people miss. + +Set your client's read timeout comfortably *above* the deadline, not below it. A client that gives up first turns a typed `504` with a request identifier into an opaque local abort, and you lose the one artifact support can trace. + +Then **re-send the same `Idempotency-Key`** — the generation you are being billed for is still running, and the key is how you collect it. Router keeps the key for **24 hours from the first request** carrying the provider's handle, and a resend with it polls that same generation rather than submitting a second one: still running answers this same `504` again with `Retry-After` naming when to ask next, finished answers `200` with the result and `Idempotent-Replayed: true` and is not charged again, and a terminal provider failure answers with its own bucket and releases the key. Without a key — or when the bound expired before the provider had accepted anything, which is every direct-return model and any submit leg that never answered — a retry is a genuine fresh dispatch, which is correct because there is nothing paid for that it could duplicate. See [the section above](#no-way-to-resume-a-call-you-lost) for the full set of answers a resend can get, and the [quickstart](/comfy-router-quickstart) for the loop in Python and TypeScript. + +If a single generation genuinely cannot finish inside the deadline, collecting it across resends works but is not the shape to design for: run it through a partner-proxy route that submits and polls, or break the work into calls that each finish inside the bound. + +Do not confuse `deadline_exceeded` with the other `504`. `provider_timeout` is the partner failing to answer in time; `deadline_exceeded` is Router's own bound expiring. Two causes on one status code, acted on differently — a `deadline_exceeded` says nothing about the request was rejected and the same call is worth re-sending under its key, while a `provider_timeout` says the partner is the thing that failed. Branch on `X-Comfy-Error-Type`, never on the status alone. + +**Status: deliberate.** A bound has to exist — without one, a stuck upstream holds a connection and a concurrency slot indefinitely. The specific number may be tuned; the existence of a deadline will not go away. + +## Requests are rate limited per caller + +Router bounds two different things about your traffic, and they answer with two different buckets on the same `429`. The concurrency limit caps how many calls you have **in flight** at once and answers `concurrency_limit_exceeded`; it clears the moment one of your own calls finishes, so retrying in seconds is right. The rate limit caps how **often** you may hit the Router surface at all — `POST /v2/models/{provider}/{model}` and the three catalog reads under `/v2/models` alike, whether the call ran a model or was refused before it could — and answers `rate_limited`. That one is an allowance that refills continuously over a one-minute window, so nothing you do drains it early: the response carries a `Retry-After` header with the seconds to wait, and `detail` names the window. Branch on `X-Comfy-Error-Type`, never on the status alone. + +The limit is keyed on the authenticated caller, not on the source address, so it follows your credential across hosts. A call that runs on your own provider key (bring-your-own-key) is exempt: you own that throughput. The allowance is a server-side configuration value rather than a published constant, and this page deliberately does not quote it; design for backoff, not for a number. + +**What to do instead.** Honour `Retry-After` — a retry inside it lands on the same refusal. Fetch `GET /v2/models` and a model's `openapi.json` once and cache them for the life of your process rather than re-reading them ahead of every call; they change only on a deploy. A client that keeps a request identifier from a `429` has the artifact support can trace. + +**Status: deliberate.** A per-caller bound on request rate has to exist for the same reason the deadline does. The number is tunable; the existence of the limit will not go away. + +## No progress while a call runs + +`POST /v2/models/{provider}/{model}` returns exactly once, at the end. There is no streaming response, no server-sent events, no percentage, no partial or preview frame. This holds even for partners whose own API is submit-and-poll: Router does that polling internally, inside your one call, and the intermediate states it sees are not forwarded to you. From the outside, a three-second image and a six-minute video are the same shape — one request, one response, nothing in between. + +**What to do instead.** On Router today, nothing: show an indeterminate progress state rather than a percentage you cannot source. If progress is a hard requirement for a specific provider, check whether that provider's partner-proxy routes expose their own polling or streaming and use those directly — a few do, and they are unchanged and fully supported. + +**Status: not yet**, and tied to the queued path: progress needs somewhere to report *to*, which a queued submission provides and a single synchronous call does not. + +## The success body is the partner's final poll document + +For a model Router runs by submitting a job and polling it upstream, the `200` body is the partner's **final poll document** — whichever document Router's last upstream poll returned, forwarded rather than reshaped. It is not the return value of any `/proxy`-side SDK or workflow helper. A helper is free to unwrap an envelope, to select the polled element out of a batch answer, or to fetch a separate result document before it hands you a value — Router deliberately does none of that, because none of it is what the partner's own endpoint answers with. The difference does not fail loudly: a client typed against a helper's narrower shape reads a field that is not there, gets nothing back, and carries on under a `200 OK`. Nor is that document always what the route a partner calls *status* answers with: `fal/minimax/h3-max` polls fal's queue **result** route, `GET /minimax/h3-max/requests/{id}`, because fal's `/status` answers `{status, urls, metrics}` and never carries the output, and the `bfl/*` family polls `GET /v1/get_result`. A client typed against a status-route shape reads fields the `200` does not carry, and fails the same quiet way. + +One thing inside that document is deliberately different from the partner's own copy of it: for a short list of providers Router copies the generated asset onto Comfy storage while it answers, so the link you receive points at Comfy rather than at the partner. Which models those are, how long such a link stays valid, and what a partly copied response looks like are stated in [Result assets](/comfy-router-reference#result-assets) and are not restated here. No field is renamed, unwrapped or re-enveloped. One caveat on that guarantee, because the re-host paths are not all written the same way: `bfl/*` and `byteplus/*` rewrite the asset field on the raw JSON document, so every other field keeps its exact bytes, while the `xai/*` and `minimax/*` paths rewrite through a generated Go type and re-marshal from it — a partner field that type does not declare is not carried through. If you depend on a partner field the model's published output schema does not list, read it from the partner's own API rather than from a re-hosted Router response. + +`kling/kling-3.0-turbo` is the concrete case. It answers with Kling's query-task **list envelope** — `{"code": …, "message": …, "request_id": …, "data": []}`, with the finished asset at `data[].outputs[].url`, or at `data[].outputs[].watermark_url` when Kling answers with only the hotlink-protected copy — because Kling's status route is a shared `GET /tasks?task_ids=…` that answers a list even when it was asked about a single task. Router polls one task, so that list carries one element; it is still a list, and `data` is still an array rather than the task object. Read both output leaves rather than only `url`: Router's own success rule accepts either, so a `200` whose element carries only `watermark_url` is a finished generation a client watching one field alone would silently discard. A helper that hands you the single task is a client convenience, not the wire contract. + +**What to do instead.** Read the model's published output schema rather than a helper's signature. `GET /v2/models/{provider}/{model}/openapi.json` serves one document per model, and its `200` describes the body Router returns for that model: for `kling/kling-3.0-turbo` that is the list envelope the served document publishes as `KlingV2QueryTaskResponse`, with `KlingV2Task` and `KlingV2Output` beneath it, spelling out the `succeeded` terminal status and the `data[].outputs[].url` and `data[].outputs[].watermark_url` leaves. The pointer is the right one to write into a client even for a model Comfy has not described yet: for **any model in the catalog** the route answers, serving a documented permissive fallback flagged `x-comfy-output-schema-authored: false`, and reading that flag is how you tell a narrowed contract from an undescribed one. It is not an oracle for arbitrary identifiers, though, so handle its non-`200` answers: an id the catalog does not resolve gets `404` `model_not_found`, a conditional `If-None-Match` read gets `304`, and the route sits inside the same [per-caller rate limit](#requests-are-rate-limited-per-caller) as everything else under `/v2/models`. + +One sentence in the [API reference](/comfy-router-reference) is worth reading precisely here: a caller can move between the partner's API and Router by changing the host. That is a statement about the partner's **HTTP surface** — the document the partner's own endpoint returns. A partner SDK's or a `/proxy` helper's convenience return value is a third shape, and that sentence does not cover it: porting from a helper is a change to what you read out of the body, not a host swap. + +Two more cases are worth naming ahead of time, conditionally. `tripo` and `fal/patina` have proxy surfaces where the same gap exists — `tripo`'s task status route answers a `{code, data}` envelope, and PATINA's finished output lives behind a result route separate from its status route, so in both cases a convenience layer has something to reduce. Router does not run either of them today, so this section describes nothing you can call on them yet. If they are enrolled later they enrol under exactly this rule: the `200` is whichever document Router's last upstream poll returned, and the model's own `openapi.json` is where its shape is published. + +**Status: deliberate**, and the reason is the contract rather than a technical obstacle. Re-enveloping one provider family so it matches a helper would break the "forwarded unchanged" contract the reference states three times over — on the route, on the request body and on the `200` — and it would give Comfy a response shape of its own to own and version per provider, which is the coupling Router exists to avoid. Reshaping a body *can* be done safely, and Router does it: the naive version decodes into a generic `any`, where Go's `encoding/json` represents every number as a `float64` and rounds partner integers above 2^53 — task ids and seeds — but typed fields, `json.RawMessage` and `Decoder.UseNumber` each preserve them exactly, and the `bfl/*` and `byteplus/*` re-hosts rewrite one field through `map[string]json.RawMessage` precisely so nothing else in the document moves. + +## Three forecast buckets are not in the vocabulary + +Router's `error_type` vocabulary is a **closed set of fifteen** buckets — the fifteen the [API reference](/comfy-router-reference) lists and the quickstart points at. Three more are named in that reference's prose as expected additions: `file_download_error`, `cancelled` and `queue_timeout`. They are named, and that is all they are. They are **not members of the set today**: no Router response carries one, a client generated from the contract does not know them, and if Router were handed one internally it substitutes `internal_error` rather than putting it on the wire. So a branch you write for them today is a branch that never runs, and their appearance in the reference is not evidence that Router cancels calls or queues them — it does neither. + +They are forecast in writing rather than left out entirely because `error_type` is deliberately a plain string and not an `enum`, and a client that hard-rejects an unrecognized bucket fails hardest exactly when something has already gone wrong. Naming the additions in advance is how a reader knows the set is open-ended by design. + +**What to do instead.** Handle the fifteen buckets Router actually publishes, listed in full in the [API reference](/comfy-router-reference), and write one fallback branch that treats any unrecognized value as `internal_error`. That fallback is the whole mechanism: it is what lets these three, and any bucket added after your client was written, arrive without breaking you. Branch on the coarse bucket for control flow, and read the per-field `type` inside a `422` body when you need the specific reason. + +**Status: not yet.** Each of the three corresponds to behaviour Router does not have yet, and each joins the vocabulary in the same change that starts emitting it — never before. + +## Router does not cover every partner operation + +Router runs partner *models*. It does not front every operation a partner exposes — the file uploads, the account and asset reads, the provider-specific management calls, the streaming chat endpoints and the submit-and-poll pairs that some partners publish. Nor does Router reshape any of them: it forwards a model's native input and returns its native output unchanged, so there is no unified envelope to port an unsupported operation onto. + +One thing Router *does* change about a result belongs beside that, because it is the exception to "returns its native output unchanged": for a short list of providers Router copies the generated asset onto Comfy storage while it answers the call and hands you a Comfy-hosted link in place of the partner's, and every other model in the catalog returns the partner's own asset reference or its inline bytes untouched. Which models are on that list, how long a Comfy-hosted link stays valid, and what a response carries when one asset could not be copied are stated in [Result assets](/comfy-router-reference#result-assets) in the API reference. This page does not restate them — asset durability has one home, and that is it. + +**What to do instead.** The partner-proxy routes under `/proxy/…` remain fully supported on the same host, with the same credential, and they are the answer for anything Router does not cover. They are not deprecated, they are not on a sunset path, and using them alongside Router in the same integration is expected rather than a workaround. Reach for Router when you want one route shape and one credential across many models; reach for `/proxy/…` when you need a specific partner operation, a provider's own streaming response, or the submit-and-poll control that Router deliberately hides. + +**Status: deliberate.** Router narrows the surface on purpose — one route shape is the feature. The proxy surface stays where it is. + +## Next + +- [Comfy Router quickstart](/comfy-router-quickstart) — a first working call in Python or TypeScript. +- [Comfy Router API reference](/comfy-router-reference) — every endpoint, every parameter and every error bucket Router does send. diff --git a/comfy-router-quickstart.mdx b/comfy-router-quickstart.mdx new file mode 100644 index 000000000..fc32f9b31 --- /dev/null +++ b/comfy-router-quickstart.mdx @@ -0,0 +1,534 @@ +--- +title: "Comfy Router quickstart" +description: "From nothing to a generated image in about five minutes, in Python and TypeScript, against the Comfy Router." +--- + + +**Comfy Router is not generally available yet.** The routes below — +`POST /v2/models/{provider}/{model}` and its catalog and schema siblings — are +not serving requests yet: an authenticated call answers `404` today. This page +documents the contract they will serve, and is published ahead of that rollout so +the integration is ready to write against. It is not a description of behaviour +you can exercise right now. + + +Comfy Router runs partner models behind one host, one credential and one route shape. This page is the shortest complete path to a generated image: install a client, set a key, send one request, read the result — and see what the first failure looks like before you hit it. + +Base URL: `https://api.comfy.org`. The route is `POST /v2/models/{provider}/{model}`, the request body is the model's own native JSON input, and a `200` carries the model's own native JSON output. Router does not wrap either, so a call you already have written against the partner's API becomes a Router call by changing the host. + +## Why this page uses `bfl/flux-2-pro` + +`bfl/flux-2-pro` returns in about 3.1s at p50, which is the fastest measured path on the Router and is what makes a five-minute first result realistic — a slower model would spend that budget waiting rather than reading. + +It is a convenience, not a requirement. Every other model on the Router is called exactly the same way: same route, same credential header, same error buckets, same `X-Comfy-Request-Id`. Only the model ID, the fields inside the request body, and the shape of the result you read back change. Gemini, for instance, clears comfortably at 72.8s p95 — Router holds the connection for the whole generation rather than returning a job handle to poll. There is no edge ceiling cutting a long call short, but Router does bound the call itself: its own server deadline (10 minutes by default) is the longest it will hold a connection, after which it answers `504` / `deadline_exceeded`. That bound is on the connection, not on the charge — a generation the provider completes is billed whether or not you received it, which is what the `Idempotency-Key` below is for. Swap the ID and read that model's fields from its own schema (below). + +## Get a key + +Router authenticates with a Comfy API key. Create one at [platform.comfy.org/profile/api-keys](https://platform.comfy.org/profile/api-keys), then put it in the environment — both samples below read `COMFY_API_KEY` and neither takes a key as a literal, so a copy-pasted snippet cannot carry your credential into a commit. + +```bash +export COMFY_API_KEY="comfyui-..." +``` + + +A `comfyui-` key is accepted in either the **`X-API-Key`** header or +**`Authorization: Bearer`** — the `comfyui-` prefix, not the header, tells the +service it is an API key, so both forms are looked up identically. The examples +below use `X-API-Key`; `Authorization: Bearer $COMFY_API_KEY` is equivalent. +If both headers are sent, a key in `X-API-Key` takes precedence. A value in +`Authorization: Bearer` WITHOUT the `comfyui-` prefix is treated as a +Cloud/Firebase **JWT** (that is what the generated +[API reference](/comfy-router-reference) means by "bearer token"). + + +Keys are per workspace and carry that workspace's model entitlements and credit balance. A request with no usable credential comes back `401` with `X-Comfy-Error-Type: unauthorized`; one whose workspace cannot run the model comes back `403` / `forbidden`. + +## Python + +Requires Python 3.9+ and `httpx`: + +```bash +pip install httpx +``` + +Save as `quickstart.py` and run it with `python quickstart.py`: + +```python +import os +import uuid + +import httpx + +BASE_URL = os.environ.get("COMFY_ROUTER_BASE_URL", "https://api.comfy.org") +MODEL = "bfl/flux-2-pro" + +# Give the client headroom ABOVE Router's own server deadline (10 minutes by +# default) so a call that reaches the server bound comes back as a typed 504 +# with a request id rather than as an opaque client abort. The deadline bounds +# how long Router holds the connection, not whether the call is billed: if the +# provider completed the generation, it is billed either way. +READ_TIMEOUT_SECONDS = 660.0 + + +class RouterError(Exception): + """A Comfy Router failure, typed by its X-Comfy-Error-Type bucket.""" + + def __init__(self, response: httpx.Response) -> None: + self.error_type = response.headers.get("X-Comfy-Error-Type", "internal_error") + self.request_id = response.headers.get("X-Comfy-Request-Id") + self.status_code = response.status_code + # Seconds to wait before re-sending the SAME Idempotency-Key. Router + # sends it on the two answers that mean "the work exists, ask again" - + # a deadline_exceeded 504 over a generation still running, and the 409 + # that refuses a key whose original call is still in flight. + raw_retry_after = response.headers.get("Retry-After") + self.retry_after = int(raw_retry_after) if (raw_retry_after or "").isdigit() else None + # Parse defensively: an error can arrive as an HTML 502 from a load + # balancer, a plain-text 429, an empty body or a truncated JSON one. The + # status, the bucket and the request id above are the parts worth + # keeping, so a body that will not parse must not replace this exception + # with a JSONDecodeError and lose them. + body = None + if response.headers.get("content-type", "").startswith("application/json"): + try: + body = response.json() + except ValueError: + body = None + detail = body.get("detail") if isinstance(body, dict) else None + # A 422 carries a detail[] array - one entry per rejected field, each + # keeping its own `loc`, `msg` and `type`. Every other bucket carries a + # plain `detail` string. + self.errors = detail if isinstance(detail, list) else [] + self.detail = detail if isinstance(detail, str) else f"HTTP {response.status_code}" + super().__init__(self.detail) + + +def run(model: str, arguments: dict, idempotency_key: str) -> dict: + # Idempotency-Key makes a retry safe on a PAID call: Router replays the + # original response for 24h instead of dispatching (and billing) the + # provider a second time. Reuse the SAME key when retrying one logical + # call; generate a new one for a new call. + response = httpx.post( + f"{BASE_URL}/v2/models/{model}", + headers={ + "X-API-Key": os.environ["COMFY_API_KEY"], + "Idempotency-Key": idempotency_key, + }, + json=arguments, + timeout=httpx.Timeout(READ_TIMEOUT_SECONDS, connect=10.0), + ) + if response.is_error: + raise RouterError(response) + return response.json() + + +result = run( + MODEL, + {"prompt": "a red teapot on a windowsill, morning light"}, + idempotency_key=str(uuid.uuid4()), +) +# Router forwards each provider's native output unchanged, so this path is +# BFL's, not a Router envelope. Reading a different model means reading its own +# output shape. +print("image:", result["result"]["sample"]) + +# The first failure most callers hit: a field the model's input schema requires +# is missing, so Router rejects the request BEFORE any provider call - which is +# why a 422 is never billed. +try: + run(MODEL, {"width": 1024}, idempotency_key=str(uuid.uuid4())) +except RouterError as exc: + print(f"{exc.error_type} (HTTP {exc.status_code}), request id {exc.request_id}") + for entry in exc.errors: + print(" ", ".".join(str(p) for p in entry["loc"]), "->", entry["msg"]) +``` + +```text +image: https://.../out.jpeg +invalid_input (HTTP 422), request id 6f1c... + body.prompt -> Field required +``` + +## TypeScript + +Requires Node 18+ (for built-in `fetch`, `AbortSignal.timeout` and `crypto.randomUUID`) and `tsx` to run TypeScript directly: + +```bash +npm install --save-dev tsx +``` + +Save as `quickstart.mts` — the `.mts` extension is load-bearing, because the file uses top-level `await` and that needs an ES module — and run it with `npx tsx quickstart.mts`: + +```typescript +const BASE_URL = process.env.COMFY_ROUTER_BASE_URL ?? "https://api.comfy.org"; +const MODEL = "bfl/flux-2-pro"; + +// Headroom ABOVE Router's own server deadline (10 minutes by default), so a +// call that reaches the server bound returns a typed 504 with a request id +// rather than aborting locally at the same moment. The deadline bounds how +// long Router holds the connection, not whether the call is billed: if the +// provider completed the generation, it is billed either way. +const CLIENT_TIMEOUT_MS = 660_000; + +interface ValidationEntry { + loc: (string | number)[]; + msg: string; + type: string; +} + +/** A Comfy Router failure, typed by its `X-Comfy-Error-Type` bucket. */ +class RouterError extends Error { + readonly errorType: string; + readonly requestId: string | null; + readonly status: number; + /** Seconds to wait before re-sending the SAME `Idempotency-Key`. Router + * sends it on the two answers that mean "the work exists, ask again" — a + * `deadline_exceeded` `504` over a generation still running, and the `409` + * that refuses a key whose original call is still in flight. */ + readonly retryAfter: number | null; + /** A 422 carries a `detail[]` array — one entry per rejected field, each + * keeping its own `loc`, `msg` and `type`. Every other bucket carries a + * plain `detail` string. */ + readonly errors: ValidationEntry[]; + + constructor(response: Response, body: unknown) { + const detail = + typeof body === "object" && body !== null + ? (body as { detail?: unknown }).detail + : undefined; + super(typeof detail === "string" ? detail : `HTTP ${String(response.status)}`); + this.name = "RouterError"; + this.errorType = response.headers.get("X-Comfy-Error-Type") ?? "internal_error"; + this.requestId = response.headers.get("X-Comfy-Request-Id"); + this.status = response.status; + const retryAfter = Number(response.headers.get("Retry-After")); + this.retryAfter = Number.isInteger(retryAfter) && retryAfter > 0 ? retryAfter : null; + this.errors = Array.isArray(detail) ? (detail as ValidationEntry[]) : []; + } +} + +/** Read a body without letting a non-JSON error page mask the real failure. */ +async function parseBody(response: Response): Promise { + const text = await response.text(); + try { + return JSON.parse(text) as unknown; + } catch { + return undefined; + } +} + +async function run( + model: string, + args: Record, + idempotencyKey: string, +): Promise { + // Idempotency-Key makes a retry safe on a PAID call: Router replays the + // original response for 24h instead of dispatching (and billing) the provider + // a second time. Reuse the SAME key when retrying one logical call. + const response = await fetch(`${BASE_URL}/v2/models/${model}`, { + method: "POST", + headers: { + "X-API-Key": process.env.COMFY_API_KEY ?? "", + "Idempotency-Key": idempotencyKey, + "Content-Type": "application/json", + }, + body: JSON.stringify(args), + signal: AbortSignal.timeout(CLIENT_TIMEOUT_MS), + }); + // Branch on `ok` FIRST: an HTML 502, a plain-text 429 or an empty body must + // still surface the status, the bucket and the request id. + const body = await parseBody(response); + if (!response.ok) throw new RouterError(response, body); + return body as T; +} + +const result = await run<{ result: { sample: string } }>( + MODEL, + { prompt: "a red teapot on a windowsill, morning light" }, + crypto.randomUUID(), +); +// Router forwards each provider's native output unchanged, so this path is +// BFL's, not a Router envelope. Reading a different model means reading its own +// output shape. +console.log("image:", result.result.sample); + +// The first failure most callers hit: a field the model's input schema requires +// is missing, so Router rejects the request BEFORE any provider call - which is +// why a 422 is never billed. +try { + await run(MODEL, { width: 1024 }, crypto.randomUUID()); +} catch (exc) { + if (!(exc instanceof RouterError)) throw exc; + console.log(`${exc.errorType} (HTTP ${String(exc.status)}), request id ${String(exc.requestId)}`); + for (const entry of exc.errors) console.log(" ", entry.loc.join("."), "->", entry.msg); +} +``` + +```text +image: https://.../out.jpeg +invalid_input (HTTP 422), request id 6f1c... + body.prompt -> Field required +``` + +## Reading the `422` + +The `422` is the one error worth understanding before your first real call, because it is the one you cause. It means Router checked your body against the model's own input schema and rejected it — a required field missing, a value outside a bound, an image too small. That check runs BEFORE any provider call, so a `422` costs nothing: no partner spend, no billing question to answer afterwards. It is not the same as a `400`, which is a request-level failure (a malformed cursor, an unreadable envelope) rather than a per-field one. + +Its body is the FastAPI `detail[]` shape: an array with one entry per offending field, each keeping its own `loc` (the path to the field), `msg`, `type` (the specific, provider-level reason — `missing`, `value_error`, `image_too_small`) and, where the reason carries a bound, `ctx`. That per-field granularity is why the samples above keep the array as data instead of flattening it into the exception message. + + +A model whose input schema has not been authored yet resolves to a documented +permissive fallback that admits any JSON object, so it will forward a body +rather than answer `422`. The samples above show the shape you handle once a +schema exists; treat the `422` block as the error path, not as a guaranteed +response to that particular body. + + +That body carries no `error_type` field of its own, so on a `422` the `X-Comfy-Error-Type` header is the *only* machine-readable bucket. Both samples read the bucket from the header first for exactly that reason, which is also what makes one error class enough to cover every failure Router can return. + +`X-Comfy-Request-Id` is on every response — success, `4xx` and `5xx` alike — and is the id to quote in a support request. Both samples attach it to the exception rather than making you re-run with header logging on to find it. + +## Retrying safely with your own key + +Both samples above send an `Idempotency-Key`. It is worth a section of its own, because the header only does its job if you handle the key correctly — and the step that makes the difference happens before the request is even sent. + +**Bring your own key — Router never gives it back.** Router does not mint one for you, and **no Router response carries the `Idempotency-Key`** — not the `200`, not the `504`, not any error body. It is yours to mint, so a caller who generated one inline and did not store it has no handle on a generation they may already have been billed for. Generate a fresh key per *logical call* — a UUID is the intended shape — and reuse that same key for every retry *of that call*. A new key per attempt buys you nothing; a key reused across two genuinely different calls is a `409`, because the same key with a different request — a different body, but also a different model path, query string or method — is a conflict rather than a silent overwrite. That includes correcting a `422`: the validation failure is itself recorded against the key, so the fixed body under the old key is a `409` — send it under a new key. + +**Persist it before you send.** Write the key somewhere that outlives the request — the row you are generating for, your job record, your queue message — *before* the `POST` goes out, not after the response comes back. A key that only ever existed in the memory of the process that crashed cannot be resent, and the retry that would have been answered from Router's record becomes a fresh, separately charged run instead. This is the one step that is easy to skip and expensive to skip. + +**Retry with it.** Router holds the key for **24 hours from the first request**. On a retry, it answers rather than re-runs whenever it still holds state for the key: + +| What you get back | What it means | What to do | +| --- | --- | --- | +| `200` with `Idempotent-Replayed: true` | Router replayed the original response. Not billed again. | Use it — it is the original result. | +| `409` / `concurrency_limit_exceeded` | The original call is still running. | Wait `Retry-After` seconds, re-send **the same key**. | +| `409` / `invalid_input` | The key cannot serve this request: a different request (body, model path, query or method) under the same key, or the original completed and its response cannot be replayed. | Use a **new** key. Do not re-send this one. | +| `504` / `deadline_exceeded` with `Retry-After` | Router stopped holding the connection but still holds a handle to a generation the provider is running. | Wait `Retry-After` seconds, re-send **the same key** to collect it. | +| Nothing — no response was ever committed to you, or the call ended in a `5xx` other than that `504`, or in a `408`/`425`/`429` | None of those charged you, so Router released the key. | Re-send if you want a **fresh run** — it is a fresh charge, not a collect. | + +Continuing the Python sample above — a file stands in for whatever durable store +you already have; the ORDERING is the part that matters, not the mechanism. Persist +the whole request next to the key, not the key alone: a retry has to re-send the +*same* model and arguments, and one rebuilt from memory after a restart that differs +by so much as a whitespace is a `409`, while one sent under a fresh key is a second +billed generation. + +```python +import json +import uuid + +# Persist BEFORE the request, so a crash between here and the response still +# leaves a key — and the exact request it belongs to — you can retry with. +request = { + "model": MODEL, + "arguments": {"prompt": "a red teapot on a windowsill, morning light"}, + "idempotency_key": str(uuid.uuid4()), +} +with open("pending-call.json", "w") as f: + json.dump(request, f) + +result = run(request["model"], request["arguments"], idempotency_key=request["idempotency_key"]) +``` + +## If the call times out or you lose the connection + +This is the one failure you do not cause and cannot avoid by writing a better request: Router holds the connection for the whole generation, and a long one can outlast the connection. Past Router's own server deadline (10 minutes by default) you get `504` with `X-Comfy-Error-Type: deadline_exceeded`; a dropped socket, a redeployed worker or a closed laptop lid gets you nothing at all. Either way the *generation* may still be running at the provider, and **a generation that completes is billed whether or not you received it**. So the question is never "was I charged" — it is "can I still collect what I paid for". + +The key you persisted above is the answer. Re-send the SAME request — same model path, same body — under the SAME key, and the table above says what each answer means: a `504` or a `409` carrying `Retry-After` is "ask again on that interval", a `200` with `Idempotent-Replayed: true` is the result you were owed, and anything without `Retry-After` is Router's final answer on that key. + +### Collecting after a lost response + +Both snippets re-send the key the failed call used and honour `Retry-After` until the generation finishes. Neither mints a new key anywhere in the loop — that is the entire point. + +```python +import time + + +def collect(model: str, arguments: dict, idempotency_key: str, attempts: int = 30) -> dict: + """Re-send one call under its ORIGINAL key until Router has an answer.""" + for _ in range(attempts): + try: + response = httpx.post( + f"{BASE_URL}/v2/models/{model}", + headers={ + "X-API-Key": os.environ["COMFY_API_KEY"], + "Idempotency-Key": idempotency_key, + }, + json=arguments, + timeout=httpx.Timeout(READ_TIMEOUT_SECONDS, connect=10.0), + ) + if response.is_error: + raise RouterError(response) + if response.headers.get("Idempotent-Replayed") == "true": + print("collected the original generation; not charged again") + return response.json() + except RouterError as exc: + # deadline_exceeded means the generation is still running; + # concurrency_limit_exceeded on a 409 means another attempt is + # already collecting it. Both say "ask again", and both name when. + # Anything without a Retry-After is Router's final answer on this + # key - including the 409 that says the key is spent for good. + if exc.retry_after is None: + raise + time.sleep(exc.retry_after) + except httpx.HTTPError: + # The flaky connection that lost the response in the first place can + # just as easily lose a collect attempt. The key is unchanged, so + # asking again is still safe. + time.sleep(2) + raise TimeoutError(f"gave up collecting {idempotency_key} after {attempts} attempts") + + +# Mint the key FIRST, so it survives the call that fails. +arguments = {"prompt": "a red teapot on a windowsill, morning light"} +key = str(uuid.uuid4()) +try: + result = run(MODEL, arguments, idempotency_key=key) +except RouterError as exc: + if exc.error_type != "deadline_exceeded": + raise + result = collect(MODEL, arguments, idempotency_key=key) +except httpx.HTTPError: + # A dropped connection never reached a response, so there is no bucket to + # branch on - but the key is still good and the generation may still be + # running, which is exactly what collect() is for. + result = collect(MODEL, arguments, idempotency_key=key) +print("image:", result["result"]["sample"]) +``` + +```typescript +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +/** Re-send one call under its ORIGINAL key until Router has an answer. */ +async function collect( + model: string, + args: Record, + idempotencyKey: string, + attempts = 30, +): Promise { + for (let i = 0; i < attempts; i++) { + let response: Response; + try { + response = await fetch(`${BASE_URL}/v2/models/${model}`, { + method: "POST", + headers: { + "X-API-Key": process.env.COMFY_API_KEY ?? "", + "Idempotency-Key": idempotencyKey, + "Content-Type": "application/json", + }, + body: JSON.stringify(args), + signal: AbortSignal.timeout(CLIENT_TIMEOUT_MS), + }); + } catch (exc) { + // The flaky connection that lost the response in the first place can just + // as easily lose a collect attempt. The key is unchanged, so asking again + // is still safe - but a caller's own abort is theirs and stops the loop. + if (!(exc instanceof TypeError || exc instanceof DOMException)) throw exc; + await sleep(2000); + continue; + } + const body = await parseBody(response); + if (response.ok) { + if (response.headers.get("Idempotent-Replayed") === "true") { + console.log("collected the original generation; not charged again"); + } + return body as T; + } + // `deadline_exceeded` means the generation is still running; + // `concurrency_limit_exceeded` on a 409 means another attempt is already + // collecting it. Both say "ask again", and both name when. Anything with no + // `Retry-After` is Router's final answer on this key. + const error = new RouterError(response, body); + if (error.retryAfter === null) throw error; + await sleep(error.retryAfter * 1000); + } + throw new Error(`gave up collecting ${idempotencyKey} after ${String(attempts)} attempts`); +} + +// Mint the key FIRST, so it survives the call that fails. +const args = { prompt: "a red teapot on a windowsill, morning light" }; +const key = crypto.randomUUID(); +let image: { result: { sample: string } }; +try { + image = await run<{ result: { sample: string } }>(MODEL, args, key); +} catch (exc) { + const lostTheResponse = + exc instanceof RouterError + ? exc.errorType === "deadline_exceeded" + // A dropped connection or a local abort never reached a response, so it + // arrives as a plain fetch failure with no bucket to branch on - but the + // key is still good and the generation may still be running, which is + // what collect() is for. Anything else is a bug in this program rather + // than a lost result, so it propagates. + : exc instanceof TypeError || exc instanceof DOMException; + if (!lostTheResponse) throw exc; + image = await collect<{ result: { sample: string } }>(MODEL, args, key); +} +console.log("image:", image.result.sample); +``` + +### Replaying from a different process + +Nothing above requires the retry to happen in the same process, or even on the same day — the key is the only state that has to survive, and Router keeps its side for 24 hours. If the process that made the call might not be the one that reads the result, derive the key from something you already persist (an order id, a job row's primary key) instead of a fresh UUID, store it alongside that record *before* you call, and hand it to `collect()` from wherever the recovery runs. + +```python +key = f"order-{order_id}-render" # deterministic, stored with the order +``` + +The key must still be unique per logical call — a second render of the same order needs a distinct key, or Router hands back the first render's result rather than rendering again (or refuses the resend `409` if the request changed at all). Keys are scoped to the authenticated caller, so you only have to be unique within your own workspace, and the value is any non-empty string up to 255 characters. + + +**The official SDKs do the minting for you.** Both send an `Idempotency-Key` on +every `models.run()` call, mint a fresh one per call, and reuse that one key +across their own internal retries — including retrying a `deadline_exceeded` +`504` under it, so a single `run()` can ride the collect loop through the server +deadline to the finished generation. Neither one starts a second billed +generation on a retry it made itself. + +Getting the key back **after** `run()` gives up differs between them today. In +Python, every exception carries the key it sent, so the recovery idiom is +`client.models.run(model, arguments, idempotency_key=exc.idempotency_key)` — +check the attribute is not `None` before passing it back, because the parameter +treats `None` as "mint a new one", which starts a second billed generation +instead of collecting the first. In TypeScript the errors do not carry it yet, +so keep the key yourself exactly as the samples above do and pass it back as +`models.run(model, input, { idempotencyKey })`. + + +## Find a model + +`bfl/flux-2-pro` is one ID; the catalog is the rest. `GET /v2/models` lists every model Router can run, one page at a time, and each entry is exactly what you need to call it: the `id` you put in the path, its `provider` and `model` segments carried separately, and a `billing` block you can branch on before you spend anything. + +```bash +curl -H "X-API-Key: $COMFY_API_KEY" \ + "https://api.comfy.org/v2/models?limit=50" +``` + +```json +{ + "data": [ + { "id": "bfl/flux-2-pro", "provider": "bfl", "model": "flux-2-pro", "billing": { "charges_on_policy_rejection": "no" } } + ], + "has_more": true, + "next_cursor": "q7Fm2xTn9pLd4RsV", + "limit": 50 +} +``` + +Walk it with the cursor, not with an offset: pass `next_cursor` back as `?cursor=` and stop when `has_more` is `false` — not when a page comes back short. The cursor is opaque and only ever round-tripped; a cursor Router does not accept is a `400` / `invalid_input`, never a silent restart at page one. A cursor is a position in the catalog's sorted order, so it stays valid across a deploy that adds or removes models — a model added behind your position is simply not visited on that walk. A `503` / `service_unavailable` on the list means Router could not answer yet (a pod still loading its release state); retry it, do not read it as an empty catalog. `limit` on the response is the page size actually served — a request above the cap is clamped rather than rejected, so paginate with the number you got back. A model that is deployed but not yet released is simply absent from every page. + +## Where the model's fields come from + +`prompt` is the only field `bfl/flux-2-pro` requires; `width`, `height`, `seed` and `output_format` are the ones you will reach for next. Rather than reproducing a field list that can drift, read the model's schema live: + +```bash +curl -H "X-API-Key: $COMFY_API_KEY" \ + https://api.comfy.org/v2/models/bfl/flux-2-pro/openapi.json +``` + +That is the same document the server validates your call against, served as a standalone OpenAPI document, so what is published and what is enforced cannot disagree. Take any `id` from the catalog above, append `/openapi.json` to its invocation path, and generate against what comes back. + +## Next + +- [Comfy Router API reference](/comfy-router-reference) — every endpoint, every parameter, and all fifteen error buckets. +- [Comfy Router limitations](/comfy-router-limitations) — what Router does not do today, and what to use instead. diff --git a/comfy-router-reference.mdx b/comfy-router-reference.mdx new file mode 100644 index 000000000..5e4cae411 --- /dev/null +++ b/comfy-router-reference.mdx @@ -0,0 +1,339 @@ +--- +title: "Comfy Router API reference" +description: "Every Comfy Router endpoint, parameter, response body and error bucket, generated from the Comfy API contract." +--- + +{/* + GENERATED FILE -- DO NOT HAND-EDIT. + + Produced from the Comfy API contract by gen_router_reference.py. Edit the + contract and regenerate; an edit made here is overwritten by the next run and + is rejected by the drift gate in the meantime. +*/} + +Comfy Router's canonical, model-ID-addressed routes. + +Base URL: `https://api.comfy.org` + +Every endpoint below is authenticated. Send `X-API-Key: ` or `Authorization: Bearer `. + +## Endpoints + +### `GET /v2/models` + +**List the models Comfy Router can run.** + +Comfy Router's model catalog - one page of the canonical model IDs that `POST /v2/models/{provider}/{model}` accepts. An SDK calls this on cold start to discover what is runnable, and the `model_not_found` suggestions come from the same catalog, so an ID listed here that then 404s on invocation would be worse than either failure alone. That agreement is structural rather than a promise: an entry's `provider` and `model` are the two path segments of the invocation route and reference the SAME schema components that route's path parameters do, and `id` is those two segments joined by `/`. + +**Parameters** + +| Name | In | Required | Type | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `cursor` | query | no | [`RouterPageCursor`](#routerpagecursor) | `pattern: ^[A-Za-z0-9._~+/=-]+$`, `minLength: 1`, `maxLength: 512` | Opaque pagination cursor. Pass a previous page's `next_cursor` to fetch the next page; omit it for the first page. See `RouterPageCursor` for why the value is opaque and why this route paginates by cursor rather than by offset. | +| `limit` | query | no | integer | `maximum: 100`, `default: 20` | Number of models to return in one page. Values above the declared maximum are outside the contract, but this route does not reject them: it serves the maximum instead, and the page size actually served is echoed back as `limit` on the response, so a clamp is always detectable by the caller. Treat the maximum as the real page stride - a client that asks for more and assumes it received more will miss rows. 0 and negative values are also accepted and select the default, which is why no `minimum` is declared: sub-1 is meaningful here, not invalid. | + +**Responses** + +| Status | Body | Headers | Description | +| --- | --- | --- | --- | +| `200` | [`RouterModelListResponse`](#routermodellistresponse) | `X-Comfy-Request-Id` | OK - one page of the model catalog. | +| `400` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `401` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `403` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `503` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | + +### `GET /v2/models/{provider}/{model}` + +**Read one partner model's catalog entry by canonical model ID.** + +Per-model detail for a single Comfy Router model, so a caller can check one model without walking the whole paginated catalog. The SDKs use it to look a model up immediately before invoking it. + +**Parameters** + +| Name | In | Required | Type | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `provider` | path | yes | [`RouterProviderSegment`](#routerprovidersegment) | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 64` | Lowercase provider segment of the canonical `{provider}/{model}[/{variant}]` model ID - the partner whose model is being run. | +| `model` | path | yes | [`RouterModelSegment`](#routermodelsegment) | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 128` | Lowercase model segment of the canonical `{provider}/{model}[/{variant}]` model ID - the model to run within that provider. | + +**Responses** + +| Status | Body | Headers | Description | +| --- | --- | --- | --- | +| `200` | [`RouterModelDetail`](#routermodeldetail) | `X-Comfy-Request-Id` | OK - the model's catalog entry. | +| `401` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `403` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `404` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `503` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | + +### `POST /v2/models/{provider}/{model}` + +**Run a partner model synchronously by canonical model ID.** + +Comfy Router's canonical, model-ID-addressed entry point. The request body is the partner model's OWN native JSON input and the success response is that model's OWN native JSON output: Router forwards both unchanged instead of imposing a Comfy-shaped envelope, so a caller can move between the partner's API and Router by changing the host. This is the SYNCHRONOUS path: the response carries the finished result. + +**Parameters** + +| Name | In | Required | Type | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `provider` | path | yes | [`RouterProviderSegment`](#routerprovidersegment) | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 64` | Lowercase provider segment of the canonical `{provider}/{model}[/{variant}]` model ID - the partner whose model is being run. | +| `model` | path | yes | [`RouterModelSegment`](#routermodelsegment) | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 128` | Lowercase model segment of the canonical `{provider}/{model}[/{variant}]` model ID - the model to run within that provider. | +| `Idempotency-Key` | header | no | string | `minLength: 1`, `maxLength: 255` | Caller-generated key that makes retrying ONE logical call safe. A call that reached the caller with an answer is recorded against its key for 24 hours, and a retry carrying the same key is answered from that record instead of dispatching - and charging - the provider a second time, marked `Idempotent-Replayed: true`. Keys are scoped to the workspace your credential carries, or to your user when it carries none - so the keyspace is SHARED by every member of a workspace rather than private to one caller. Make a key unique across the whole workspace, not just within your own client: a second member who reuses a key string is answered from the first member's record, or refused `409` if the request differs. Because the scope follows the CREDENTIAL and not the person, a credential that carries no workspace at all scopes to your user id instead - so retrying one logical call under a different credential can land in a different namespace, where it is dispatched and charged again. Retry with the credential you started with. A keyed request with no authenticated caller is refused `401`. The guarantee is a BILLING one: a key is charged at most once. It is not a promise that a key is dispatched at most once, and it does not make a lost call resumable. Some answers are RECORDED but not replayable for the full 24 hours, and the billing guarantee is the half that always holds: the key stays consumed - the retry never re-runs and never re-charges - but it is answered `409 invalid_input` instead of being served the original body. That happens whenever Comfy does not hold a copy of the response it can still stand behind; a response past the replay size cap and a result addressed by an asset URL Comfy does not host are the two you are most likely to meet. The second is the one worth planning for, because it looks like an ordinary success: which models answer with a Comfy-hosted asset link, how long one stays valid, and what a result carries when an individual asset could not be copied are stated in one place, under Result assets in the API reference, and this paragraph does not restate them. On a model that returns its result on the original call, an answer still holding a partner's own asset link is replayed for a few minutes - which is where a dropped connection puts an SDK's automatic same-key re-send, and while the partner's link is certainly still alive - and refused after that rather than replayed dead. So a prompt retry of a partially re-hosted result behaves exactly like any other replay, and only a later one meets the `409`. That short window is deliberately NOT offered on a model that submits and is polled, because there the partner may have minted the URL long before your call collected it and its remaining life is unknowable - and those models do not need it: a call cut off mid-generation keeps its key holding the generation, so the same-key retry collects the ORIGINAL result rather than a recorded copy of it. A response past the size cap has no window either and is refused from the start. The action on any of these `409 invalid_input` refusals is the same: use a new key. Only an answer a provider actually produced is recorded, though. A refusal Router raises on its own BEFORE dispatching anything - not enabled for you yet (`403`), unknown model (`404`), not entitled to the model (`403`), a body the model's schema rejects or that names a different model than the path (`422`), a malformed request (`400 invalid_input`) - dispatched nothing and charged nothing, so it RELEASES the key: re-send the SAME key once you are on the rollout ramp or have corrected the request and it runs for real, rather than replaying the refusal or colliding with it as a `409`. That turns on whether a provider was reached, NEVER on the status, so a `400 content_policy_violation` - the partner's own answer to a call that ran, which some models meter - is recorded and replayed like any other answer. Releasing a refusal that dispatched nothing frees nothing chargeable, so it does not weaken the at-most-once billing guarantee above. | + +**Request body** + +`application/json` -- [`RouterModelInput`](#routermodelinput) (required) + +The partner model's native JSON input, forwarded to the provider unchanged. + +**Responses** + +| Status | Body | Headers | Description | +| --- | --- | --- | --- | +| `200` | [`RouterModelOutput`](#routermodeloutput) | `X-Comfy-Request-Id`, `Idempotent-Replayed`, `X-Committed-Spend-Limit`, `X-Committed-Spend-Current`, `X-Committed-Spend-Remaining` | OK - the partner model's native JSON output, returned unchanged. When this response was replayed from the record held against an `Idempotency-Key` rather than produced by running the model again, it carries `Idempotent-Replayed: true` and is not charged a second time. | +| `400` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id`, `Idempotent-Replayed` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. On this route the status is ALSO how the partner's own refusal of a call that really ran is returned - the `content_policy_violation` some models meter - and that answer is recorded against an `Idempotency-Key` and served to a same-key retry, so unlike the catalog reads' shared error this response can arrive carrying `Idempotent-Replayed: true`. | +| `401` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `403` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `404` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `409` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id`, `Retry-After` | The `Idempotency-Key` on this request is already held, and this request cannot be answered from its record. Two conditions share the status and `X-Comfy-Error-Type` is what separates them, because they are acted on in opposite ways. `concurrency_limit_exceeded` means the original call for this key is still running: wait `Retry-After` seconds and re-send THE SAME key, which collects that call's result rather than starting a second one. `invalid_input` means the key cannot serve this request at all - it was already used for a different request (the method, the path and query, or the body differ from the original), or the original completed (and, if it succeeded, was charged) and Router holds no copy of its response it can still stand behind - for example it was too large to store, or it names an asset Comfy does not host and so cannot promise still resolves, which on a direct-return model is replayed for a few minutes after the original call and refused after that - or the copy it holds is content-encoded in a way this request did not accept - and the answer is always a NEW key, never a re-send of this one. There is no `Retry-After` on any of these, because waiting changes nothing. `detail` says which case it is; the different-request case says nothing about how the call that does own the key turned out. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `413` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `422` | [`RouterValidationErrorResponse`](#routervalidationerrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id`, `Idempotent-Replayed` | The request's contents were rejected against the model's schema. The body is `RouterValidationErrorResponse`, the FastAPI `detail[]` shape, so each offending field keeps its own specific `type` and `ctx`. `X-Comfy-Error-Type` carries the coarse bucket for the whole response. | +| `429` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id`, `X-Committed-Spend-Limit`, `X-Committed-Spend-Current`, `X-Committed-Spend-Remaining` | The caller is holding as much in-flight capacity as they are allowed and the request was refused before it reached the model. The bucket is `concurrency_limit_exceeded` in either case and `detail` says which bound was hit: the number of concurrent calls, or the committed spend of the calls still in flight, whose refusal also carries the `X-Committed-Spend-Limit`, `X-Committed-Spend-Current` and `X-Committed-Spend-Remaining` headers (USD cents). Retry once one of the caller's own in-flight calls finishes. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `503` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `504` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id`, `Retry-After` | Comfy stopped holding the connection at its own configured bound (`deadline_exceeded`). The body and the two headers are exactly `RouterRequestError`'s; what this adds is the optional `Retry-After`, present when a retry with the same `Idempotency-Key` will collect the generation that is still running rather than dispatch a new one. See the `504` on `POST /v2/models/{provider}/{model}`. | + +### `GET /v2/models/{provider}/{model}/openapi.json` + +**Read one partner model's input and output schemas as an OpenAPI document.** + +The per-model input AND output schemas for a single Comfy Router model, served as a standalone OpenAPI document, so a caller - an SDK, a codegen tool, or an agent - can discover a model's arguments, and the shape of what it returns, without reading Comfy's prose docs. It is the discovery mechanism the SDK quickstart depends on. + +**Parameters** + +| Name | In | Required | Type | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `provider` | path | yes | [`RouterProviderSegment`](#routerprovidersegment) | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 64` | Lowercase provider segment of the canonical `{provider}/{model}[/{variant}]` model ID - the partner whose model is being run. | +| `model` | path | yes | [`RouterModelSegment`](#routermodelsegment) | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 128` | Lowercase model segment of the canonical `{provider}/{model}[/{variant}]` model ID - the model to run within that provider. | +| `If-None-Match` | header | no | string | - | The `ETag` a caller holds from an earlier `200`. When it matches the current document (RFC 9110 weak comparison; `*` matches any current document) the answer is a bodyless `304` carrying the same `ETag`, otherwise the full document. | + +**Responses** + +| Status | Body | Headers | Description | +| --- | --- | --- | --- | +| `200` | [`RouterModelInputSchemaDocument`](#routermodelinputschemadocument) | `X-Comfy-Request-Id`, `ETag`, `Cache-Control` | OK - the model's input AND output schemas, as a standalone OpenAPI document. | +| `304` | - | `X-Comfy-Request-Id`, `ETag`, `Cache-Control` | Not Modified - the document is unchanged since the `ETag` the caller sent in `If-None-Match`. No body is returned. | +| `401` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `403` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `404` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `500` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | +| `503` | [`RouterErrorResponse`](#routererrorresponse) | `X-Comfy-Error-Type`, `X-Comfy-Request-Id` | A Router request-level failure - the request never reached the model, or failed for a reason the model itself did not report. The body is `RouterErrorResponse` and the bucket is repeated on `X-Comfy-Error-Type`. | + +## Error buckets + +Coarse, machine-readable bucket for a Router failure, mirrored on the `X-Comfy-Error-Type` response header so a caller can branch without parsing the body. The set is closed at fifteen values: the six request-level buckets `invalid_input`, `content_policy_violation`, `provider_error`, `provider_timeout`, `insufficient_credits` and `model_not_found`, plus the transport-level `unauthorized`, `forbidden`, `concurrency_limit_exceeded`, `client_disconnected`, `internal_error`, `deadline_exceeded`, `not_enabled`, `service_unavailable` and `rate_limited`. + +### Request-level buckets + +Raised for a request Router accepted and then could not complete. + +| `error_type` | Meaning | +| --- | --- | +| `invalid_input` | The request was rejected before it reached the model - a malformed body, a malformed or expired pagination cursor, an input the model's own schema does not accept, or an `Idempotency-Key` that cannot serve this request (already used for a different request - the method, the path and query, or the body differ - or already consumed by a call whose response cannot be replayed). Sent with `409` in the key cases and with `400`/`422` in the others; the status says which, and the key cases are the ones answered by using a NEW key rather than by editing the request. | +| `content_policy_violation` | The provider refused the request on content-policy grounds. The refusal is deterministic: re-sending the same input will be refused again. | +| `provider_error` | The partner provider reported a failure of its own, or returned a response Router could not interpret as a result. | +| `provider_timeout` | The partner provider did not answer within its deadline. This bucket is the PROVIDER timing out and never Router's own server deadline, which is reported as `deadline_exceeded` - the two share `504` and are separated because they name different causes: this one says the partner failed, that one says Comfy stopped holding the connection. | +| `insufficient_credits` | The calling workspace does not have enough credits to run the model. | +| `model_not_found` | The `{provider}/{model}` ID names no model Router can run; an unknown provider lands here too. `detail` carries up to three suggestions drawn from the models the caller is entitled to see. | + +### Transport-level buckets + +Raised by Router itself, before or around the call to the model. + +| `error_type` | Meaning | +| --- | --- | +| `unauthorized` | The request carried no usable credential. | +| `forbidden` | The credential is valid but is not entitled to this model or this operation. | +| `concurrency_limit_exceeded` | The workspace already has as many calls in flight as it is allowed; retry once one of them finishes. It carries one further condition on the run route, on a `409` rather than the `429` above: another call is already in flight for the `Idempotency-Key` this request presented. Re-send the SAME key after `Retry-After` seconds to collect that call's result. | +| `client_disconnected` | The caller closed the connection before Router could return a result. It is logged rather than delivered - there is no socket left to write it to - and it is an attribution, not a billing outcome: a provider generation that completed is billed regardless of whether the caller received the response. | +| `internal_error` | Router itself failed. It is also the value a client should treat any UNRECOGNIZED bucket as, so a later addition to the set does not break a client generated before it. | +| `deadline_exceeded` | Comfy stopped holding the connection at its own configured bound before an answer arrived. It shares `504` with `provider_timeout` and the pair says which side ran out of time; this one is Comfy's own bound, so nothing about the request was rejected and the same request may be retried. It says nothing about the charge: a provider generation that completed is billed regardless of whether the caller received the response. Retry it with the SAME `Idempotency-Key`: when the provider had already accepted the generation, the retry collects that generation rather than dispatching another, and a `Retry-After` on the `504` says when to ask. | +| `not_enabled` | Comfy Router is not switched on for this caller yet. Nothing about the request is wrong and the model exists, which is why this is not `model_not_found`; it shares `403` with `forbidden` and is NOT the same thing, because `forbidden` is an entitlement decision about the caller while this is a state of the rollout. It is TERMINAL: do not retry, and do not treat it as an outage. | +| `service_unavailable` | A service Comfy Router depends on is temporarily unavailable and the caller did nothing wrong. Retry it with backoff: it is the one bucket here whose condition clears on its own, without the caller changing the request and without a concurrency slot freeing, which is what distinguishes it from the other retryable answers (`concurrency_limit_exceeded`, `deadline_exceeded`). It is separate from `internal_error` - which is a `500` and means Router itself failed - so a client can tell "come back shortly" from "this call is not going to work". | +| `rate_limited` | The caller has spent an allowance measured over a WINDOW and must wait for that window to roll. It shares `429` with `concurrency_limit_exceeded` and is not the same thing: that one clears the moment one of the caller's own in-flight calls finishes, so retrying in seconds is right, whereas nothing the caller does drains this one early. `detail` names the window. | + +## Response headers + +| Header | Type | Description | +| --- | --- | --- | +| `Cache-Control` | string | Freshness directives for the served schema document. `private` because the route is authenticated - the document itself is not caller-specific, but a shared cache must not hold a response to an authenticated request - and `must-revalidate` so a stale copy is revalidated against the `ETag` rather than served on. | +| `ETag` | string | Strong entity tag over the served document's bytes, for `GET /v2/models/{provider}/{model}/openapi.json`. A per-model schema changes rarely and an SDK re-fetches it often, so a caller should store this value and send it back as `If-None-Match` to get a `304` instead of the document. | +| `Idempotent-Replayed` | boolean | Present and `true` when this response was served from an `Idempotency-Key`'s record rather than by running the model again. It carries the original call's status, body and content type, and it is not billed a second time - the charge settled when the original completed. The header is ABSENT on a fresh run rather than sent as `false`, so branch on its presence. | +| `Retry-After` | integer | Seconds to wait before retrying the SAME request with the SAME `Idempotency-Key`. It is set on the two answers such a retry can actually collect from: a `409` carrying `error_type: concurrency_limit_exceeded`, where the original call for that key is still running, and a `deadline_exceeded` `504`, where Comfy stopped holding the connection but still holds a handle to a generation the provider is running. In both cases the value is the interval Router itself would wait before asking again, which is the one honest number this route has for "ask again later". Absent when there is nothing to collect: an unkeyed call, a bound that expired before the provider accepted anything, or a `409` that refuses the key outright instead of asking the caller to wait. | +| `X-Comfy-Error-Type` | [`RouterErrorType`](#routererrortype) | Coarse, machine-readable bucket for the failure, set by Router on every error response. It carries the same value as `RouterErrorResponse.error_type`, and on the `422` it is the ONLY machine-readable bucket, because that body is the FastAPI `detail[]` shape and has no `error_type` field of its own. A client can therefore branch on this header alone, before deciding which of the two Router error bodies it received. | +| `X-Comfy-Request-Id` | string | Server-generated identifier for this call, present on EVERY Router response - success, 4xx and 5xx alike, because an error response is exactly when a user needs an id to quote in a support request. The SAME value is written into the call's usage/audit event, which is what lets a complaint about a charge be joined to the charge itself instead of searched for by timestamp. | +| `X-Committed-Spend-Current` | integer | The USD cents the caller currently has committed to calls still in flight. On a `429` this EXCLUDES the refused call, whose commitment was rolled back before the refusal was sent; on an admitted response it INCLUDES the call being answered. Present alongside `X-Committed-Spend-Limit`. | +| `X-Committed-Spend-Limit` | integer | The ceiling, in USD cents, on the partner spend the caller may have committed to calls still in flight - money held from the moment a call is admitted and released when that call finishes. It is not a budget, a balance, or any running total of what the caller has spent to date: settling an invoice frees no room under it, and letting an in-flight call finish does. Contrast `X-Concurrency-Limit`, which bounds those same in-flight calls counted as a NUMBER OF CALLS rather than priced. How the ceiling is SIZED is a separate question from what it measures, and it is not tier-independent: the ceiling moves with the account's lifetime paid spend, off the same thresholds the concurrent-call tier uses, so paying more raises it - see [partner-node concurrency limits](https://docs.comfy.org/tutorials/partner-nodes/concurrency-limits) for that ladder and for the concurrent-call bound that shares this `429`. Present on BOTH outcomes of an enforcing committed-spend gate - the `429` it raises and the success it admits - and absent while the gate is not enforcing, when it declines to decide and lets the call through, or on a `429` raised by the concurrent-call pool instead (a committed-spend `429` carries this trio and drops `X-Concurrency-*`). | +| `X-Committed-Spend-Remaining` | integer | The USD cents of headroom left under the ceiling, floored at zero. It can be positive on a refusal: the refused call cost more than what was left, and a cheaper call would still be admitted. Present alongside `X-Committed-Spend-Limit`. | + +## Result assets + +A model that produces an image, a video or an audio file answers with a link to it, with the bytes inline, or with both, and Router forwards whichever the partner sent on the partner's own field. What is not always the partner's is where a link POINTS: for the models below, Router copies the asset onto Comfy storage while it answers your call and the link you receive is a Comfy-hosted one. Which models those are is a property of the model, not of anything you send or of any header you set. + +| Models | What is copied onto Comfy storage | The Comfy-hosted link is valid for | +| --- | --- | --- | +| `bfl/*` | the finished asset, and the draft-cache asset when the result carries one | 24 hours | +| `byteplus/*` video models (`seedance`, `dreamina-seedance`) | the finished video, and the last-frame image when the result carries one | 24 hours | +| `minimax/*` | the finished video | 12 hours | +| `xai/*` | every generated image, and the finished video | 24 hours | + +The clock starts when Comfy copies the asset, which happens while your call is being answered - not when you read the response, and not when you first follow the link. Each row names what is copied for that provider, and a result a row does not name is not copied: a `byteplus/seedream-*` or `byteplus/seededit-*` image is returned the way everything below is. + +**Every other model in the catalog returns the partner's own asset reference, or the bytes inline, exactly as the partner produced it.** `veo/*` is the family worth naming here, because it is a video family like two of the rows in the table and does not behave like them: a Veo result carries its videos inline as base64 on `response.videos[].bytesBase64Encoded`, which is what Comfy's configuration of that partner produces, so the bytes are yours the moment you have the response and there is no link to expire. Where a model on no row above answers with a link instead, that link is the PARTNER's: it carries the partner's expiry, which is the partner's to set, is frequently much shorter than the values in the table, and is not a figure this contract states. Fetch the asset promptly rather than storing the URL and following it later. + +Copying is best effort and is decided per asset, never per response. An asset Comfy could not copy keeps the partner's own reference on THAT entry while every other entry of the same response keeps its Comfy-hosted link; the call still succeeds and is charged exactly as a fully copied one is, and nothing in the body marks which entry is which. So read the value you were given rather than inferring it from the model, and never read one Comfy-hosted link in a response as a promise about the entries beside it. + +Whether a result is Comfy-hosted also decides whether a completed call can still be replayed from its `Idempotency-Key` record later; the `Idempotency-Key` parameter above says what a retry is answered with when it cannot be. + +## Per-model input schemas + +A model's own input fields are not reproduced here. Read them live from `GET /v2/models/{provider}/{model}/openapi.json`, which serves the same document the server validates the call against, so what is published and what is enforced cannot drift apart. Take a model ID from `GET /v2/models`, append `/openapi.json` to its invocation path, and generate against the document you get back. + +## Schemas + +### RouterChargesOnPolicyRejection + +Whether a call this model REFUSES on content-policy grounds is nevertheless charged to the caller. Providers differ, the difference is invisible at call time, and a user who sees an error and a charge for the same call has no way to have known - so it is stated per model, before the call, rather than left to per-provider folklore. + +Type: `string` + +### RouterErrorResponse + +Router's request-level error body: what is returned when the request never reached the model, or failed for a reason the model itself did not report - auth, quota, an unknown model ID, or provider transport. A model-level validation failure has its own shape, `RouterValidationErrorResponse`, because flattening a FastAPI `detail[]` array into this `detail` string would destroy the per-field granularity an SDK branches on. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `detail` | string | yes | - | Human-readable description of the failure, safe to surface to an end user. Not machine-parsed - branch on `error_type` instead. | +| `error_type` | [`RouterErrorType`](#routererrortype) | yes | - | Coarse, machine-readable bucket for a Router failure, mirrored on the `X-Comfy-Error-Type` response header so a caller can branch without parsing the body. The set is closed at fifteen values: the six request-level buckets `invalid_input`, `content_policy_violation`, `provider_error`, `provider_timeout`, `insufficient_credits` and `model_not_found`, plus the transport-level `unauthorized`, `forbidden`, `concurrency_limit_exceeded`, `client_disconnected`, `internal_error`, `deadline_exceeded`, `not_enabled`, `service_unavailable` and `rate_limited`. | + +### RouterErrorType + +Coarse, machine-readable bucket for a Router failure, mirrored on the `X-Comfy-Error-Type` response header so a caller can branch without parsing the body. The set is closed at fifteen values: the six request-level buckets `invalid_input`, `content_policy_violation`, `provider_error`, `provider_timeout`, `insufficient_credits` and `model_not_found`, plus the transport-level `unauthorized`, `forbidden`, `concurrency_limit_exceeded`, `client_disconnected`, `internal_error`, `deadline_exceeded`, `not_enabled`, `service_unavailable` and `rate_limited`. + +Type: `string` + +### RouterModelBilling + +Per-model billing FACTS a caller needs before invoking - not prices. Usage and cost figures never appear here. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `charges_on_policy_rejection` | [`RouterChargesOnPolicyRejection`](#routerchargesonpolicyrejection) | yes | - | Whether a call this model REFUSES on content-policy grounds is nevertheless charged to the caller. Providers differ, the difference is invisible at call time, and a user who sees an error and a charge for the same call has no way to have known - so it is stated per model, before the call, rather than left to per-provider folklore. | + +### RouterModelDetail + +Per-model detail for one Comfy Router model: everything the catalog listing reports for it, plus the per-model fields that only the single-model route carries. + +Composes [`RouterModelListEntry`](#routermodellistentry), [`RouterModelDetailFields`](#routermodeldetailfields). + +Type: `object` + +### RouterModelDetailFields + +The half of `RouterModelDetail` the catalog listing does NOT carry: per-model fields worth one lookup but not worth repeating on every entry of a paginated catalog page. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `input_schema_url` | string | no | `format: uri`, `pattern: ^https://`, `maxLength: 2048` | Pointer to this model's input schema document - the description of the body `POST /v2/models/{provider}/{model}` accepts for this model. Only the POINTER is part of this contract: the document it addresses is authored separately. Absent when no schema has been authored for the model. | + +### RouterModelId + +A canonical Comfy Router model ID, `{provider}/{model}` - exactly the value that addresses the model on `POST /v2/models/{provider}/{model}`, so a caller can interpolate it into that path without re-deriving it from anything. Its `pattern` is `RouterProviderSegment` and `RouterModelSegment` joined by a single `/`, and `maxLength` is their sum plus that separator. + +Type: `string` -- `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*/[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 193` + +### RouterModelInput + +A partner model's native JSON input document, forwarded to the provider as-is. Its concrete shape is owned by the partner rather than by Comfy, so this is an open object: Router does not narrow, rename, or re-envelope the fields. It is a named component (never an inline anonymous object) because ComfyUI's spec-driven codegen needs a class to generate. + +Type: `object` + +### RouterModelInputSchemaDocument + +A standalone OpenAPI document describing ONE Comfy Router model's input AND output - the body `POST /v2/models/{provider}/{model}` accepts for that model, under the operation's `requestBody`, and the body it returns, under that operation's `200` content. It is what `GET /v2/models/{provider}/{model}/openapi.json` returns. The component keeps its historical name, which predates the output half; the shape it describes is the whole document, not the input alone. + +Type: `object` + +### RouterModelListEntry + +One entry in the Router model catalog: the identity of a runnable model, and nothing else. The per-model detail route composes this same entry rather than restating it, which is why the name is `...ListEntry` and not `...Summary` - there must be exactly one definition of what a catalog entry is. Per-model detail and the per-model input/output schemas are their own routes, so this shape stays the minimum a caller needs in order to invoke the model - deliberately, because this is the payload an SDK fetches on cold start. `id` is `provider` and `model` joined by `/`; the two fields are carried separately as well so a caller composes the invocation path without splitting a string. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `id` | [`RouterModelId`](#routermodelid) | yes | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*/[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 193` | A canonical Comfy Router model ID, `{provider}/{model}` - exactly the value that addresses the model on `POST /v2/models/{provider}/{model}`, so a caller can interpolate it into that path without re-deriving it from anything. Its `pattern` is `RouterProviderSegment` and `RouterModelSegment` joined by a single `/`, and `maxLength` is their sum plus that separator. | +| `provider` | [`RouterProviderSegment`](#routerprovidersegment) | yes | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 64` | Lowercase `provider` segment of the canonical `{provider}/{model}[/{variant}]` model ID - the partner whose model is being addressed. The invocation route's `provider` path parameter and a catalog entry's `provider` field both reference this one schema, which is what keeps the listed IDs and the accepted IDs from drifting apart. | +| `model` | [`RouterModelSegment`](#routermodelsegment) | yes | `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 128` | Lowercase `model` segment of the canonical `{provider}/{model}[/{variant}]` model ID - the model to run within that provider. Shared by the invocation route's `model` path parameter and a catalog entry's `model` field, for the same no-drift reason as `RouterProviderSegment`. | +| `billing` | [`RouterModelBilling`](#routermodelbilling) | yes | - | Per-model billing FACTS a caller needs before invoking - not prices. Usage and cost figures never appear here. | + +### RouterModelListResponse + +One page of the Router model catalog. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `data` | array of [`RouterModelListEntry`](#routermodellistentry) | yes | - | The models on this page, at most `limit` of them. | +| `has_more` | boolean | yes | - | Whether another page exists beyond this one. Keep walking while this is true; do not infer the end of the catalog from a short or empty `data`. | +| `next_cursor` | [`RouterPageCursor`](#routerpagecursor) | no | `pattern: ^[A-Za-z0-9._~+/=-]+$`, `minLength: 1`, `maxLength: 512` | An OPAQUE cursor into a Router list. It is produced by the server and only ever round-tripped: it is not an offset, not a model ID, not ordered, and not stable across catalog rebuilds, so parsing one, incrementing one, or persisting one beyond the walk it came from are all outside the contract. Cursor rather than offset because the catalog is a moving list - an offset walk silently skips or repeats entries when entries are added or removed mid-walk, and a caller cannot tell that it happened. | +| `limit` | integer | yes | `minimum: 1`, `maximum: 100` | The page size actually served. A requested `limit` above the maximum is CLAMPED down to the maximum rather than rejected, so this can be smaller than the value asked for - paginate with this number, not with the one you sent, or you will assume rows you never received. | + +### RouterModelOutput + +A partner model's native JSON output document, returned to the caller as-is. Its concrete shape is owned by the partner rather than by Comfy, so this is an open object: Router does not narrow, rename, or re-envelope the fields. It is a named component (never an inline anonymous object) because ComfyUI's spec-driven codegen needs a class to generate. For the concrete shape ONE model returns, read that model's own document at `GET /v2/models/{provider}/{model}/openapi.json`, whose `200` carries the per-model output schema when Comfy has described it. + +Type: `object` + +### RouterModelSegment + +Lowercase `model` segment of the canonical `{provider}/{model}[/{variant}]` model ID - the model to run within that provider. Shared by the invocation route's `model` path parameter and a catalog entry's `model` field, for the same no-drift reason as `RouterProviderSegment`. + +Type: `string` -- `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 128` + +### RouterPageCursor + +An OPAQUE cursor into a Router list. It is produced by the server and only ever round-tripped: it is not an offset, not a model ID, not ordered, and not stable across catalog rebuilds, so parsing one, incrementing one, or persisting one beyond the walk it came from are all outside the contract. Cursor rather than offset because the catalog is a moving list - an offset walk silently skips or repeats entries when entries are added or removed mid-walk, and a caller cannot tell that it happened. + +Type: `string` -- `pattern: ^[A-Za-z0-9._~+/=-]+$`, `minLength: 1`, `maxLength: 512` + +### RouterProviderSegment + +Lowercase `provider` segment of the canonical `{provider}/{model}[/{variant}]` model ID - the partner whose model is being addressed. The invocation route's `provider` path parameter and a catalog entry's `provider` field both reference this one schema, which is what keeps the listed IDs and the accepted IDs from drifting apart. + +Type: `string` -- `pattern: ^[a-z0-9]+([._-][a-z0-9]+)*$`, `maxLength: 64` + +### RouterValidationErrorContext + +The violated bound for one `RouterValidationErrorDetail`, carried from the provider verbatim - for example `{"limit_value": 8}` alongside `greater_than`, `{"min_width": 512}` alongside `image_too_small`, or `{"max_size_bytes": 10485760}` alongside `file_too_large`. The key set is specific to the provider and the error type, so this is deliberately an open object: narrowing it to a fixed field list, or folding it into the `msg` string, is precisely how a ported integration compiles and then silently loses the branch that read the bound. Absent when the error type carries no bound. + +Type: `object` + +### RouterValidationErrorDetail + +One model-level validation failure, in the FastAPI form. `type` carries the SPECIFIC provider reason - `value_error`, `missing`, `image_too_small`, `unsupported_audio_format`, `greater_than`, `file_too_large` and the rest - which is the granularity `RouterErrorType`'s coarse bucket cannot express. It is an open string and not an `enum` for the same reason: the provider vocabulary runs to roughly 48 values across two tiers and grows on the provider's release cycle, not ours, and an unmodelled value must reach the caller rather than fail deserialization. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `loc` | array of any | yes | - | Path to the offending field, outermost segment first - for example `["body", "image_url"]`, or `["body", "images", 0]` where an integer indexes into an array. | +| `msg` | string | yes | - | Human-readable description of this single failure. | +| `type` | string | yes | - | Specific, machine-readable reason for this failure, passed through from the provider unchanged. This is the value a typed SDK exception hierarchy branches on; `error_type` on the response header is only its coarse bucket. | +| `ctx` | [`RouterValidationErrorContext`](#routervalidationerrorcontext) | no | - | The violated bound for one `RouterValidationErrorDetail`, carried from the provider verbatim - for example `{"limit_value": 8}` alongside `greater_than`, `{"min_width": 512}` alongside `image_too_small`, or `{"max_size_bytes": 10485760}` alongside `file_too_large`. The key set is specific to the provider and the error type, so this is deliberately an open object: narrowing it to a fixed field list, or folding it into the `msg` string, is precisely how a ported integration compiles and then silently loses the branch that read the bound. Absent when the error type carries no bound. | +| `input` | [`RouterValidationErrorInput`](#routervalidationerrorinput) | no | - | The offending input value, echoed back verbatim so a caller can see what was rejected without re-deriving it from `loc`. Any JSON type - string, number, boolean, array, object or null - so this schema is deliberately left untyped rather than narrowed to an object. Absent when the provider does not echo the input back. | + +### RouterValidationErrorInput + +The offending input value, echoed back verbatim so a caller can see what was rejected without re-deriving it from `loc`. Any JSON type - string, number, boolean, array, object or null - so this schema is deliberately left untyped rather than narrowed to an object. Absent when the provider does not echo the input back. + +### RouterValidationErrorResponse + +Router's model-level `422` body, in the FastAPI form: the request was well-formed enough to reach the model and the model rejected its contents. Note it carries no `error_type` of its own - that is what `X-Comfy-Error-Type` on the response is for, so a client can read the coarse bucket off the header without first deciding which of the two Router error bodies it received. + +| Field | Type | Required | Constraints | Description | +| --- | --- | --- | --- | --- | +| `detail` | array of [`RouterValidationErrorDetail`](#routervalidationerrordetail) | yes | - | Every validation failure found on the request, one entry per offending field. | diff --git a/openapi-v2.yaml b/openapi-v2.yaml index 14fecb831..bc033dd0e 100644 --- a/openapi-v2.yaml +++ b/openapi-v2.yaml @@ -291,7 +291,7 @@ paths: summary: Asset bytes description: 'Serves the bytes directly on surfaces where the platform stores blobs - itself (self-hosted); on Comfy Cloud and serverless deployments issues a `302` to a + itself (self-hosted); on Cloud and serverless issues a `302` to a fresh signed URL. Range requests are supported for resumable @@ -322,7 +322,7 @@ paths: type: string format: binary '302': - description: Redirect to a fresh signed URL (Comfy Cloud / serverless deployments). + description: Redirect to a fresh signed URL (Cloud / serverless). headers: Location: schema: @@ -414,12 +414,18 @@ paths: additionalProperties: true extra_data: type: object - description: 'Per-prompt ComfyUI `extra_data`, same shape as Comfy Cloud and local ComfyUI. Closed object: only the enumerated keys are accepted, keeping the contract fully typed. Forwarded to the worker per-prompt, never persisted, and excluded from idempotency comparison.' + description: 'Per-prompt ComfyUI `extra_data`, same shape as Comfy Cloud and local ComfyUI. Closed object: only the enumerated keys are accepted, keeping the contract fully typed. Forwarded to the worker per-prompt and excluded from idempotency comparison. On a deployment it is dispatch-only and never stored; on Comfy Cloud it is persisted with the prompt, because the worker needs it, and redacted on every path that returns a workflow to a caller. + + + Send the one credential you hold: an API key as `api_key_comfy_org`, or the session token an interactively signed-in client has instead as `auth_token_comfy_org`. Sending both is accepted and both are forwarded, but it is not a supported combination and which one a node uses is not defined here. Note a session token is short-lived and is not re-minted for you, so one submitted long before it executes may expire in the queue.' additionalProperties: false properties: api_key_comfy_org: type: string description: API key for partner (API) nodes. + auth_token_comfy_org: + type: string + description: Session bearer token for partner (API) nodes — the equivalent of `api_key_comfy_org` for a caller authenticated by session rather than by key. responses: '201': description: Job created and queued. @@ -430,7 +436,7 @@ paths: '401': $ref: '#/components/responses/Unauthorized' '402': - description: '`insufficient_credits` (Comfy Cloud / serverless deployments only).' + description: '`insufficient_credits` (Cloud / serverless only).' content: application/json: schema: @@ -516,6 +522,134 @@ paths: $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/UpstreamError' + /api/v2/jobs/{id}/logs: + get: + operationId: getJobLogs + tags: + - jobs + summary: What the run printed + description: 'Returns the job''s captured execution log. Fetched on demand: a log + + is a debugging artifact a caller wants occasionally, while + + `GET /api/v2/jobs/{id}` is polled to terminal on every run, so the + + log is a resource of its own rather than a field that would ride + + every one of those polls to be read at most once. + + + Captured whenever the worker reports its own outcome, success and + + failure alike, since a job that succeeds while producing the wrong + + thing is exactly what a failure-only log cannot explain. A run the + + platform or the provider killed — out of memory, a crashed worker, a + + timeout, a job past its maximum runtime — never gets that far, so it + + reaches a terminal status carrying no log at all. That is a real gap + + and worth stating: the failures a caller most wants a log for are + + the ones least likely to have produced one. + + + **`204` is the normal answer for a job with no log**, and the cases + + behind it are deliberately not distinguished: this surface does not + + capture logs at all, the job has not finished, the job predates log + + capture, the run was killed before the worker could report one, + + capture was attempted and failed, or the job ran on the public demo + + deployment, which captures and stores the log like every other + + serverless deployment but withholds it on read, because that surface + + takes callers with no credential and a job id would otherwise be the + + only thing between one anonymous caller and another''s run. + + + Because a `204` never says which of those it is, do not branch on the + + reason — but do note that one of them resolves itself. A job that has + + not finished may have a log once it does, so a caller that wants one + + reads again after a terminal status. A `204` on a job already in a + + terminal state is final, and so is a missing `urls.logs`; both mean + + stop asking. + + + **Only jobs run on the serverless platform** (a + + `{deployment}.run.comfy.app` host) have one today. An implementation + + that captures no logs must still serve this operation, answering + + `204` for every job it can read, so that the two answers stay + + distinct — Comfy Cloud does. A self-hosted deployment on a build + + predating this operation has not implemented it yet and will answer + + a routing `404` instead, which is the case `job.urls.logs` exists to + + keep a client out of: its absence says the surface has no logs at + + all, without a request. + + + Tied to the job''s own retention: this `404`s under the same + + conditions `GET /api/v2/jobs/{id}` does (unknown, not-yours, or past + + its retention deadline). Nothing ages a log out ahead of the job''s + + own `expires_at`, so a job never outlives its log. + + + Live tailing is not offered here yet. When it is, it arrives on this + + same path under `Accept: text/event-stream`, leaving this + + JSON snapshot the default; its resume semantics will be defined + + then, against a capture that is incremental. Until then the SSE + + `log` event on `GET /api/v2/jobs/{id}/events` is the reserved live + + rail, and this is the authoritative snapshot it reconciles against. + + ' + parameters: + - $ref: '#/components/parameters/JobId' + responses: + '200': + description: The captured log. + content: + application/json: + schema: + $ref: '#/components/schemas/JobLogs' + '204': + description: This job has no log. A normal answer, not an error — see the description for the cases it covers. + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/UpstreamError' /api/v2/jobs/{id}/events: get: operationId: getJobEvents @@ -556,7 +690,7 @@ paths: description: 'Emitted the moment each output asset is committed, carrying the same `Output` object that appears on `job.outputs[]`. A latency optimization only: it lets a client render each result as it lands instead of waiting for the terminal `status` event. It is delivered best-effort over the live broadcast path — an output whose durable asset record is not yet resolvable when its node finishes may be delivered on a slightly later event or, failing that, only in the terminal `status` snapshot — so the authoritative, complete set of outputs is always `job.outputs[]` on `GET /api/v2/jobs/{id}` and on the terminal `status` event. A client must therefore treat these as additive hints and must not assume it receives one per output.' schema: '#/components/schemas/Output' log: - description: 'Selected execution log lines. Best-effort diagnostics. Its snapshot equivalent is `job.logs` on `GET /api/v2/jobs/{id}`, which carries the whole log the run produced, read back once the run has finished; this event is the live view of that same output, carrying lines while the run is still going. NOT YET EMITTED by the server in the first iteration — reserved in the catalog so the wire contract is stable. Clients must not depend on receiving this event yet: to get a log today, stream to a terminal status and re-read the job.' + description: 'Selected execution log lines, carried while the run is still going. Best-effort diagnostics, and lossy by the same rule as the rest of this stream: lines emitted while a client was disconnected are gone and no `Last-Event-ID` replays them. The authoritative, complete log is the snapshot at `GET /api/v2/jobs/{id}/logs`, which a client re-reads after a terminal status to reconcile whatever it missed — on a surface that captures logs at all. Comfy Cloud does not, and answers `204` there for every job, so this event has nothing to be the live view of; see that operation for what a self-hosted deployment answers. NOT YET EMITTED by the server in the first iteration — reserved in the catalog so the wire contract is stable. Clients must not depend on receiving this event yet: to get a log today, stream to a terminal status and read the snapshot.' x-sse-not-yet-emitted: true schema: '#/components/schemas/LogEvent' parameters: @@ -604,7 +738,7 @@ paths: already-terminal. Idempotent: canceling a finished job is a no-op - returning the terminal state. On serverless deployments, GPU seconds consumed + returning the terminal state. On serverless, GPU seconds consumed before the interrupt takes effect are still billed. @@ -634,7 +768,7 @@ components: bearerAuth: type: http scheme: bearer - description: '`Authorization: Bearer ` — account-scoped API keys on Comfy Cloud and serverless deployments. Self-hosted accepts unauthenticated requests by default and can be configured with a static bearer token.' + description: '`Authorization: Bearer ` — account-scoped API keys on Cloud and serverless. Self-hosted accepts unauthenticated requests by default and can be configured with a static bearer token.' parameters: IdempotencyKey: name: Idempotency-Key @@ -810,10 +944,6 @@ components: allOf: - $ref: '#/components/schemas/JobError' nullable: true - logs: - allOf: - - $ref: '#/components/schemas/JobLogs' - description: 'What the run printed. **Only jobs run on a Developer Platform serverless deployment** (a `{deployment}.run.comfy.app` host) carry it. Comfy Cloud and self-hosted callers never receive it, so on those surfaces the field is always absent and a client should not wait for one. Where it is populated it is captured for every job, success and failure alike, since a job that succeeds while producing the wrong thing is exactly what a failure-only log cannot explain. It lives as long as the job it belongs to: nothing ages it out ahead of the job''s own `expires_at`, so a job never outlives its log. **Absent, not null**, when there is none: the surface does not populate it at all, the job has not finished, the job predates log capture, or the job ran on the public demo deployment, which captures and stores the log like every other serverless deployment but withholds it on read, because that surface takes callers with no credential and a job id would otherwise be the only thing between one anonymous caller and another''s run. Those cases are deliberately not distinguished, because a caller''s next action is the same in all of them, which is to stop expecting a log. Returned by `GET /api/v2/jobs/{id}` only. It is deliberately absent from the job object on `POST /api/v2/jobs`, on `POST /api/v2/jobs/{id}/cancel`, and on the SSE `status` event: the last is pushed on every transition to every open stream, and a log on each frame would pay for the whole thing repeatedly to deliver it once. A client that streams to a terminal status and wants the log re-reads the job.' metrics: type: object description: 'Values are nullable (a metric not yet available — e.g. `execution_ms` before a job starts running — is `null`, not omitted); the example below is deliberately all-non-null purely to work around a Spectral/nimma lint-tooling crash on a literal `null` inside a schema `example` combined with `additionalProperties.nullable: true` — the schema itself is unchanged and still allows null values at runtime.' @@ -827,22 +957,26 @@ components: $ref: '#/components/schemas/JobUrls' JobLogs: type: object - description: 'A job''s captured execution log. Diagnostics, not a contract on content: this is whatever the workflow''s own code and nodes wrote to standard output, in the order they wrote it, so nothing about its shape is stable between runs or between releases of a build. It is **untrusted text** — a workflow chooses what goes in it — and must be rendered as plain text rather than interpreted.' + description: 'A job''s captured execution log — the body of `GET /api/v2/jobs/{id}/logs`. Diagnostics, not a contract on content: this is whatever the workflow''s own code and nodes wrote to standard output, in the order they wrote it, so nothing about its shape is stable between runs or between releases of a build. It is **untrusted text** — a workflow chooses what goes in it — and must be rendered as plain text rather than interpreted.' required: - text - truncated - captured_at + - complete properties: text: type: string description: The captured output. truncated: type: boolean - description: '`text` is the TAIL of a longer run. Implementations bound what they capture and store, so a workflow that prints megabytes keeps its last lines — where a failure normally is — instead of being dropped whole. True with an empty `text` means the log was captured and then shed entirely to fit.' + description: 'The BEGINNING of the captured output was discarded — `text` is the TAIL of a longer run. Implementations bound what they capture and store, so a workflow that prints megabytes keeps its last lines, where a failure normally is, instead of being dropped whole. True with an empty `text` means the log was captured and then shed entirely to fit. This describes the stored log, never the response: it does not mean a caller asked for part of one.' captured_at: type: string format: date-time description: When the run's output was read back off the worker. + complete: + type: boolean + description: No further output will be appended to this log. Always `true` today, because a log is read back off the worker once, when the run ends, so a log that exists is already whole. Sent so that a surface which later captures output while a run is still going can say so, and a client written now against `false` keeps working when it does. `false` does not promise that more output will arrive, only that this snapshot may not be the last one. JobWorkflowResponse: type: object description: The workflow behind a job. See GET /api/v2/jobs/{id}/workflow's description for exactly when `format` is `save` vs `api`. @@ -894,6 +1028,12 @@ components: cancel: type: string format: uri-reference + logs: + type: string + format: uri-reference + description: 'Where to read what this run printed. Present on any surface that captures execution logs, which is why it is the one link here that is optional: absent means this surface captures none, for any job, so a client can stop looking without spending a request on an answer it already has. + + Follow this link rather than building the path from the job id. The two are not interchangeable: a surface may be mounted under a prefix this link already carries and a hand-built path would not, and a surface that does not implement the operation at all answers a routing `404` — indistinguishable, to the client, from the `404` that means the job itself is gone. Present does NOT mean this job has a log, and it is deliberately not a signal about one: a surface that captures logs offers the link on every job, including those it will answer `204` for and those whose log it withholds. Read the log, not the link.' Progress: type: object description: Server-computed progress snapshot (node-count and sampler-step weighted). Complete per snapshot — one fully re-syncs a client. @@ -951,6 +1091,7 @@ components: properties: node_id: type: string + description: The workflow node that reported this file; empty when the worker named none. example: '9' name: type: string diff --git a/router-schemas/anthropic/claude-fable-5.json b/router-schemas/anthropic/claude-fable-5.json new file mode 100644 index 000000000..ba2fd23be --- /dev/null +++ b/router-schemas/anthropic/claude-fable-5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-fable-5","description":"The request body Comfy Router accepts for the model \"anthropic/claude-fable-5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-fable-5":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-fable-5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-fable-5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-haiku-4-5-20251001.json b/router-schemas/anthropic/claude-haiku-4-5-20251001.json new file mode 100644 index 000000000..f444175fc --- /dev/null +++ b/router-schemas/anthropic/claude-haiku-4-5-20251001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-haiku-4-5-20251001","description":"The request body Comfy Router accepts for the model \"anthropic/claude-haiku-4-5-20251001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-haiku-4-5-20251001":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-haiku-4-5-20251001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-haiku-4-5-20251001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-opus-4-6.json b/router-schemas/anthropic/claude-opus-4-6.json new file mode 100644 index 000000000..e743a2959 --- /dev/null +++ b/router-schemas/anthropic/claude-opus-4-6.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-opus-4-6","description":"The request body Comfy Router accepts for the model \"anthropic/claude-opus-4-6\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-opus-4-6":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-opus-4-6 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-opus-4-6","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-opus-4-7.json b/router-schemas/anthropic/claude-opus-4-7.json new file mode 100644 index 000000000..74c54bd0f --- /dev/null +++ b/router-schemas/anthropic/claude-opus-4-7.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-opus-4-7","description":"The request body Comfy Router accepts for the model \"anthropic/claude-opus-4-7\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-opus-4-7":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-opus-4-7 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-opus-4-7","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-opus-4-8.json b/router-schemas/anthropic/claude-opus-4-8.json new file mode 100644 index 000000000..395c1e14d --- /dev/null +++ b/router-schemas/anthropic/claude-opus-4-8.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-opus-4-8","description":"The request body Comfy Router accepts for the model \"anthropic/claude-opus-4-8\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-opus-4-8":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-opus-4-8 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-opus-4-8","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-opus-5.json b/router-schemas/anthropic/claude-opus-5.json new file mode 100644 index 000000000..ab0dd9e79 --- /dev/null +++ b/router-schemas/anthropic/claude-opus-5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-opus-5","description":"The request body Comfy Router accepts for the model \"anthropic/claude-opus-5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-opus-5":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-opus-5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-opus-5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-sonnet-4-5-20250929.json b/router-schemas/anthropic/claude-sonnet-4-5-20250929.json new file mode 100644 index 000000000..b21d82d23 --- /dev/null +++ b/router-schemas/anthropic/claude-sonnet-4-5-20250929.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-sonnet-4-5-20250929","description":"The request body Comfy Router accepts for the model \"anthropic/claude-sonnet-4-5-20250929\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-sonnet-4-5-20250929":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-sonnet-4-5-20250929 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-sonnet-4-5-20250929","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-sonnet-4-6.json b/router-schemas/anthropic/claude-sonnet-4-6.json new file mode 100644 index 000000000..0bfca414a --- /dev/null +++ b/router-schemas/anthropic/claude-sonnet-4-6.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-sonnet-4-6","description":"The request body Comfy Router accepts for the model \"anthropic/claude-sonnet-4-6\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-sonnet-4-6":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-sonnet-4-6 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-sonnet-4-6","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/anthropic/claude-sonnet-5.json b/router-schemas/anthropic/claude-sonnet-5.json new file mode 100644 index 000000000..b91bb9d9f --- /dev/null +++ b/router-schemas/anthropic/claude-sonnet-5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"anthropic/claude-sonnet-5","description":"The request body Comfy Router accepts for the model \"anthropic/claude-sonnet-5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f7ffd6c12415"},"paths":{"/v2/models/anthropic/claude-sonnet-5":{"post":{"operationId":"runRouterModel","summary":"Run anthropic/claude-sonnet-5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnthropicCreateMessageResponse"},{"properties":{"content":{"description":"The reply's content blocks, in order. Present on every completed message; empty when the turn produced nothing.","items":{"additionalProperties":true,"description":"One Messages API content block. `type` names the block's kind and decides which sibling fields it carries; unknown kinds pass through unchanged.","properties":{"text":{"description":"The block's text. Present on `text` blocks and absent on every other kind.","type":"string"},"type":{"description":"The block kind. `text` is the one that carries `text`; `thinking`, `redacted_thinking`, `tool_use`, `server_tool_use` and the tool-result blocks are the others Anthropic sends today, and the list is open.","type":"string"}},"type":"object"},"type":"array"}},"required":["content"],"type":"object"}],"description":"Comfy Router output schema for the Anthropic Claude models: the non-streaming Messages API document `POST /proxy/anthropic/v1/messages` answers with, forwarded unchanged. The operation is DIRECT RETURN — `routerresult/classification.go` records `{provider: anthropic, endpoint: /v1/messages}` as `ReturnModeDirect` with no poll route, because `messagesProxy`'s `ModifyResponse` reads `usage` off the finished body and meters it synchronously through `TrackUsage` — so the body a caller receives is the completed message on the original call, never a task handle.\nRouter CAPTURES rather than streams. `stream` is a settled field for this operation (`routerSettledBoolFields`, server/middleware): a Router request naming `stream` is forwarded with it set to FALSE, so a Router caller always receives this single JSON document and never the `text/event-stream` of message events the `/proxy/` route serves when `stream: true`. Send `stream` false or omit it; the `text/event-stream` half of the `/proxy/` operation's 200 has no Router counterpart.\nThe reply text is at `content[].text`. `content` is the Messages API block list and it is ALWAYS present on a completed message, but it is not uniformly text: a block carries `type`, and only `text` blocks carry `text`. `thinking`, `tool_use` and the server-tool blocks are legitimate members that carry no `text` at all, so a caller must select on `type` rather than read `content[0].text` — and an empty `content` is what a request that generated nothing looks like. The sibling top-level fields are NOT the result: `stop_reason` and `usage` are populated on a turn that emitted no content, and `model` is an echo of the request.","example":{"content":[{"text":"ok","type":"text"}],"id":"msg_01ExampleInvalidPlaceholder","model":"claude-haiku-4-5-20251001","role":"assistant","stop_reason":"end_turn","stop_sequence":null,"type":"message","usage":{"input_tokens":16,"output_tokens":3}}}}}}}}}},"components":{"schemas":{"AnthropicCacheCreationUsage":{"description":"Per-TTL breakdown of cache-write input tokens for an Anthropic Messages API call.","properties":{"ephemeral_1h_input_tokens":{"type":"integer"},"ephemeral_5m_input_tokens":{"type":"integer"}},"type":"object"},"AnthropicCreateMessageResponse":{"additionalProperties":true,"description":"JSON shape of a non-streaming Messages API response. Most fields pass through; the proxy reads `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"role":{"type":"string"},"stop_reason":{"nullable":true,"type":"string"},"stop_sequence":{"nullable":true,"type":"string"},"type":{"type":"string"},"usage":{"$ref":"#/components/schemas/AnthropicMessagesUsage"}},"type":"object"},"AnthropicMessagesUsage":{"description":"Token usage for an Anthropic Messages API call.","properties":{"cache_creation":{"$ref":"#/components/schemas/AnthropicCacheCreationUsage"},"cache_creation_input_tokens":{"type":"integer"},"cache_read_input_tokens":{"type":"integer"},"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"anthropic/claude-sonnet-5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/beeble/switchx.json b/router-schemas/beeble/switchx.json new file mode 100644 index 000000000..8bafb3188 --- /dev/null +++ b/router-schemas/beeble/switchx.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"beeble/switchx","description":"The request body Comfy Router accepts for the model \"beeble/switchx\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"2fcbc670fe85"},"paths":{"/v2/models/beeble/switchx":{"post":{"operationId":"runRouterModel","summary":"Run beeble/switchx synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BeebleSwitchXStatusResponse"}],"description":"Comfy Router output schema for Beeble SwitchX: the terminal `GET /v1/switchx/generations/{job_id}` status document, forwarded unchanged. The operation is submit-and-poll — `routerresult/classification.go` records `{provider: beeble, endpoint: /v1/switchx/generations}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished status document (`status: completed`) rather than the `{id, status}` handle the underlying submit answers with.\nThe relit composite is at `output.render`. The sibling fields are NOT interchangeable with it and a caller should not treat them as the result: `source` is the caller's own input echoed back after preprocessing, and `alpha` is the alpha matte — which, when `alpha_mode` is `custom` or `select`, is the caller's own `alpha_uri`. Either can therefore be present on a job that composited nothing, which is why `routerpollstate.classifyBeeble` keys success on `output.render` ALONE and why the nightly SDK case for this family asserts the dotted path `output.render` rather than the `output` container (`testing/e2e/router_sdk/cases.json`).\n`output` is absent or null until the job reaches `completed`. A caller polling this document directly must key completion off `output.render` rather than off a `200` alone; through Router that distinction is already settled, because a `completed` job carrying no `render` is answered as a Comfy Router error rather than with the provider document.\nThe URLs are SIGNED and expire roughly 72 hours after the job completes, so download them promptly rather than storing them; re-fetching the status document mints fresh ones. `status` is Beeble's own lowercase vocabulary (`in_queue`, `processing`, `completed`, `failed`), forwarded unchanged and compared without a case fold.","example":{"alpha_mode":"auto","completed_at":"2027-01-01T00:01:04Z","created_at":"2027-01-01T00:00:00Z","generation_type":"image","id":"swx_1a2b3c4d5e6f7a8b","modified_at":"2027-01-01T00:01:04Z","output":{"alpha":"https://example.invalid/beeble/switchx/alpha.png","render":"https://example.invalid/beeble/switchx/render.png","source":"https://example.invalid/beeble/switchx/source.png"},"progress":100,"status":"completed"}}}}}}}}},"components":{"schemas":{"BeebleSwitchXOutputUrls":{"description":"Signed URLs for SwitchX job outputs.","properties":{"alpha":{"description":"Alpha matte URL.","nullable":true,"type":"string"},"render":{"description":"Composited output URL.","nullable":true,"type":"string"},"source":{"description":"Preprocessed source URL.","nullable":true,"type":"string"}},"type":"object"},"BeebleSwitchXStatusResponse":{"description":"Status response for a SwitchX job.","properties":{"alpha_mode":{"description":"auto, fill, custom, or select","nullable":true,"type":"string"},"completed_at":{"description":"ISO 8601 timestamp when the job completed or failed.","nullable":true,"type":"string"},"created_at":{"description":"ISO 8601 timestamp when the job was created.","nullable":true,"type":"string"},"error":{"description":"Error message (present when status is failed).","nullable":true,"type":"string"},"generation_type":{"description":"image or video","nullable":true,"type":"string"},"id":{"description":"Job identifier (swx_...)","type":"string"},"modified_at":{"description":"ISO 8601 timestamp of the last status change.","nullable":true,"type":"string"},"output":{"allOf":[{"$ref":"#/components/schemas/BeebleSwitchXOutputUrls"}],"description":"Output URLs (present when status is completed). URLs are signed and expire after 72 hours; re-fetch this endpoint for fresh URLs.","nullable":true},"progress":{"description":"Progress percentage (0-100).","nullable":true,"type":"integer"},"status":{"description":"Current job status.","enum":["in_queue","processing","completed","failed"],"type":"string"},"webhook":{"allOf":[{"$ref":"#/components/schemas/BeebleWebhookStatus"}],"description":"Webhook delivery status (present only when callback_url was provided).","nullable":true}},"required":["id","status"],"type":"object"},"BeebleWebhookStatus":{"description":"Webhook delivery status for a SwitchX job.","properties":{"attempts":{"description":"Number of delivery attempts so far.","nullable":true,"type":"integer"},"last_error":{"description":"Error message from the last failed delivery attempt.","nullable":true,"type":"string"},"status":{"description":"pending, delivered, or failed","nullable":true,"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"beeble/switchx","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/erase-v1.json b/router-schemas/bfl/erase-v1.json new file mode 100644 index 000000000..6902e59eb --- /dev/null +++ b/router-schemas/bfl/erase-v1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/erase-v1","description":"The request body Comfy Router accepts for the model \"bfl/erase-v1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/erase-v1":{"post":{"operationId":"runRouterModel","summary":"Run bfl/erase-v1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/erase-v1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-2-max.json b/router-schemas/bfl/flux-2-max.json new file mode 100644 index 000000000..45b0c1b0b --- /dev/null +++ b/router-schemas/bfl/flux-2-max.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-2-max","description":"The request body Comfy Router accepts for the model \"bfl/flux-2-max\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/flux-2-max":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-2-max synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-2-max","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-2-pro.json b/router-schemas/bfl/flux-2-pro.json new file mode 100644 index 000000000..6931b903a --- /dev/null +++ b/router-schemas/bfl/flux-2-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-2-pro","description":"The request body Comfy Router accepts for the model \"bfl/flux-2-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/flux-2-pro":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-2-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-2-pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-3-video.json b/router-schemas/bfl/flux-3-video.json new file mode 100644 index 000000000..5e20e31f1 --- /dev/null +++ b/router-schemas/bfl/flux-3-video.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-3-video","description":"The request body Comfy Router accepts for the model \"bfl/flux-3-video\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"2e35f97dccb0"},"paths":{"/v2/models/bfl/flux-3-video":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-3-video synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":false,"description":"Request body for BFL FLUX 3 video generation. The mode field selects the generation type and decides which fields are required: t2v needs prompt; i2v needs prompt and keyframes; v2v needs prompt and start_video; draft_enhance needs draft_cache and accepts no other generation parameters.","example":{"mode":"t2v","prompt":"A slow dolly shot through a rain-soaked neon street at night"},"properties":{"aspect_ratio":{"default":"auto","description":"Output aspect ratio: auto, 21:9, 2:1, 16:9, 4:3, 1:1, 3:4, or 9:16. auto lets BFL choose from the prompt and any references.","type":"string"},"draft":{"default":false,"description":"Draft mode: generate a fast preview whose result includes a draft_cache download URL. Send that bundle back with mode draft_enhance to render the full-quality version of the same generation.","type":"boolean"},"draft_cache":{"description":"draft_enhance only. Encrypted draft-cache bundle from a prior draft generation, as the base64-encoded downloaded bundle or its still-valid http(s) URL. The original inputs are embedded in the bundle.","type":"string"},"duration":{"default":"auto","description":"Video duration in seconds (any whole second from 5 to 20), or auto to fit the content.","oneOf":[{"maximum":20,"minimum":5,"type":"integer"},{"type":"string"}]},"generate_audio":{"default":true,"description":"Generate synchronized audio alongside the video.","type":"boolean"},"keyframes":{"description":"i2v only. Images that become frames of the video, each an http(s) URL or base64, one to ten total. Accepts a single image, a list of images (one starts the video, two start and end it, more spread evenly and need a set duration), or timestamped [seconds, image] pairs in time order, e.g. [[0, \"...\"], [3.5, \"...\"]]."},"mode":{"description":"Generation mode: t2v (text-to-video), i2v (image-continuation), v2v (video-continuation), or draft_enhance (full-quality render of a prior draft). Spelled-out aliases such as text-to-video are accepted.","type":"string"},"prompt":{"description":"Free-form prompt describing the video. Required for every mode except draft_enhance.","type":"string"},"resolution":{"default":"hd","description":"Video resolution class: hd, or fhd for a higher-resolution result finished by the video upsampler. Exact dimensions vary with the aspect ratio.","type":"string"},"safety_tolerance":{"default":2,"description":"Tolerance level for input and output harm moderation, 0 strictest. Sexual content is limited to level 3 and hate content to level 2 regardless of the requested tolerance; requests with conditioning media are limited to level 2.","maximum":4,"minimum":0,"type":"integer"},"start_video":{"description":"v2v only. The video to continue, an http(s) URL or base64 MP4; the generated clip carries on from its final frames.","type":"string"},"version":{"default":"latest","description":"Endpoint version. latest serves the current release; dated pinnable release tags are added as they are published.","type":"string"}},"required":["mode"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"description":"Comfy Router output schema for `bfl/flux-3-video`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted result leaf described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/v1/flux-3-video` submit returns.\nThe result leaf DEPENDS ON THE MODE. The default mode returns `result.sample`; the `draft: true` mode returns `result.draft_cache` and no `sample`. Both are legitimate successes — Router's own poll classifier terminates on either — so `result` is an `anyOf` over the two rather than requiring `sample`. Branch on which key is present rather than on which is absent: `anyOf` asks for at least one leaf, so a document that carried both would still validate.\nBoth leaves are COMFY-HOSTED signed URLs, valid for up to 24 hours: Router re-hosts each asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own roughly two-hour delivery URL. Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum, for the reason `BFLRouterResultOutput` documents: Router forwards this field exactly as BFL spelled it and the classifier case-folds it before comparing.","example":{"cost":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","progress":null,"result":{"cost":null,"draft_cache":"https://example.invalid/bfl/flux-3-video/draft.mp4"},"status":"Ready"},"properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"anyOf":[{"required":["sample"]},{"required":["draft_cache"]}],"description":"The finished generation. Exactly one of the two URL leaves is populated: `sample` in the default mode, `draft_cache` in `draft: true` mode.","properties":{"cost":{"description":"Provider-reported task cost. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"draft_cache":{"description":"Signed URL returned INSTEAD of `sample` by the `draft: true` mode, re-hosted onto Comfy storage the same way `sample` is: normally a Comfy-hosted URL valid for up to 24 hours, and BFL's own roughly two-hour delivery URL when the re-host could not be performed.","format":"uri","type":"string"},"sample":{"description":"Signed URL for the generated MP4. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own roughly two-hour delivery URL instead. Absent in `draft: true` mode.","format":"uri","type":"string"}},"type":"object"},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found. Compare case-insensitively; Router forwards BFL's spelling unchanged.","type":"string"}},"required":["id","status","result"],"type":"object"}}}}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-3-video","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-kontext-max.json b/router-schemas/bfl/flux-kontext-max.json new file mode 100644 index 000000000..8a58adaee --- /dev/null +++ b/router-schemas/bfl/flux-kontext-max.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-kontext-max","description":"The request body Comfy Router accepts for the model \"bfl/flux-kontext-max\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1c1aa8006086"},"paths":{"/v2/models/bfl/flux-kontext-max":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-kontext-max synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Request body for the BFL FLUX.1 Kontext [max] API. Edits input_image when one is supplied; generates from the prompt alone when it is not.","example":{"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water"},"properties":{"aspect_ratio":{"description":"Aspect ratio of the output between 21:9 and 9:21, e.g. 16:9. Defaults to the input image's aspect ratio when one is given, otherwise 1:1.","example":"16:9","type":"string"},"input_image":{"description":"Image to edit, as a base64-encoded image or an http(s) URL. Optional; without it the model generates from the prompt alone.","type":"string"},"input_image_2":{"description":"Additional reference image, base64-encoded or an http(s) URL (experimental multi-reference).","type":"string"},"input_image_3":{"description":"Additional reference image, base64-encoded or an http(s) URL (experimental multi-reference).","type":"string"},"input_image_4":{"description":"Additional reference image, base64-encoded or an http(s) URL (experimental multi-reference).","type":"string"},"output_format":{"default":"png","description":"Output image format.","enum":["jpeg","png","webp"],"type":"string"},"prompt":{"description":"Text prompt describing the edit to apply to input_image, or the image to generate when no input_image is given.","type":"string"},"prompt_upsampling":{"default":false,"description":"Whether to upsample the prompt. If active, the prompt is automatically modified for more creative generation.","type":"boolean"},"safety_tolerance":{"default":2,"description":"Tolerance level for input and output moderation, between 0 (most strict) and 6 (least strict).","maximum":6,"minimum":0,"type":"integer"},"seed":{"description":"Optional seed for reproducibility. A random seed is used when omitted.","example":42,"type":"integer"},"webhook_secret":{"description":"Optional secret for webhook signature verification.","type":"string"},"webhook_url":{"description":"URL to receive webhook notifications.","format":"uri","maxLength":2083,"minLength":1,"type":"string"}},"required":["prompt"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-kontext-max","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-kontext-pro.json b/router-schemas/bfl/flux-kontext-pro.json new file mode 100644 index 000000000..69d9dcfe6 --- /dev/null +++ b/router-schemas/bfl/flux-kontext-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-kontext-pro","description":"The request body Comfy Router accepts for the model \"bfl/flux-kontext-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"bf73cde9674c"},"paths":{"/v2/models/bfl/flux-kontext-pro":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-kontext-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Request body for the BFL FLUX.1 Kontext [pro] API. Edits input_image when one is supplied; generates from the prompt alone when it is not.","example":{"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water"},"properties":{"aspect_ratio":{"description":"Aspect ratio of the output between 21:9 and 9:21, e.g. 16:9. Defaults to the input image's aspect ratio when one is given, otherwise 1:1.","example":"16:9","type":"string"},"input_image":{"description":"Image to edit, as a base64-encoded image or an http(s) URL. Optional; without it the model generates from the prompt alone.","type":"string"},"input_image_2":{"description":"Additional reference image, base64-encoded or an http(s) URL (experimental multi-reference).","type":"string"},"input_image_3":{"description":"Additional reference image, base64-encoded or an http(s) URL (experimental multi-reference).","type":"string"},"input_image_4":{"description":"Additional reference image, base64-encoded or an http(s) URL (experimental multi-reference).","type":"string"},"output_format":{"default":"png","description":"Output image format.","enum":["jpeg","png","webp"],"type":"string"},"prompt":{"description":"Text prompt describing the edit to apply to input_image, or the image to generate when no input_image is given.","type":"string"},"prompt_upsampling":{"default":false,"description":"Whether to upsample the prompt. If active, the prompt is automatically modified for more creative generation.","type":"boolean"},"safety_tolerance":{"default":2,"description":"Tolerance level for input and output moderation, between 0 (most strict) and 6 (least strict).","maximum":6,"minimum":0,"type":"integer"},"seed":{"description":"Optional seed for reproducibility. A random seed is used when omitted.","example":42,"type":"integer"},"webhook_secret":{"description":"Optional secret for webhook signature verification.","type":"string"},"webhook_url":{"description":"URL to receive webhook notifications.","format":"uri","maxLength":2083,"minLength":1,"type":"string"}},"required":["prompt"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-kontext-pro","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-pro-1.0-canny.json b/router-schemas/bfl/flux-pro-1.0-canny.json new file mode 100644 index 000000000..d65c65c06 --- /dev/null +++ b/router-schemas/bfl/flux-pro-1.0-canny.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-pro-1.0-canny","description":"The request body Comfy Router accepts for the model \"bfl/flux-pro-1.0-canny\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/flux-pro-1.0-canny":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-pro-1.0-canny synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-pro-1.0-canny","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-pro-1.0-depth.json b/router-schemas/bfl/flux-pro-1.0-depth.json new file mode 100644 index 000000000..16f2bc4f4 --- /dev/null +++ b/router-schemas/bfl/flux-pro-1.0-depth.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-pro-1.0-depth","description":"The request body Comfy Router accepts for the model \"bfl/flux-pro-1.0-depth\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/flux-pro-1.0-depth":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-pro-1.0-depth synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-pro-1.0-depth","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-pro-1.0-expand.json b/router-schemas/bfl/flux-pro-1.0-expand.json new file mode 100644 index 000000000..b1a2174fd --- /dev/null +++ b/router-schemas/bfl/flux-pro-1.0-expand.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-pro-1.0-expand","description":"The request body Comfy Router accepts for the model \"bfl/flux-pro-1.0-expand\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/flux-pro-1.0-expand":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-pro-1.0-expand synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-pro-1.0-expand","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-pro-1.0-fill.json b/router-schemas/bfl/flux-pro-1.0-fill.json new file mode 100644 index 000000000..e15d5f75d --- /dev/null +++ b/router-schemas/bfl/flux-pro-1.0-fill.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-pro-1.0-fill","description":"The request body Comfy Router accepts for the model \"bfl/flux-pro-1.0-fill\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/flux-pro-1.0-fill":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-pro-1.0-fill synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-pro-1.0-fill","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-pro-1.1-ultra.json b/router-schemas/bfl/flux-pro-1.1-ultra.json new file mode 100644 index 000000000..b7be53bb9 --- /dev/null +++ b/router-schemas/bfl/flux-pro-1.1-ultra.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-pro-1.1-ultra","description":"The request body Comfy Router accepts for the model \"bfl/flux-pro-1.1-ultra\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"706c32befeb4"},"paths":{"/v2/models/bfl/flux-pro-1.1-ultra":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-pro-1.1-ultra synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Request body for the BFL FLUX 1.1 [pro] Ultra image generation API. Ultra selects the output size from aspect_ratio rather than explicit pixel dimensions.","example":{"aspect_ratio":"16:9","prompt":"A lighthouse on a rocky coast at golden hour, cinematic"},"properties":{"aspect_ratio":{"default":"16:9","description":"Aspect ratio of the image between 21:9 and 9:21, e.g. 16:9.","type":"string"},"image_prompt":{"description":"Optional base64-encoded image to remix.","type":"string"},"image_prompt_strength":{"default":0.1,"description":"Blend between the prompt and the image prompt, from 0 (prompt only) to 1 (image prompt only).","maximum":1,"minimum":0,"type":"number"},"output_format":{"default":"jpeg","description":"Output image format.","enum":["jpeg","png","webp"],"type":"string"},"prompt":{"description":"Text prompt for image generation.","type":"string"},"prompt_upsampling":{"default":false,"description":"Whether to upsample the prompt. If active, the prompt is automatically modified for more creative generation.","type":"boolean"},"raw":{"default":false,"description":"Generate less processed, more natural-looking images.","type":"boolean"},"safety_tolerance":{"default":2,"description":"Tolerance level for input and output moderation, between 0 (most strict) and 6 (least strict).","maximum":6,"minimum":0,"type":"integer"},"seed":{"description":"Optional seed for reproducibility. A random seed is used when omitted.","example":42,"type":"integer"},"webhook_secret":{"description":"Optional secret for webhook signature verification.","type":"string"},"webhook_url":{"description":"URL to receive webhook notifications.","format":"uri","maxLength":2083,"minLength":1,"type":"string"}},"required":["prompt"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-pro-1.1-ultra","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/flux-pro-1.1.json b/router-schemas/bfl/flux-pro-1.1.json new file mode 100644 index 000000000..9947b4a32 --- /dev/null +++ b/router-schemas/bfl/flux-pro-1.1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/flux-pro-1.1","description":"The request body Comfy Router accepts for the model \"bfl/flux-pro-1.1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"fcb5edebf468"},"paths":{"/v2/models/bfl/flux-pro-1.1":{"post":{"operationId":"runRouterModel","summary":"Run bfl/flux-pro-1.1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Request body for the BFL FLUX 1.1 [pro] image generation API.","example":{"height":768,"prompt":"An impressionist landscape of rolling hills under a summer sky","width":1024},"properties":{"height":{"default":768,"description":"Height of the generated image in pixels. Must be a multiple of 32.","maximum":1440,"minimum":256,"multipleOf":32,"type":"integer"},"image_prompt":{"description":"Optional base64-encoded image to use with FLUX Redux.","type":"string"},"output_format":{"default":"jpeg","description":"Output image format.","enum":["jpeg","png","webp"],"type":"string"},"prompt":{"description":"Text prompt for image generation.","type":"string"},"prompt_upsampling":{"default":false,"description":"Whether to upsample the prompt. If active, the prompt is automatically modified for more creative generation.","type":"boolean"},"safety_tolerance":{"default":2,"description":"Tolerance level for input and output moderation, between 0 (most strict) and 6 (least strict).","maximum":6,"minimum":0,"type":"integer"},"seed":{"description":"Optional seed for reproducibility. A random seed is used when omitted.","example":42,"type":"integer"},"webhook_secret":{"description":"Optional secret for webhook signature verification.","type":"string"},"webhook_url":{"description":"URL to receive webhook notifications.","format":"uri","maxLength":2083,"minLength":1,"type":"string"},"width":{"default":1024,"description":"Width of the generated image in pixels. Must be a multiple of 32.","maximum":1440,"minimum":256,"multipleOf":32,"type":"integer"}},"required":["prompt"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/flux-pro-1.1","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/video-upscale-v1.json b/router-schemas/bfl/video-upscale-v1.json new file mode 100644 index 000000000..9d6d0c179 --- /dev/null +++ b/router-schemas/bfl/video-upscale-v1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/video-upscale-v1","description":"The request body Comfy Router accepts for the model \"bfl/video-upscale-v1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"79bcbc5ec2e5"},"paths":{"/v2/models/bfl/video-upscale-v1":{"post":{"operationId":"runRouterModel","summary":"Run bfl/video-upscale-v1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Request body for the BFL Flux Tools Video Upscale v1 API. Charges are based on the delivered output only; input resolution and upscale factor are not billed separately.","example":{"input_video":"https://example.com/clip.mp4","upscale_factor":2},"properties":{"creativity":{"default":1,"description":"0 preserves the source precisely and sharpens it; 1 allows creative detail enhancement, which does not strictly preserve faces or products.","maximum":1,"minimum":0,"type":"integer"},"input_video":{"description":"Video to upscale, either an HTTP(S) URL or a base64-encoded MP4. At most 20 seconds of source footage and 50MB.","type":"string"},"prompt":{"description":"Optional description of the clip's content, steering the enhanced detail. Leave empty for a neutral upscale.","type":"string"},"safety_tolerance":{"default":2,"description":"Tolerance level for prompt and output frame moderation, 0 being most strict.","maximum":4,"minimum":0,"type":"integer"},"upscale_factor":{"default":2,"description":"Output scaling relative to the source resolution. The output preserves the source aspect ratio and is capped at roughly 14.4 megapixels per frame, so very large sources scale by less than the requested factor.","format":"float","maximum":3,"minimum":1.5,"type":"number"},"webhook_secret":{"description":"Optional secret for webhook signature verification.","type":"string"},"webhook_url":{"description":"URL to receive webhook notifications.","format":"uri","maxLength":2083,"minLength":1,"type":"string"}},"required":["input_video"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/video-upscale-v1","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bfl/vto-v1.json b/router-schemas/bfl/vto-v1.json new file mode 100644 index 000000000..96107ddbc --- /dev/null +++ b/router-schemas/bfl/vto-v1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bfl/vto-v1","description":"The request body Comfy Router accepts for the model \"bfl/vto-v1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"751c3a975570"},"paths":{"/v2/models/bfl/vto-v1":{"post":{"operationId":"runRouterModel","summary":"Run bfl/vto-v1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3ResultResponse"},{"properties":{"result":{"description":"The finished generation. Not nullable here: this component's `required` entry is a promise that a `200` carries the result, and a nullable `result` would reduce it to a key-presence check.","properties":{"cost":{"description":"Provider-reported cost of the generation. This is BFL's number, not the Comfy charge.","format":"double","nullable":true,"type":"number"},"duration":{"description":"Provider-reported generation duration in seconds.","format":"double","nullable":true,"type":"number"},"end_time":{"description":"Provider-reported completion time of the generation, in seconds since the Unix epoch. `double` for the same reason as `start_time`.","format":"double","nullable":true,"type":"number"},"prompt":{"description":"The prompt the generation actually ran, after any prompt upsampling.","type":"string"},"sample":{"description":"Signed URL for the generated asset. Router re-hosts the asset onto Comfy storage and rewrites this field, so it is normally a Comfy-hosted URL valid for up to 24 hours - signed for 24 hours when minted, and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left; a leaf whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"},"seed":{"description":"The seed the generation used, whether supplied or chosen by the provider. Declared `int64` because BFL returns seeds above 2^31 (e.g. 2784347701), which an unformatted `integer` generates as a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"start_time":{"description":"Provider-reported start time of the generation, in seconds since the Unix epoch. `double`, not `float`: float32 spacing near a present-day epoch value is ~128 seconds, which collapses a whole generation's span to a single decoded value.","format":"double","nullable":true,"type":"number"}},"type":"object"}},"required":["id","status","result"],"type":"object"}],"description":"Comfy Router output schema for the BFL models that return a `sample`: the terminal `GET /v1/get_result` document BFL returns, forwarded unchanged EXCEPT for the re-hosted `result.sample` described below - every other field is BFL's own. Router submits the generation and polls on the caller's behalf, so the body a caller receives is the finished status document rather than the task handle the underlying `/proxy/bfl/*` submit returns.\n`result.sample` is a COMFY-HOSTED signed URL, valid for up to 24 hours: Router re-hosts the generated asset onto Comfy storage and rewrites the leaf, so what a caller receives is a Comfy link and not BFL's own delivery URL (roughly two hours for video, roughly ten minutes for images). Durability is per leaf, not per response: a leaf whose re-host could not be performed keeps BFL's own short-lived URL rather than a Comfy one, and the rest of the document is unaffected — so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing the URL. The image routes additionally populate `prompt`, `seed` and the timing fields below.\n`bfl/flux-3-video` is described by `BFLFlux3VideoRouterOutput` instead, because its `draft: true` mode answers with `result.draft_cache` and no `sample`.\nThe composition is deliberate. `BFLFlux3ResultResponse` carries the envelope (`id`, `status`, `progress`, `cost`); the second member adds the image-result fields `BFLFlux3Result` omits and requires a non-null `result`. Requiring it is safe on THIS body because a BFL task that terminated without an asset is answered as a Comfy Router error (`provider_failed`) rather than with the provider document, so a `200` here always carries the finished result.\n`status` is deliberately NOT narrowed to the `BFLStatus` enum. Router forwards this field exactly as BFL spelled it, and the poll classifier (`routerpollstate.classifyBFL`) compares this field only after `strings.ToLower(strings.TrimSpace(...))` — so a terminal success may legitimately spell it `ready` rather than `Ready`, which a title-cased enum would reject.","example":{"cost":null,"id":"b2e0c1a4-0f2f-4a55-9f2e-2f9a1c0d4e77","progress":null,"result":{"cost":null,"duration":3.4,"end_time":1767225603.4,"prompt":"A watercolor painting of a lighthouse at dawn, soft light on the water","sample":"https://example.invalid/bfl/flux-pro-1.1/sample.png","seed":2784347701,"start_time":1767225600},"status":"Ready"}}}}}}}}},"components":{"schemas":{"BFLFlux3Result":{"description":"Completed FLUX 3 video result.","properties":{"cost":{"description":"Provider-reported task cost, currently null ahead of BFL GA.","format":"float","nullable":true,"type":"number"},"sample":{"description":"Signed URL for the generated asset. The asset is re-hosted onto Comfy storage and this field rewritten to the Comfy-hosted URL, valid for 24 hours; a result whose re-host could not be performed keeps BFL's own short-lived delivery URL instead - roughly two hours for video, roughly ten minutes for images. Either way the link expires, so download the asset rather than storing the URL.","format":"uri","type":"string"}},"required":["sample"],"type":"object"},"BFLFlux3ResultResponse":{"description":"Current state of an asynchronous FLUX 3 task.","properties":{"cost":{"description":"Provider-reported cost in credits, populated once the task is Ready.","format":"float","nullable":true,"type":"number"},"id":{"description":"BFL task identifier.","type":"string"},"progress":{"description":"Optional generation progress reported by BFL.","format":"float","maximum":1,"minimum":0,"nullable":true,"type":"number"},"result":{"allOf":[{"$ref":"#/components/schemas/BFLFlux3Result"}],"nullable":true},"status":{"description":"Task status: Pending, Reasoning, Generating, Ready, Request Moderated, Content Moderated, Error, or Task not found.","type":"string"}},"required":["id","status"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bfl/vto-v1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bria/fibo.json b/router-schemas/bria/fibo.json new file mode 100644 index 000000000..31df269a7 --- /dev/null +++ b/router-schemas/bria/fibo.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bria/fibo","description":"The request body Comfy Router accepts for the model \"bria/fibo\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5a621ac8bcfa"},"paths":{"/v2/models/bria/fibo":{"post":{"operationId":"runRouterModel","summary":"Run bria/fibo synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BriaStatusResponse"}],"description":"Comfy Router output schema for the Bria image models: the terminal `GET /v2/status/{request_id}` document, forwarded unchanged. Every Bria operation is SUBMIT-AND-POLL — the submit answers with a `BriaAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyBria`), so the body a caller receives is the finished result rather than that handle. All ten Bria operations share this one status route and this one document shape.\nThe edited image is at `result.image_url`. `result` is present ONLY when `status` is `COMPLETED`, and it also carries `seed`, `prompt` and `refined_prompt`, which are echoes of the REQUEST rather than output — so a caller must key completion off `result.image_url` rather than off `result` being non-empty. `routerpollstate` draws the same line: a `COMPLETED` whose `result` holds only those echoes is `success_without_output`, not a finished generation.\n`status` is Bria's own UPPERCASE four-value vocabulary — `IN_PROGRESS`, `COMPLETED`, `ERROR`, `UNKNOWN` — and it is not case-folded. `error` is present only when `status` is `ERROR`.","example":{"request_id":"0b3f9d7e-2c41-4a8b-9f10-6d5c8e2a4b71","result":{"image_url":"https://example.invalid/bria/image-edit-remove-background/generated.png","seed":42},"status":"COMPLETED"}}}}}}}}},"components":{"schemas":{"BriaStatusResponse":{"description":"Status response from Bria API","properties":{"error":{"description":"Error object (only present when status is ERROR)","properties":{"code":{"description":"Error code.","type":"integer"},"details":{"description":"Additional error details.","type":"string"},"message":{"description":"Error message.","type":"string"}},"type":"object"},"request_id":{"description":"Unique identifier for the request.","type":"string"},"result":{"description":"Result object (only present when status is COMPLETED)","properties":{"image_url":{"description":"URL of the generated/edited image.","type":"string"},"prompt":{"description":"Original prompt.","type":"string"},"refined_prompt":{"description":"Refined version of the prompt.","type":"string"},"seed":{"description":"Seed used for generation.","type":"integer"},"structured_prompt":{"description":"The detailed JSON structured prompt.","type":"string"},"video_url":{"description":"URL of the generated video.","type":"string"}},"type":"object"},"status":{"description":"Current status of the request.","enum":["IN_PROGRESS","COMPLETED","ERROR","UNKNOWN"],"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bria/fibo","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bria/image-edit-erase.json b/router-schemas/bria/image-edit-erase.json new file mode 100644 index 000000000..d1e89b6f5 --- /dev/null +++ b/router-schemas/bria/image-edit-erase.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bria/image-edit-erase","description":"The request body Comfy Router accepts for the model \"bria/image-edit-erase\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5a621ac8bcfa"},"paths":{"/v2/models/bria/image-edit-erase":{"post":{"operationId":"runRouterModel","summary":"Run bria/image-edit-erase synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BriaStatusResponse"}],"description":"Comfy Router output schema for the Bria image models: the terminal `GET /v2/status/{request_id}` document, forwarded unchanged. Every Bria operation is SUBMIT-AND-POLL — the submit answers with a `BriaAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyBria`), so the body a caller receives is the finished result rather than that handle. All ten Bria operations share this one status route and this one document shape.\nThe edited image is at `result.image_url`. `result` is present ONLY when `status` is `COMPLETED`, and it also carries `seed`, `prompt` and `refined_prompt`, which are echoes of the REQUEST rather than output — so a caller must key completion off `result.image_url` rather than off `result` being non-empty. `routerpollstate` draws the same line: a `COMPLETED` whose `result` holds only those echoes is `success_without_output`, not a finished generation.\n`status` is Bria's own UPPERCASE four-value vocabulary — `IN_PROGRESS`, `COMPLETED`, `ERROR`, `UNKNOWN` — and it is not case-folded. `error` is present only when `status` is `ERROR`.","example":{"request_id":"0b3f9d7e-2c41-4a8b-9f10-6d5c8e2a4b71","result":{"image_url":"https://example.invalid/bria/image-edit-remove-background/generated.png","seed":42},"status":"COMPLETED"}}}}}}}}},"components":{"schemas":{"BriaStatusResponse":{"description":"Status response from Bria API","properties":{"error":{"description":"Error object (only present when status is ERROR)","properties":{"code":{"description":"Error code.","type":"integer"},"details":{"description":"Additional error details.","type":"string"},"message":{"description":"Error message.","type":"string"}},"type":"object"},"request_id":{"description":"Unique identifier for the request.","type":"string"},"result":{"description":"Result object (only present when status is COMPLETED)","properties":{"image_url":{"description":"URL of the generated/edited image.","type":"string"},"prompt":{"description":"Original prompt.","type":"string"},"refined_prompt":{"description":"Refined version of the prompt.","type":"string"},"seed":{"description":"Seed used for generation.","type":"integer"},"structured_prompt":{"description":"The detailed JSON structured prompt.","type":"string"},"video_url":{"description":"URL of the generated video.","type":"string"}},"type":"object"},"status":{"description":"Current status of the request.","enum":["IN_PROGRESS","COMPLETED","ERROR","UNKNOWN"],"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bria/image-edit-erase","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bria/image-edit-expand.json b/router-schemas/bria/image-edit-expand.json new file mode 100644 index 000000000..f90b22b95 --- /dev/null +++ b/router-schemas/bria/image-edit-expand.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bria/image-edit-expand","description":"The request body Comfy Router accepts for the model \"bria/image-edit-expand\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5a621ac8bcfa"},"paths":{"/v2/models/bria/image-edit-expand":{"post":{"operationId":"runRouterModel","summary":"Run bria/image-edit-expand synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BriaStatusResponse"}],"description":"Comfy Router output schema for the Bria image models: the terminal `GET /v2/status/{request_id}` document, forwarded unchanged. Every Bria operation is SUBMIT-AND-POLL — the submit answers with a `BriaAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyBria`), so the body a caller receives is the finished result rather than that handle. All ten Bria operations share this one status route and this one document shape.\nThe edited image is at `result.image_url`. `result` is present ONLY when `status` is `COMPLETED`, and it also carries `seed`, `prompt` and `refined_prompt`, which are echoes of the REQUEST rather than output — so a caller must key completion off `result.image_url` rather than off `result` being non-empty. `routerpollstate` draws the same line: a `COMPLETED` whose `result` holds only those echoes is `success_without_output`, not a finished generation.\n`status` is Bria's own UPPERCASE four-value vocabulary — `IN_PROGRESS`, `COMPLETED`, `ERROR`, `UNKNOWN` — and it is not case-folded. `error` is present only when `status` is `ERROR`.","example":{"request_id":"0b3f9d7e-2c41-4a8b-9f10-6d5c8e2a4b71","result":{"image_url":"https://example.invalid/bria/image-edit-remove-background/generated.png","seed":42},"status":"COMPLETED"}}}}}}}}},"components":{"schemas":{"BriaStatusResponse":{"description":"Status response from Bria API","properties":{"error":{"description":"Error object (only present when status is ERROR)","properties":{"code":{"description":"Error code.","type":"integer"},"details":{"description":"Additional error details.","type":"string"},"message":{"description":"Error message.","type":"string"}},"type":"object"},"request_id":{"description":"Unique identifier for the request.","type":"string"},"result":{"description":"Result object (only present when status is COMPLETED)","properties":{"image_url":{"description":"URL of the generated/edited image.","type":"string"},"prompt":{"description":"Original prompt.","type":"string"},"refined_prompt":{"description":"Refined version of the prompt.","type":"string"},"seed":{"description":"Seed used for generation.","type":"integer"},"structured_prompt":{"description":"The detailed JSON structured prompt.","type":"string"},"video_url":{"description":"URL of the generated video.","type":"string"}},"type":"object"},"status":{"description":"Current status of the request.","enum":["IN_PROGRESS","COMPLETED","ERROR","UNKNOWN"],"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bria/image-edit-expand","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bria/image-edit-gen-fill.json b/router-schemas/bria/image-edit-gen-fill.json new file mode 100644 index 000000000..2d6a59a8c --- /dev/null +++ b/router-schemas/bria/image-edit-gen-fill.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bria/image-edit-gen-fill","description":"The request body Comfy Router accepts for the model \"bria/image-edit-gen-fill\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5a621ac8bcfa"},"paths":{"/v2/models/bria/image-edit-gen-fill":{"post":{"operationId":"runRouterModel","summary":"Run bria/image-edit-gen-fill synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BriaStatusResponse"}],"description":"Comfy Router output schema for the Bria image models: the terminal `GET /v2/status/{request_id}` document, forwarded unchanged. Every Bria operation is SUBMIT-AND-POLL — the submit answers with a `BriaAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyBria`), so the body a caller receives is the finished result rather than that handle. All ten Bria operations share this one status route and this one document shape.\nThe edited image is at `result.image_url`. `result` is present ONLY when `status` is `COMPLETED`, and it also carries `seed`, `prompt` and `refined_prompt`, which are echoes of the REQUEST rather than output — so a caller must key completion off `result.image_url` rather than off `result` being non-empty. `routerpollstate` draws the same line: a `COMPLETED` whose `result` holds only those echoes is `success_without_output`, not a finished generation.\n`status` is Bria's own UPPERCASE four-value vocabulary — `IN_PROGRESS`, `COMPLETED`, `ERROR`, `UNKNOWN` — and it is not case-folded. `error` is present only when `status` is `ERROR`.","example":{"request_id":"0b3f9d7e-2c41-4a8b-9f10-6d5c8e2a4b71","result":{"image_url":"https://example.invalid/bria/image-edit-remove-background/generated.png","seed":42},"status":"COMPLETED"}}}}}}}}},"components":{"schemas":{"BriaStatusResponse":{"description":"Status response from Bria API","properties":{"error":{"description":"Error object (only present when status is ERROR)","properties":{"code":{"description":"Error code.","type":"integer"},"details":{"description":"Additional error details.","type":"string"},"message":{"description":"Error message.","type":"string"}},"type":"object"},"request_id":{"description":"Unique identifier for the request.","type":"string"},"result":{"description":"Result object (only present when status is COMPLETED)","properties":{"image_url":{"description":"URL of the generated/edited image.","type":"string"},"prompt":{"description":"Original prompt.","type":"string"},"refined_prompt":{"description":"Refined version of the prompt.","type":"string"},"seed":{"description":"Seed used for generation.","type":"integer"},"structured_prompt":{"description":"The detailed JSON structured prompt.","type":"string"},"video_url":{"description":"URL of the generated video.","type":"string"}},"type":"object"},"status":{"description":"Current status of the request.","enum":["IN_PROGRESS","COMPLETED","ERROR","UNKNOWN"],"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bria/image-edit-gen-fill","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/bria/image-edit-remove-background.json b/router-schemas/bria/image-edit-remove-background.json new file mode 100644 index 000000000..505980e51 --- /dev/null +++ b/router-schemas/bria/image-edit-remove-background.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"bria/image-edit-remove-background","description":"The request body Comfy Router accepts for the model \"bria/image-edit-remove-background\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5a621ac8bcfa"},"paths":{"/v2/models/bria/image-edit-remove-background":{"post":{"operationId":"runRouterModel","summary":"Run bria/image-edit-remove-background synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BriaStatusResponse"}],"description":"Comfy Router output schema for the Bria image models: the terminal `GET /v2/status/{request_id}` document, forwarded unchanged. Every Bria operation is SUBMIT-AND-POLL — the submit answers with a `BriaAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyBria`), so the body a caller receives is the finished result rather than that handle. All ten Bria operations share this one status route and this one document shape.\nThe edited image is at `result.image_url`. `result` is present ONLY when `status` is `COMPLETED`, and it also carries `seed`, `prompt` and `refined_prompt`, which are echoes of the REQUEST rather than output — so a caller must key completion off `result.image_url` rather than off `result` being non-empty. `routerpollstate` draws the same line: a `COMPLETED` whose `result` holds only those echoes is `success_without_output`, not a finished generation.\n`status` is Bria's own UPPERCASE four-value vocabulary — `IN_PROGRESS`, `COMPLETED`, `ERROR`, `UNKNOWN` — and it is not case-folded. `error` is present only when `status` is `ERROR`.","example":{"request_id":"0b3f9d7e-2c41-4a8b-9f10-6d5c8e2a4b71","result":{"image_url":"https://example.invalid/bria/image-edit-remove-background/generated.png","seed":42},"status":"COMPLETED"}}}}}}}}},"components":{"schemas":{"BriaStatusResponse":{"description":"Status response from Bria API","properties":{"error":{"description":"Error object (only present when status is ERROR)","properties":{"code":{"description":"Error code.","type":"integer"},"details":{"description":"Additional error details.","type":"string"},"message":{"description":"Error message.","type":"string"}},"type":"object"},"request_id":{"description":"Unique identifier for the request.","type":"string"},"result":{"description":"Result object (only present when status is COMPLETED)","properties":{"image_url":{"description":"URL of the generated/edited image.","type":"string"},"prompt":{"description":"Original prompt.","type":"string"},"refined_prompt":{"description":"Refined version of the prompt.","type":"string"},"seed":{"description":"Seed used for generation.","type":"integer"},"structured_prompt":{"description":"The detailed JSON structured prompt.","type":"string"},"video_url":{"description":"URL of the generated video.","type":"string"}},"type":"object"},"status":{"description":"Current status of the request.","enum":["IN_PROGRESS","COMPLETED","ERROR","UNKNOWN"],"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"bria/image-edit-remove-background","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/dreamina-seedance-2-0-260128.json b/router-schemas/byteplus/dreamina-seedance-2-0-260128.json new file mode 100644 index 000000000..6b4fc033f --- /dev/null +++ b/router-schemas/byteplus/dreamina-seedance-2-0-260128.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/dreamina-seedance-2-0-260128","description":"The request body Comfy Router accepts for the model \"byteplus/dreamina-seedance-2-0-260128\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/dreamina-seedance-2-0-260128":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/dreamina-seedance-2-0-260128 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/dreamina-seedance-2-0-260128","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/dreamina-seedance-2-0-fast-260128.json b/router-schemas/byteplus/dreamina-seedance-2-0-fast-260128.json new file mode 100644 index 000000000..babf8fe55 --- /dev/null +++ b/router-schemas/byteplus/dreamina-seedance-2-0-fast-260128.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/dreamina-seedance-2-0-fast-260128","description":"The request body Comfy Router accepts for the model \"byteplus/dreamina-seedance-2-0-fast-260128\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/dreamina-seedance-2-0-fast-260128":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/dreamina-seedance-2-0-fast-260128 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/dreamina-seedance-2-0-fast-260128","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/dreamina-seedance-2-0-mini.json b/router-schemas/byteplus/dreamina-seedance-2-0-mini.json new file mode 100644 index 000000000..b7406f2a0 --- /dev/null +++ b/router-schemas/byteplus/dreamina-seedance-2-0-mini.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/dreamina-seedance-2-0-mini","description":"The request body Comfy Router accepts for the model \"byteplus/dreamina-seedance-2-0-mini\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/dreamina-seedance-2-0-mini":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/dreamina-seedance-2-0-mini synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/dreamina-seedance-2-0-mini","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/dreamina-seedance-2-5-260628.json b/router-schemas/byteplus/dreamina-seedance-2-5-260628.json new file mode 100644 index 000000000..99003c77a --- /dev/null +++ b/router-schemas/byteplus/dreamina-seedance-2-5-260628.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/dreamina-seedance-2-5-260628","description":"The request body Comfy Router accepts for the model \"byteplus/dreamina-seedance-2-5-260628\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/dreamina-seedance-2-5-260628":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/dreamina-seedance-2-5-260628 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/dreamina-seedance-2-5-260628","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seed-audio-1.0-multilingual.json b/router-schemas/byteplus/seed-audio-1.0-multilingual.json new file mode 100644 index 000000000..b69192340 --- /dev/null +++ b/router-schemas/byteplus/seed-audio-1.0-multilingual.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seed-audio-1.0-multilingual","description":"The request body Comfy Router accepts for the model \"byteplus/seed-audio-1.0-multilingual\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"098ac01c2262"},"paths":{"/v2/models/byteplus/seed-audio-1.0-multilingual":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seed-audio-1.0-multilingual synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusTTSCreateResponse"}],"description":"Comfy Router output schema for the BytePlus Seed Audio 1.0 text-to-speech models: BytePlus's own generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/tts/create` as ReturnModeDirect), so the body a caller receives is this finished document from the one call rather than a task handle: the operation registers no task checker and declares no status route.\nThe generated audio arrives at one of two leaves and a caller selects by which key is present: inline base64 bytes at `audio`, or a temporary download URL at `url` which BytePlus expires after two hours. Router does not re-host the audio: the BytePlus re-host is the video task query only, so `url` stays BytePlus's own two-hour link and `audio` is inline. The example below populates BOTH, to document each leaf's shape rather than to claim a response carries both. A 2xx carrying NEITHER leaf is reachable: BytePlus reports a non-generating outcome in `code` and `message` on this same body, and Router forwards it unchanged, so a caller that switches on the two asset keys needs that third branch. `subtitle` is present only when the request set `audio_config.enable_subtitle`.\n`duration` and `original_duration` are BytePlus's own accounting in seconds — BytePlus's numbers, not the Comfy charge — and `original_duration`, rounded up to a whole second, is what the call meters on. Its 120-second ceiling is BytePlus's own limit on a single request, reported by them rather than re-clamped by Comfy: read it as their documented cap, not as a bound Comfy enforces on the value it meters.\n`code` and `message` are deliberately absent from the EXAMPLE: this schema does not enumerate BytePlus's status codes (it points at their error-code document instead), so a literal here would be an unverified claim on a page Comfy publishes.","example":{"audio":"PGJhc2U2ND4=","duration":1.2,"original_duration":1.2,"url":"https://example.invalid/byteplus/seed-audio/generated.wav"}}}}}}}}},"components":{"schemas":{"BytePlusTTSCreateResponse":{"description":"Response body for a BytePlus Seed Audio 1.0 generation.","properties":{"audio":{"description":"Generated audio data, Base64-encoded.","type":"string"},"code":{"description":"Status code. Refer to the official error-code document for details.","type":"integer"},"duration":{"description":"Duration after speed or post-processing, in seconds.","format":"double","type":"number"},"message":{"description":"Status details.","type":"string"},"original_duration":{"description":"Original model output duration in seconds. Used for billing and capped at 120 seconds.","format":"double","type":"number"},"subtitle":{"$ref":"#/components/schemas/BytePlusTTSSubtitle"},"url":{"description":"Temporary audio URL, valid for 2 hours.","type":"string"}},"type":"object"},"BytePlusTTSSubtitle":{"description":"Subtitle information for the audio. Present only when audio_config.enable_subtitle is set to true in the request.\n","properties":{"sentences":{"description":"Utterance-level subtitle information.","items":{"$ref":"#/components/schemas/BytePlusTTSSubtitleSegment"},"type":"array"},"text":{"description":"Subtitle text corresponding to the audio.","type":"string"},"words":{"description":"Word-level subtitle information.","items":{"$ref":"#/components/schemas/BytePlusTTSSubtitleSegment"},"type":"array"}},"type":"object"},"BytePlusTTSSubtitleSegment":{"description":"A single subtitle segment with timestamps.","properties":{"end_time":{"description":"Segment end time, in milliseconds from the beginning of the audio.","type":"integer"},"start_time":{"description":"Segment start time, in milliseconds from the beginning of the audio.","type":"integer"},"text":{"description":"The complete text of the segment.","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seed-audio-1.0-multilingual","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seed-audio-1.0.json b/router-schemas/byteplus/seed-audio-1.0.json new file mode 100644 index 000000000..673a10326 --- /dev/null +++ b/router-schemas/byteplus/seed-audio-1.0.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seed-audio-1.0","description":"The request body Comfy Router accepts for the model \"byteplus/seed-audio-1.0\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"098ac01c2262"},"paths":{"/v2/models/byteplus/seed-audio-1.0":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seed-audio-1.0 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusTTSCreateResponse"}],"description":"Comfy Router output schema for the BytePlus Seed Audio 1.0 text-to-speech models: BytePlus's own generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/tts/create` as ReturnModeDirect), so the body a caller receives is this finished document from the one call rather than a task handle: the operation registers no task checker and declares no status route.\nThe generated audio arrives at one of two leaves and a caller selects by which key is present: inline base64 bytes at `audio`, or a temporary download URL at `url` which BytePlus expires after two hours. Router does not re-host the audio: the BytePlus re-host is the video task query only, so `url` stays BytePlus's own two-hour link and `audio` is inline. The example below populates BOTH, to document each leaf's shape rather than to claim a response carries both. A 2xx carrying NEITHER leaf is reachable: BytePlus reports a non-generating outcome in `code` and `message` on this same body, and Router forwards it unchanged, so a caller that switches on the two asset keys needs that third branch. `subtitle` is present only when the request set `audio_config.enable_subtitle`.\n`duration` and `original_duration` are BytePlus's own accounting in seconds — BytePlus's numbers, not the Comfy charge — and `original_duration`, rounded up to a whole second, is what the call meters on. Its 120-second ceiling is BytePlus's own limit on a single request, reported by them rather than re-clamped by Comfy: read it as their documented cap, not as a bound Comfy enforces on the value it meters.\n`code` and `message` are deliberately absent from the EXAMPLE: this schema does not enumerate BytePlus's status codes (it points at their error-code document instead), so a literal here would be an unverified claim on a page Comfy publishes.","example":{"audio":"PGJhc2U2ND4=","duration":1.2,"original_duration":1.2,"url":"https://example.invalid/byteplus/seed-audio/generated.wav"}}}}}}}}},"components":{"schemas":{"BytePlusTTSCreateResponse":{"description":"Response body for a BytePlus Seed Audio 1.0 generation.","properties":{"audio":{"description":"Generated audio data, Base64-encoded.","type":"string"},"code":{"description":"Status code. Refer to the official error-code document for details.","type":"integer"},"duration":{"description":"Duration after speed or post-processing, in seconds.","format":"double","type":"number"},"message":{"description":"Status details.","type":"string"},"original_duration":{"description":"Original model output duration in seconds. Used for billing and capped at 120 seconds.","format":"double","type":"number"},"subtitle":{"$ref":"#/components/schemas/BytePlusTTSSubtitle"},"url":{"description":"Temporary audio URL, valid for 2 hours.","type":"string"}},"type":"object"},"BytePlusTTSSubtitle":{"description":"Subtitle information for the audio. Present only when audio_config.enable_subtitle is set to true in the request.\n","properties":{"sentences":{"description":"Utterance-level subtitle information.","items":{"$ref":"#/components/schemas/BytePlusTTSSubtitleSegment"},"type":"array"},"text":{"description":"Subtitle text corresponding to the audio.","type":"string"},"words":{"description":"Word-level subtitle information.","items":{"$ref":"#/components/schemas/BytePlusTTSSubtitleSegment"},"type":"array"}},"type":"object"},"BytePlusTTSSubtitleSegment":{"description":"A single subtitle segment with timestamps.","properties":{"end_time":{"description":"Segment end time, in milliseconds from the beginning of the audio.","type":"integer"},"start_time":{"description":"Segment start time, in milliseconds from the beginning of the audio.","type":"integer"},"text":{"description":"The complete text of the segment.","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seed-audio-1.0","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedance-1-0-lite-i2v-250428.json b/router-schemas/byteplus/seedance-1-0-lite-i2v-250428.json new file mode 100644 index 000000000..43bb2846b --- /dev/null +++ b/router-schemas/byteplus/seedance-1-0-lite-i2v-250428.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedance-1-0-lite-i2v-250428","description":"The request body Comfy Router accepts for the model \"byteplus/seedance-1-0-lite-i2v-250428\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/seedance-1-0-lite-i2v-250428":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedance-1-0-lite-i2v-250428 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedance-1-0-lite-i2v-250428","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedance-1-0-lite-t2v-250428.json b/router-schemas/byteplus/seedance-1-0-lite-t2v-250428.json new file mode 100644 index 000000000..7dd1d2f3e --- /dev/null +++ b/router-schemas/byteplus/seedance-1-0-lite-t2v-250428.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedance-1-0-lite-t2v-250428","description":"The request body Comfy Router accepts for the model \"byteplus/seedance-1-0-lite-t2v-250428\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/seedance-1-0-lite-t2v-250428":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedance-1-0-lite-t2v-250428 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedance-1-0-lite-t2v-250428","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedance-1-0-pro-250528.json b/router-schemas/byteplus/seedance-1-0-pro-250528.json new file mode 100644 index 000000000..f11266e57 --- /dev/null +++ b/router-schemas/byteplus/seedance-1-0-pro-250528.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedance-1-0-pro-250528","description":"The request body Comfy Router accepts for the model \"byteplus/seedance-1-0-pro-250528\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/seedance-1-0-pro-250528":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedance-1-0-pro-250528 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedance-1-0-pro-250528","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedance-1-0-pro-fast-251015.json b/router-schemas/byteplus/seedance-1-0-pro-fast-251015.json new file mode 100644 index 000000000..03371cfc3 --- /dev/null +++ b/router-schemas/byteplus/seedance-1-0-pro-fast-251015.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedance-1-0-pro-fast-251015","description":"The request body Comfy Router accepts for the model \"byteplus/seedance-1-0-pro-fast-251015\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/seedance-1-0-pro-fast-251015":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedance-1-0-pro-fast-251015 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedance-1-0-pro-fast-251015","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedance-1-5-pro-251215.json b/router-schemas/byteplus/seedance-1-5-pro-251215.json new file mode 100644 index 000000000..2bc6e152b --- /dev/null +++ b/router-schemas/byteplus/seedance-1-5-pro-251215.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedance-1-5-pro-251215","description":"The request body Comfy Router accepts for the model \"byteplus/seedance-1-5-pro-251215\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0d3d9a0cba86"},"paths":{"/v2/models/byteplus/seedance-1-5-pro-251215":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedance-1-5-pro-251215 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusVideoGenerationQueryResponse"}],"description":"Comfy Router output schema for the BytePlus Seedance video models: the terminal task-query document, forwarded unchanged EXCEPT for the two re-hosted `content` URLs described below - every other field is BytePlus's own. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`status: succeeded`) rather than the task handle the underlying submit returns.\nThe generated video's download URL is at `content.video_url`, present once the task succeeds. A failed task carries `error.code` and `error.message` instead; `status` is BytePlus's own vocabulary (queued, running, cancelled, succeeded, failed, expired), forwarded unchanged.\nThat URL is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of BytePlus's own, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Exactly two leaves are rewritten this way - `content.video_url` and `content.last_frame_url`, both modelled here. Any OTHER asset URL a task carries keeps BytePlus's own link and is NOT flagged as non-durable, so a stored or replayed document can still hand back a link that BytePlus has since cleared.\nDurability is per leaf, not per response: a leaf whose re-host could not be performed keeps BytePlus's own URL rather than a Comfy one, and the other is unaffected - so a caller that stores or replays this document should not assume every URL in it outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the asset rather than storing it.","example":{"content":{"last_frame_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/last-frame","video_url":"https://example.invalid/byteplus/seedance-1-0-lite-t2v-250428/generated.mp4"},"created_at":1767225600,"duration":5,"error":null,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"seedance-1-0-lite-t2v-250428","output_format":"mp4","resolution":"1080p","seed":1234567890123,"status":"succeeded","updated_at":1767225730}}}}}}}}},"components":{"schemas":{"BytePlusVideoGenerationQueryResponse":{"properties":{"content":{"description":"The output after the video generation task is completed, which contains the download URL of the output video and, when BytePlus returns one, the download URL of its last frame. Both `video_url` and `last_frame_url` are RE-HOSTED onto Comfy storage; every other field here is BytePlus's own. Nullable - BytePlus clears the URLs 24 hours after the task, and a succeeded document polled after that can carry `content` absent or null.","nullable":true,"properties":{"last_frame_url":{"description":"Download URL for the last frame of the generated video, returned when the request set `return_last_frame`. Do not infer the image format from this URL: BytePlus documents the last frame as PNG on the request side, Router re-hosts whatever bytes it is served and types them from the upstream Content-Type or a content sniff, and `image/jpeg` is only the last-resort fallback when both fail. Router re-hosts the last frame onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task. Either way the link expires, so download the frame rather than storing the URL.","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), when BytePlus nests it inside `content`. Seedance models more commonly return it as a TOP-LEVEL sibling of `content` - see the top-level `output_format` field - and Router reads whichever of the two is present.","type":"string"},"video_url":{"description":"Download URL for the output video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps BytePlus's own URL instead, which BytePlus clears 24 hours after the task and caps at 100 downloads on some models. Either way the link expires, so download the video rather than storing the URL.","type":"string"}},"type":"object"},"created_at":{"description":"The time when the task was created. The value is a UNIX timestamp in seconds.","type":"integer"},"duration":{"description":"The duration of the generated video in seconds. Declared as a number rather than an integer because BytePlus is not consistent about it - video tasks have been observed returning whole seconds and sibling BytePlus surfaces report fractional durations - so a client must not assume an integral value. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"number"},"error":{"description":"The error information. If the task succeeds, null is returned. If the task fails, the error information is returned.","nullable":true,"properties":{"code":{"description":"The error code","type":"string"},"message":{"description":"The error message","type":"string"}},"type":"object"},"id":{"description":"The ID of the video generation task","type":"string"},"model":{"description":"The name and version of the model used by the task","type":"string"},"output_format":{"description":"Container format of the generated video (mp4 or mov), returned at the TOP LEVEL as a sibling of `content` - this is where the Seedance video task query returns it. BytePlus's own field, forwarded unchanged.","type":"string"},"resolution":{"description":"The resolution of the generated video, for example `1080p`. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","type":"string"},"seed":{"description":"The generation seed actually used for the task. BytePlus's own field, returned on succeeded video tasks and forwarded unchanged.","format":"int64","type":"integer"},"status":{"description":"The state of the task","enum":["queued","running","cancelled","succeeded","failed","expired"],"type":"string"},"updated_at":{"description":"The time when the task was last updated. The value is a UNIX timestamp in seconds.","type":"integer"},"usage":{"description":"The token usage for the request","properties":{"completion_tokens":{"description":"The number of tokens generated by the model","type":"integer"},"total_tokens":{"description":"For the video generation model, the number of input tokens is not calculated and defaults to 0. Therefore, total_tokens = completion_tokens.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedance-1-5-pro-251215","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seededit-3-0-i2i-250628.json b/router-schemas/byteplus/seededit-3-0-i2i-250628.json new file mode 100644 index 000000000..25f439b67 --- /dev/null +++ b/router-schemas/byteplus/seededit-3-0-i2i-250628.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seededit-3-0-i2i-250628","description":"The request body Comfy Router accepts for the model \"byteplus/seededit-3-0-i2i-250628\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"3f79141493f3"},"paths":{"/v2/models/byteplus/seededit-3-0-i2i-250628":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seededit-3-0-i2i-250628 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusImageGenerationResponse"}],"description":"Comfy Router output schema for the BytePlus Seedream/Seededit image models: BytePlus's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/images/generations` as ReturnModeDirect), so the body a caller receives is this finished document from the one call.\nThe generated images are in `data`, each with either a download URL at `data[].url` or inline base64 bytes at `data[].b64_json`, depending on the request's `response_format`; select the leaf by which key is present. `usage` reports BytePlus's own generation accounting — BytePlus's numbers, not the Comfy charge.\nRouter does not re-host these images onto Comfy storage the way it re-hosts the Seedance VIDEO results: a `data[].url` here stays BytePlus's own link, which BytePlus clears 24 hours after the generation and caps at 100 downloads on some models. Download it rather than storing the URL.","example":{"created":1767225600,"data":[{"size":"1024x1024","url":"https://example.invalid/byteplus/seedream-3-0-t2i-250415/generated.png"}],"model":"seedream-3-0-t2i-250415","usage":{"generated_images":1}}}}}}}}}},"components":{"schemas":{"BytePlusImageGenerationResponse":{"properties":{"created":{"description":"Unix timestamp (in seconds) indicating the time when the request was created","type":"integer"},"data":{"description":"Contains information about the generated image(s).\nIn the layer-separation scenario, the first element of the array is the base image (z_index=0), and the following elements are the layers, ordered by increasing z_index.\n","items":{"properties":{"b64_json":{"description":"Base64-encoded image data (if response_format is \"b64_json\")","type":"string"},"bounding_box":{"description":"The bounding-box information of the region that the current layer occupies within the base image. Only layers return this field; the base image covers the whole canvas and does not return bounding_box. Returned only when layer_decomposition is true.","properties":{"absolute":{"description":"The absolute pixel coordinates of the layer's bounding box, in the output base image's coordinate system with the top-left corner at (0, 0). Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"},"normalized":{"description":"The per-mille quantized (normalized) coordinates of the layer's bounding box, proportionally mapped to a discrete integer range of [0, 1000] based on the base image size, truncated at a maximum of 1000. Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"}},"type":"object"},"description":{"description":"A detailed description of the current separated element, providing richer layer characteristics (such as color, state, material) than name. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"name":{"description":"The name/label of the current separated element, automatically generated by the model from the characteristics of the separated subject. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"output_format":{"description":"The file format of the output image. Only seedream-5.0-pro supports this field.","type":"string"},"size":{"description":"The width and height of the image in pixels, in the format \u003cwidth\u003ex\u003cheight\u003e. Only seedream-5.0-pro, 5.0-lite, 4.5 and 4.0 support this parameter.","type":"string"},"url":{"description":"URL for image download (if response_format is \"url\")","format":"uri","type":"string"},"z_index":{"description":"The stacking order of the layer, increasing from bottom to top: 0 is the bottom-most layer (the base image); larger values sit higher. Use it to recompose the layers into the complete image at the correct stacking order. Returned only when layer_decomposition is true.","type":"integer"}},"type":"object"},"type":"array"},"error":{"description":"Error information (if any)","properties":{"code":{"description":"Error code","type":"string"},"message":{"description":"Error message","type":"string"}},"type":"object"},"model":{"description":"The model ID used for the request","example":"seedream-3-0-t2i-250415","type":"string"},"usage":{"properties":{"generated_images":{"description":"Number of images generated by the model","type":"integer"},"input_images":{"description":"The number of images input to the model. Only seedream-5.0-pro supports this field.","type":"integer"},"output_tokens":{"description":"The number of tokens used for the picture generated by the model.","type":"integer"},"total_tokens":{"description":"The total number of tokens consumed by this request.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seededit-3-0-i2i-250628","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedream-3-0-t2i-250415.json b/router-schemas/byteplus/seedream-3-0-t2i-250415.json new file mode 100644 index 000000000..22d627e41 --- /dev/null +++ b/router-schemas/byteplus/seedream-3-0-t2i-250415.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedream-3-0-t2i-250415","description":"The request body Comfy Router accepts for the model \"byteplus/seedream-3-0-t2i-250415\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"3f79141493f3"},"paths":{"/v2/models/byteplus/seedream-3-0-t2i-250415":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedream-3-0-t2i-250415 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusImageGenerationResponse"}],"description":"Comfy Router output schema for the BytePlus Seedream/Seededit image models: BytePlus's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/images/generations` as ReturnModeDirect), so the body a caller receives is this finished document from the one call.\nThe generated images are in `data`, each with either a download URL at `data[].url` or inline base64 bytes at `data[].b64_json`, depending on the request's `response_format`; select the leaf by which key is present. `usage` reports BytePlus's own generation accounting — BytePlus's numbers, not the Comfy charge.\nRouter does not re-host these images onto Comfy storage the way it re-hosts the Seedance VIDEO results: a `data[].url` here stays BytePlus's own link, which BytePlus clears 24 hours after the generation and caps at 100 downloads on some models. Download it rather than storing the URL.","example":{"created":1767225600,"data":[{"size":"1024x1024","url":"https://example.invalid/byteplus/seedream-3-0-t2i-250415/generated.png"}],"model":"seedream-3-0-t2i-250415","usage":{"generated_images":1}}}}}}}}}},"components":{"schemas":{"BytePlusImageGenerationResponse":{"properties":{"created":{"description":"Unix timestamp (in seconds) indicating the time when the request was created","type":"integer"},"data":{"description":"Contains information about the generated image(s).\nIn the layer-separation scenario, the first element of the array is the base image (z_index=0), and the following elements are the layers, ordered by increasing z_index.\n","items":{"properties":{"b64_json":{"description":"Base64-encoded image data (if response_format is \"b64_json\")","type":"string"},"bounding_box":{"description":"The bounding-box information of the region that the current layer occupies within the base image. Only layers return this field; the base image covers the whole canvas and does not return bounding_box. Returned only when layer_decomposition is true.","properties":{"absolute":{"description":"The absolute pixel coordinates of the layer's bounding box, in the output base image's coordinate system with the top-left corner at (0, 0). Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"},"normalized":{"description":"The per-mille quantized (normalized) coordinates of the layer's bounding box, proportionally mapped to a discrete integer range of [0, 1000] based on the base image size, truncated at a maximum of 1000. Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"}},"type":"object"},"description":{"description":"A detailed description of the current separated element, providing richer layer characteristics (such as color, state, material) than name. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"name":{"description":"The name/label of the current separated element, automatically generated by the model from the characteristics of the separated subject. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"output_format":{"description":"The file format of the output image. Only seedream-5.0-pro supports this field.","type":"string"},"size":{"description":"The width and height of the image in pixels, in the format \u003cwidth\u003ex\u003cheight\u003e. Only seedream-5.0-pro, 5.0-lite, 4.5 and 4.0 support this parameter.","type":"string"},"url":{"description":"URL for image download (if response_format is \"url\")","format":"uri","type":"string"},"z_index":{"description":"The stacking order of the layer, increasing from bottom to top: 0 is the bottom-most layer (the base image); larger values sit higher. Use it to recompose the layers into the complete image at the correct stacking order. Returned only when layer_decomposition is true.","type":"integer"}},"type":"object"},"type":"array"},"error":{"description":"Error information (if any)","properties":{"code":{"description":"Error code","type":"string"},"message":{"description":"Error message","type":"string"}},"type":"object"},"model":{"description":"The model ID used for the request","example":"seedream-3-0-t2i-250415","type":"string"},"usage":{"properties":{"generated_images":{"description":"Number of images generated by the model","type":"integer"},"input_images":{"description":"The number of images input to the model. Only seedream-5.0-pro supports this field.","type":"integer"},"output_tokens":{"description":"The number of tokens used for the picture generated by the model.","type":"integer"},"total_tokens":{"description":"The total number of tokens consumed by this request.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedream-3-0-t2i-250415","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedream-4-0-250828.json b/router-schemas/byteplus/seedream-4-0-250828.json new file mode 100644 index 000000000..760a32007 --- /dev/null +++ b/router-schemas/byteplus/seedream-4-0-250828.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedream-4-0-250828","description":"The request body Comfy Router accepts for the model \"byteplus/seedream-4-0-250828\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"3f79141493f3"},"paths":{"/v2/models/byteplus/seedream-4-0-250828":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedream-4-0-250828 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusImageGenerationResponse"}],"description":"Comfy Router output schema for the BytePlus Seedream/Seededit image models: BytePlus's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/images/generations` as ReturnModeDirect), so the body a caller receives is this finished document from the one call.\nThe generated images are in `data`, each with either a download URL at `data[].url` or inline base64 bytes at `data[].b64_json`, depending on the request's `response_format`; select the leaf by which key is present. `usage` reports BytePlus's own generation accounting — BytePlus's numbers, not the Comfy charge.\nRouter does not re-host these images onto Comfy storage the way it re-hosts the Seedance VIDEO results: a `data[].url` here stays BytePlus's own link, which BytePlus clears 24 hours after the generation and caps at 100 downloads on some models. Download it rather than storing the URL.","example":{"created":1767225600,"data":[{"size":"1024x1024","url":"https://example.invalid/byteplus/seedream-3-0-t2i-250415/generated.png"}],"model":"seedream-3-0-t2i-250415","usage":{"generated_images":1}}}}}}}}}},"components":{"schemas":{"BytePlusImageGenerationResponse":{"properties":{"created":{"description":"Unix timestamp (in seconds) indicating the time when the request was created","type":"integer"},"data":{"description":"Contains information about the generated image(s).\nIn the layer-separation scenario, the first element of the array is the base image (z_index=0), and the following elements are the layers, ordered by increasing z_index.\n","items":{"properties":{"b64_json":{"description":"Base64-encoded image data (if response_format is \"b64_json\")","type":"string"},"bounding_box":{"description":"The bounding-box information of the region that the current layer occupies within the base image. Only layers return this field; the base image covers the whole canvas and does not return bounding_box. Returned only when layer_decomposition is true.","properties":{"absolute":{"description":"The absolute pixel coordinates of the layer's bounding box, in the output base image's coordinate system with the top-left corner at (0, 0). Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"},"normalized":{"description":"The per-mille quantized (normalized) coordinates of the layer's bounding box, proportionally mapped to a discrete integer range of [0, 1000] based on the base image size, truncated at a maximum of 1000. Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"}},"type":"object"},"description":{"description":"A detailed description of the current separated element, providing richer layer characteristics (such as color, state, material) than name. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"name":{"description":"The name/label of the current separated element, automatically generated by the model from the characteristics of the separated subject. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"output_format":{"description":"The file format of the output image. Only seedream-5.0-pro supports this field.","type":"string"},"size":{"description":"The width and height of the image in pixels, in the format \u003cwidth\u003ex\u003cheight\u003e. Only seedream-5.0-pro, 5.0-lite, 4.5 and 4.0 support this parameter.","type":"string"},"url":{"description":"URL for image download (if response_format is \"url\")","format":"uri","type":"string"},"z_index":{"description":"The stacking order of the layer, increasing from bottom to top: 0 is the bottom-most layer (the base image); larger values sit higher. Use it to recompose the layers into the complete image at the correct stacking order. Returned only when layer_decomposition is true.","type":"integer"}},"type":"object"},"type":"array"},"error":{"description":"Error information (if any)","properties":{"code":{"description":"Error code","type":"string"},"message":{"description":"Error message","type":"string"}},"type":"object"},"model":{"description":"The model ID used for the request","example":"seedream-3-0-t2i-250415","type":"string"},"usage":{"properties":{"generated_images":{"description":"Number of images generated by the model","type":"integer"},"input_images":{"description":"The number of images input to the model. Only seedream-5.0-pro supports this field.","type":"integer"},"output_tokens":{"description":"The number of tokens used for the picture generated by the model.","type":"integer"},"total_tokens":{"description":"The total number of tokens consumed by this request.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedream-4-0-250828","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedream-4-5-251128.json b/router-schemas/byteplus/seedream-4-5-251128.json new file mode 100644 index 000000000..8f7147d5a --- /dev/null +++ b/router-schemas/byteplus/seedream-4-5-251128.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedream-4-5-251128","description":"The request body Comfy Router accepts for the model \"byteplus/seedream-4-5-251128\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"3f79141493f3"},"paths":{"/v2/models/byteplus/seedream-4-5-251128":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedream-4-5-251128 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusImageGenerationResponse"}],"description":"Comfy Router output schema for the BytePlus Seedream/Seededit image models: BytePlus's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/images/generations` as ReturnModeDirect), so the body a caller receives is this finished document from the one call.\nThe generated images are in `data`, each with either a download URL at `data[].url` or inline base64 bytes at `data[].b64_json`, depending on the request's `response_format`; select the leaf by which key is present. `usage` reports BytePlus's own generation accounting — BytePlus's numbers, not the Comfy charge.\nRouter does not re-host these images onto Comfy storage the way it re-hosts the Seedance VIDEO results: a `data[].url` here stays BytePlus's own link, which BytePlus clears 24 hours after the generation and caps at 100 downloads on some models. Download it rather than storing the URL.","example":{"created":1767225600,"data":[{"size":"1024x1024","url":"https://example.invalid/byteplus/seedream-3-0-t2i-250415/generated.png"}],"model":"seedream-3-0-t2i-250415","usage":{"generated_images":1}}}}}}}}}},"components":{"schemas":{"BytePlusImageGenerationResponse":{"properties":{"created":{"description":"Unix timestamp (in seconds) indicating the time when the request was created","type":"integer"},"data":{"description":"Contains information about the generated image(s).\nIn the layer-separation scenario, the first element of the array is the base image (z_index=0), and the following elements are the layers, ordered by increasing z_index.\n","items":{"properties":{"b64_json":{"description":"Base64-encoded image data (if response_format is \"b64_json\")","type":"string"},"bounding_box":{"description":"The bounding-box information of the region that the current layer occupies within the base image. Only layers return this field; the base image covers the whole canvas and does not return bounding_box. Returned only when layer_decomposition is true.","properties":{"absolute":{"description":"The absolute pixel coordinates of the layer's bounding box, in the output base image's coordinate system with the top-left corner at (0, 0). Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"},"normalized":{"description":"The per-mille quantized (normalized) coordinates of the layer's bounding box, proportionally mapped to a discrete integer range of [0, 1000] based on the base image size, truncated at a maximum of 1000. Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"}},"type":"object"},"description":{"description":"A detailed description of the current separated element, providing richer layer characteristics (such as color, state, material) than name. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"name":{"description":"The name/label of the current separated element, automatically generated by the model from the characteristics of the separated subject. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"output_format":{"description":"The file format of the output image. Only seedream-5.0-pro supports this field.","type":"string"},"size":{"description":"The width and height of the image in pixels, in the format \u003cwidth\u003ex\u003cheight\u003e. Only seedream-5.0-pro, 5.0-lite, 4.5 and 4.0 support this parameter.","type":"string"},"url":{"description":"URL for image download (if response_format is \"url\")","format":"uri","type":"string"},"z_index":{"description":"The stacking order of the layer, increasing from bottom to top: 0 is the bottom-most layer (the base image); larger values sit higher. Use it to recompose the layers into the complete image at the correct stacking order. Returned only when layer_decomposition is true.","type":"integer"}},"type":"object"},"type":"array"},"error":{"description":"Error information (if any)","properties":{"code":{"description":"Error code","type":"string"},"message":{"description":"Error message","type":"string"}},"type":"object"},"model":{"description":"The model ID used for the request","example":"seedream-3-0-t2i-250415","type":"string"},"usage":{"properties":{"generated_images":{"description":"Number of images generated by the model","type":"integer"},"input_images":{"description":"The number of images input to the model. Only seedream-5.0-pro supports this field.","type":"integer"},"output_tokens":{"description":"The number of tokens used for the picture generated by the model.","type":"integer"},"total_tokens":{"description":"The total number of tokens consumed by this request.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedream-4-5-251128","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedream-5-0-260128.json b/router-schemas/byteplus/seedream-5-0-260128.json new file mode 100644 index 000000000..2ae3cc7ce --- /dev/null +++ b/router-schemas/byteplus/seedream-5-0-260128.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedream-5-0-260128","description":"The request body Comfy Router accepts for the model \"byteplus/seedream-5-0-260128\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"3f79141493f3"},"paths":{"/v2/models/byteplus/seedream-5-0-260128":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedream-5-0-260128 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusImageGenerationResponse"}],"description":"Comfy Router output schema for the BytePlus Seedream/Seededit image models: BytePlus's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/images/generations` as ReturnModeDirect), so the body a caller receives is this finished document from the one call.\nThe generated images are in `data`, each with either a download URL at `data[].url` or inline base64 bytes at `data[].b64_json`, depending on the request's `response_format`; select the leaf by which key is present. `usage` reports BytePlus's own generation accounting — BytePlus's numbers, not the Comfy charge.\nRouter does not re-host these images onto Comfy storage the way it re-hosts the Seedance VIDEO results: a `data[].url` here stays BytePlus's own link, which BytePlus clears 24 hours after the generation and caps at 100 downloads on some models. Download it rather than storing the URL.","example":{"created":1767225600,"data":[{"size":"1024x1024","url":"https://example.invalid/byteplus/seedream-3-0-t2i-250415/generated.png"}],"model":"seedream-3-0-t2i-250415","usage":{"generated_images":1}}}}}}}}}},"components":{"schemas":{"BytePlusImageGenerationResponse":{"properties":{"created":{"description":"Unix timestamp (in seconds) indicating the time when the request was created","type":"integer"},"data":{"description":"Contains information about the generated image(s).\nIn the layer-separation scenario, the first element of the array is the base image (z_index=0), and the following elements are the layers, ordered by increasing z_index.\n","items":{"properties":{"b64_json":{"description":"Base64-encoded image data (if response_format is \"b64_json\")","type":"string"},"bounding_box":{"description":"The bounding-box information of the region that the current layer occupies within the base image. Only layers return this field; the base image covers the whole canvas and does not return bounding_box. Returned only when layer_decomposition is true.","properties":{"absolute":{"description":"The absolute pixel coordinates of the layer's bounding box, in the output base image's coordinate system with the top-left corner at (0, 0). Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"},"normalized":{"description":"The per-mille quantized (normalized) coordinates of the layer's bounding box, proportionally mapped to a discrete integer range of [0, 1000] based on the base image size, truncated at a maximum of 1000. Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"}},"type":"object"},"description":{"description":"A detailed description of the current separated element, providing richer layer characteristics (such as color, state, material) than name. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"name":{"description":"The name/label of the current separated element, automatically generated by the model from the characteristics of the separated subject. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"output_format":{"description":"The file format of the output image. Only seedream-5.0-pro supports this field.","type":"string"},"size":{"description":"The width and height of the image in pixels, in the format \u003cwidth\u003ex\u003cheight\u003e. Only seedream-5.0-pro, 5.0-lite, 4.5 and 4.0 support this parameter.","type":"string"},"url":{"description":"URL for image download (if response_format is \"url\")","format":"uri","type":"string"},"z_index":{"description":"The stacking order of the layer, increasing from bottom to top: 0 is the bottom-most layer (the base image); larger values sit higher. Use it to recompose the layers into the complete image at the correct stacking order. Returned only when layer_decomposition is true.","type":"integer"}},"type":"object"},"type":"array"},"error":{"description":"Error information (if any)","properties":{"code":{"description":"Error code","type":"string"},"message":{"description":"Error message","type":"string"}},"type":"object"},"model":{"description":"The model ID used for the request","example":"seedream-3-0-t2i-250415","type":"string"},"usage":{"properties":{"generated_images":{"description":"Number of images generated by the model","type":"integer"},"input_images":{"description":"The number of images input to the model. Only seedream-5.0-pro supports this field.","type":"integer"},"output_tokens":{"description":"The number of tokens used for the picture generated by the model.","type":"integer"},"total_tokens":{"description":"The total number of tokens consumed by this request.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedream-5-0-260128","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/byteplus/seedream-5-0-pro-260628.json b/router-schemas/byteplus/seedream-5-0-pro-260628.json new file mode 100644 index 000000000..1d562b0df --- /dev/null +++ b/router-schemas/byteplus/seedream-5-0-pro-260628.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"byteplus/seedream-5-0-pro-260628","description":"The request body Comfy Router accepts for the model \"byteplus/seedream-5-0-pro-260628\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"3f79141493f3"},"paths":{"/v2/models/byteplus/seedream-5-0-pro-260628":{"post":{"operationId":"runRouterModel","summary":"Run byteplus/seedream-5-0-pro-260628 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BytePlusImageGenerationResponse"}],"description":"Comfy Router output schema for the BytePlus Seedream/Seededit image models: BytePlus's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `byteplus /api/v3/images/generations` as ReturnModeDirect), so the body a caller receives is this finished document from the one call.\nThe generated images are in `data`, each with either a download URL at `data[].url` or inline base64 bytes at `data[].b64_json`, depending on the request's `response_format`; select the leaf by which key is present. `usage` reports BytePlus's own generation accounting — BytePlus's numbers, not the Comfy charge.\nRouter does not re-host these images onto Comfy storage the way it re-hosts the Seedance VIDEO results: a `data[].url` here stays BytePlus's own link, which BytePlus clears 24 hours after the generation and caps at 100 downloads on some models. Download it rather than storing the URL.","example":{"created":1767225600,"data":[{"size":"1024x1024","url":"https://example.invalid/byteplus/seedream-3-0-t2i-250415/generated.png"}],"model":"seedream-3-0-t2i-250415","usage":{"generated_images":1}}}}}}}}}},"components":{"schemas":{"BytePlusImageGenerationResponse":{"properties":{"created":{"description":"Unix timestamp (in seconds) indicating the time when the request was created","type":"integer"},"data":{"description":"Contains information about the generated image(s).\nIn the layer-separation scenario, the first element of the array is the base image (z_index=0), and the following elements are the layers, ordered by increasing z_index.\n","items":{"properties":{"b64_json":{"description":"Base64-encoded image data (if response_format is \"b64_json\")","type":"string"},"bounding_box":{"description":"The bounding-box information of the region that the current layer occupies within the base image. Only layers return this field; the base image covers the whole canvas and does not return bounding_box. Returned only when layer_decomposition is true.","properties":{"absolute":{"description":"The absolute pixel coordinates of the layer's bounding box, in the output base image's coordinate system with the top-left corner at (0, 0). Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"},"normalized":{"description":"The per-mille quantized (normalized) coordinates of the layer's bounding box, proportionally mapped to a discrete integer range of [0, 1000] based on the base image size, truncated at a maximum of 1000. Coordinate format: [left, top, right, bottom].","items":{"type":"integer"},"type":"array"}},"type":"object"},"description":{"description":"A detailed description of the current separated element, providing richer layer characteristics (such as color, state, material) than name. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"name":{"description":"The name/label of the current separated element, automatically generated by the model from the characteristics of the separated subject. Only layers return this field; the base image does not. Returned only when layer_decomposition is true.","type":"string"},"output_format":{"description":"The file format of the output image. Only seedream-5.0-pro supports this field.","type":"string"},"size":{"description":"The width and height of the image in pixels, in the format \u003cwidth\u003ex\u003cheight\u003e. Only seedream-5.0-pro, 5.0-lite, 4.5 and 4.0 support this parameter.","type":"string"},"url":{"description":"URL for image download (if response_format is \"url\")","format":"uri","type":"string"},"z_index":{"description":"The stacking order of the layer, increasing from bottom to top: 0 is the bottom-most layer (the base image); larger values sit higher. Use it to recompose the layers into the complete image at the correct stacking order. Returned only when layer_decomposition is true.","type":"integer"}},"type":"object"},"type":"array"},"error":{"description":"Error information (if any)","properties":{"code":{"description":"Error code","type":"string"},"message":{"description":"Error message","type":"string"}},"type":"object"},"model":{"description":"The model ID used for the request","example":"seedream-3-0-t2i-250415","type":"string"},"usage":{"properties":{"generated_images":{"description":"Number of images generated by the model","type":"integer"},"input_images":{"description":"The number of images input to the model. Only seedream-5.0-pro supports this field.","type":"integer"},"output_tokens":{"description":"The number of tokens used for the picture generated by the model.","type":"integer"},"total_tokens":{"description":"The total number of tokens consumed by this request.","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"byteplus/seedream-5-0-pro-260628","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/gemini-interactions/gemini-omni-1.1-flash.json b/router-schemas/gemini-interactions/gemini-omni-1.1-flash.json new file mode 100644 index 000000000..896471a6c --- /dev/null +++ b/router-schemas/gemini-interactions/gemini-omni-1.1-flash.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"gemini-interactions/gemini-omni-1.1-flash","description":"The request body Comfy Router accepts for the model \"gemini-interactions/gemini-omni-1.1-flash\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1d1fc88da5ec"},"paths":{"/v2/models/gemini-interactions/gemini-omni-1.1-flash":{"post":{"operationId":"runRouterModel","summary":"Run gemini-interactions/gemini-omni-1.1-flash synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiInteraction"},{"properties":{"status":{"description":"Always `completed` on a Router response. The provider's other statuses (`in_progress`, `requires_action`, `failed`, `cancelled`, `incomplete`, `budget_exceeded`) do not reach a caller through Router as a 200; they are returned as a Comfy Router error carrying the provider's body.","enum":["completed"],"type":"string"},"steps":{"description":"The interaction timeline, in order. Narrowed here from the untyped `steps` on `GeminiInteraction` so the output leaf is addressable; the provider keeps adding step types, so an item is `additionalProperties: true` and only the fields a Router caller reads are declared.","items":{"additionalProperties":true,"properties":{"content":{"description":"The typed content blocks of this step.","items":{"additionalProperties":true,"properties":{"data":{"description":"Base64-encoded inline media, on a media block delivered inline. Google caps inline media at 4 MB and requires `delivery: uri` above it.","type":"string"},"mime_type":{"description":"Media type of `data` or `uri`, on a media block.","type":"string"},"text":{"description":"The generated text. Present on a `text` block, and this is the leaf the nightly Router SDK case asserts (`steps[].content[].text`).","type":"string"},"type":{"description":"Block kind — `text`, `image`, `audio`, `video` or `document`.","type":"string"},"uri":{"description":"Reference to media delivered out of band, fetched by the caller from the URI it names.","type":"string"}},"type":"object"},"type":"array"},"type":{"description":"Step kind. `model_output` is the generated answer; `user_input` is the caller's own turn echoed back by the retrieval route; `thought` is internal reasoning. The tool steps (`function_call`, `function_result`, `code_execution_call`, `google_search_call`, ...) are open-ended and Google adds to them.","type":"string"}},"type":"object"},"type":"array"}},"required":["status","steps"],"type":"object"}],"description":"Comfy Router output schema for the Gemini Interactions omni models: the `POST /v1beta/interactions` response document, forwarded unchanged. The operation is direct-return — one call answers with the finished document, and Router never continues the interaction on the caller's behalf. Driving an interaction across turns is the caller's own business against the Gemini Interactions proxy routes.\nThe generated text is at `steps[].content[].text`. `steps` is the interaction timeline: each step carries a `type` discriminator (`model_output`, `user_input`, `thought`, the `*_call`/`*_result` tool steps) and, for the output steps, a `content` array of typed blocks — `text` blocks carry `text`, and the media blocks carry `data` (base64) or `uri` plus `mime_type`. A Router call answers with the model's output steps only; a caller that reads an interaction back over the proxy's own retrieval route gets the whole timeline, user input included, and must select on `type: model_output` rather than assume the first step is the answer. Field names follow Google's current reference: the pre-May-2026 `outputs[]` array this family used to answer with was removed on 2026-06-08 and is NOT part of this contract.\n`status` is the caller's completion key, and it is the only one. Router pins it: a document is answered to a Router caller ONLY for `status: completed`, which is why this schema requires that value. `in_progress` and `requires_action` — a queued handle shaped like a result — and every other terminal status (`failed`, `cancelled`, `incomplete`, `budget_exceeded`, or one Comfy does not recognise) are surfaced as a Comfy Router error carrying the provider's untouched body, not as a 200. On a captured, non-streamed response an `in_progress` is a FAILURE of this contract, not a partial success.\nTwo request fields are accepted and ignored rather than honoured: `stream` and `background` are both forwarded to the provider as false, so a Router call always resolves to one captured, synchronous document. Sending either as true is not an error and does not change the result.\n`usage` is Google's own per-modality token accounting — Google's numbers, not the Comfy charge. Note that the presence of `usage` is what makes a call billable: a document that reports usage is metered even when its `status` is a refusal such as `failed` or `incomplete`, because the model ran and consumed tokens. A call that reports no `usage` is not metered. Media returned with `delivery: uri` is referenced by URI; Router returns the referencing document and does not relay the fetch, so retrieving the asset is the caller's.","example":{"id":"interactions/3f6c1a90-2b47-4d18-9a55-7c0e8b21d4f3","object":"interaction","status":"completed","steps":[{"content":[{"text":"ok","type":"text"}],"type":"model_output"}],"usage":{"input_tokens_by_modality":[{"modality":"text","tokens":9}],"output_tokens_by_modality":[{"modality":"text","tokens":2}],"total_cached_tokens":0,"total_input_tokens":9,"total_output_tokens":2,"total_thought_tokens":0,"total_tokens":11}}}}}}}}}},"components":{"schemas":{"GeminiInteraction":{"additionalProperties":true,"description":"A Gemini Interactions API resource. Most fields pass through; the proxy reads `status` and `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"object":{"type":"string"},"status":{"description":"One of `in_progress`, `requires_action`, `completed`, `failed`, `cancelled`, `incomplete`, `budget_exceeded`.","type":"string"},"steps":{"description":"Interaction history (user input, thoughts, model outputs with inline media).","x-go-type":"interface{}"},"usage":{"$ref":"#/components/schemas/GeminiInteractionUsage"}},"type":"object"},"GeminiInteractionModalityTokens":{"description":"Token count for one modality.","properties":{"modality":{"description":"One of `text`, `image`, `audio`, `video`, `document`.","type":"string"},"tokens":{"type":"integer"}},"type":"object"},"GeminiInteractionUsage":{"additionalProperties":true,"description":"Token usage for a Gemini interaction.","properties":{"input_tokens_by_modality":{"items":{"$ref":"#/components/schemas/GeminiInteractionModalityTokens"},"type":"array"},"output_tokens_by_modality":{"items":{"$ref":"#/components/schemas/GeminiInteractionModalityTokens"},"type":"array"},"total_cached_tokens":{"type":"integer"},"total_input_tokens":{"type":"integer"},"total_output_tokens":{"type":"integer"},"total_thought_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"gemini-interactions/gemini-omni-1.1-flash","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/gemini-interactions/gemini-omni-flash-preview.json b/router-schemas/gemini-interactions/gemini-omni-flash-preview.json new file mode 100644 index 000000000..84031fe05 --- /dev/null +++ b/router-schemas/gemini-interactions/gemini-omni-flash-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"gemini-interactions/gemini-omni-flash-preview","description":"The request body Comfy Router accepts for the model \"gemini-interactions/gemini-omni-flash-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1d1fc88da5ec"},"paths":{"/v2/models/gemini-interactions/gemini-omni-flash-preview":{"post":{"operationId":"runRouterModel","summary":"Run gemini-interactions/gemini-omni-flash-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiInteraction"},{"properties":{"status":{"description":"Always `completed` on a Router response. The provider's other statuses (`in_progress`, `requires_action`, `failed`, `cancelled`, `incomplete`, `budget_exceeded`) do not reach a caller through Router as a 200; they are returned as a Comfy Router error carrying the provider's body.","enum":["completed"],"type":"string"},"steps":{"description":"The interaction timeline, in order. Narrowed here from the untyped `steps` on `GeminiInteraction` so the output leaf is addressable; the provider keeps adding step types, so an item is `additionalProperties: true` and only the fields a Router caller reads are declared.","items":{"additionalProperties":true,"properties":{"content":{"description":"The typed content blocks of this step.","items":{"additionalProperties":true,"properties":{"data":{"description":"Base64-encoded inline media, on a media block delivered inline. Google caps inline media at 4 MB and requires `delivery: uri` above it.","type":"string"},"mime_type":{"description":"Media type of `data` or `uri`, on a media block.","type":"string"},"text":{"description":"The generated text. Present on a `text` block, and this is the leaf the nightly Router SDK case asserts (`steps[].content[].text`).","type":"string"},"type":{"description":"Block kind — `text`, `image`, `audio`, `video` or `document`.","type":"string"},"uri":{"description":"Reference to media delivered out of band, fetched by the caller from the URI it names.","type":"string"}},"type":"object"},"type":"array"},"type":{"description":"Step kind. `model_output` is the generated answer; `user_input` is the caller's own turn echoed back by the retrieval route; `thought` is internal reasoning. The tool steps (`function_call`, `function_result`, `code_execution_call`, `google_search_call`, ...) are open-ended and Google adds to them.","type":"string"}},"type":"object"},"type":"array"}},"required":["status","steps"],"type":"object"}],"description":"Comfy Router output schema for the Gemini Interactions omni models: the `POST /v1beta/interactions` response document, forwarded unchanged. The operation is direct-return — one call answers with the finished document, and Router never continues the interaction on the caller's behalf. Driving an interaction across turns is the caller's own business against the Gemini Interactions proxy routes.\nThe generated text is at `steps[].content[].text`. `steps` is the interaction timeline: each step carries a `type` discriminator (`model_output`, `user_input`, `thought`, the `*_call`/`*_result` tool steps) and, for the output steps, a `content` array of typed blocks — `text` blocks carry `text`, and the media blocks carry `data` (base64) or `uri` plus `mime_type`. A Router call answers with the model's output steps only; a caller that reads an interaction back over the proxy's own retrieval route gets the whole timeline, user input included, and must select on `type: model_output` rather than assume the first step is the answer. Field names follow Google's current reference: the pre-May-2026 `outputs[]` array this family used to answer with was removed on 2026-06-08 and is NOT part of this contract.\n`status` is the caller's completion key, and it is the only one. Router pins it: a document is answered to a Router caller ONLY for `status: completed`, which is why this schema requires that value. `in_progress` and `requires_action` — a queued handle shaped like a result — and every other terminal status (`failed`, `cancelled`, `incomplete`, `budget_exceeded`, or one Comfy does not recognise) are surfaced as a Comfy Router error carrying the provider's untouched body, not as a 200. On a captured, non-streamed response an `in_progress` is a FAILURE of this contract, not a partial success.\nTwo request fields are accepted and ignored rather than honoured: `stream` and `background` are both forwarded to the provider as false, so a Router call always resolves to one captured, synchronous document. Sending either as true is not an error and does not change the result.\n`usage` is Google's own per-modality token accounting — Google's numbers, not the Comfy charge. Note that the presence of `usage` is what makes a call billable: a document that reports usage is metered even when its `status` is a refusal such as `failed` or `incomplete`, because the model ran and consumed tokens. A call that reports no `usage` is not metered. Media returned with `delivery: uri` is referenced by URI; Router returns the referencing document and does not relay the fetch, so retrieving the asset is the caller's.","example":{"id":"interactions/3f6c1a90-2b47-4d18-9a55-7c0e8b21d4f3","object":"interaction","status":"completed","steps":[{"content":[{"text":"ok","type":"text"}],"type":"model_output"}],"usage":{"input_tokens_by_modality":[{"modality":"text","tokens":9}],"output_tokens_by_modality":[{"modality":"text","tokens":2}],"total_cached_tokens":0,"total_input_tokens":9,"total_output_tokens":2,"total_thought_tokens":0,"total_tokens":11}}}}}}}}}},"components":{"schemas":{"GeminiInteraction":{"additionalProperties":true,"description":"A Gemini Interactions API resource. Most fields pass through; the proxy reads `status` and `usage` for billing.","properties":{"id":{"type":"string"},"model":{"type":"string"},"object":{"type":"string"},"status":{"description":"One of `in_progress`, `requires_action`, `completed`, `failed`, `cancelled`, `incomplete`, `budget_exceeded`.","type":"string"},"steps":{"description":"Interaction history (user input, thoughts, model outputs with inline media).","x-go-type":"interface{}"},"usage":{"$ref":"#/components/schemas/GeminiInteractionUsage"}},"type":"object"},"GeminiInteractionModalityTokens":{"description":"Token count for one modality.","properties":{"modality":{"description":"One of `text`, `image`, `audio`, `video`, `document`.","type":"string"},"tokens":{"type":"integer"}},"type":"object"},"GeminiInteractionUsage":{"additionalProperties":true,"description":"Token usage for a Gemini interaction.","properties":{"input_tokens_by_modality":{"items":{"$ref":"#/components/schemas/GeminiInteractionModalityTokens"},"type":"array"},"output_tokens_by_modality":{"items":{"$ref":"#/components/schemas/GeminiInteractionModalityTokens"},"type":"array"},"total_cached_tokens":{"type":"integer"},"total_input_tokens":{"type":"integer"},"total_output_tokens":{"type":"integer"},"total_thought_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"gemini-interactions/gemini-omni-flash-preview","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/ideogram/ideogram-v4.json b/router-schemas/ideogram/ideogram-v4.json new file mode 100644 index 000000000..eb337a57b --- /dev/null +++ b/router-schemas/ideogram/ideogram-v4.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"ideogram/ideogram-v4","description":"The request body Comfy Router accepts for the model \"ideogram/ideogram-v4\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"59e2ff805ee0"},"paths":{"/v2/models/ideogram/ideogram-v4":{"post":{"operationId":"runRouterModel","summary":"Run ideogram/ideogram-v4 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"description":"Parameters for the Ideogram 4.0 (V4) text-to-image generation proxy request. Supply exactly one of text_prompt or json_prompt.","example":{"rendering_speed":"DEFAULT","text_prompt":"A poster for a jazz festival, bold typography, warm colours"},"minProperties":1,"properties":{"enable_copyright_detection":{"description":"Opt into post-generation copyright detection (Hive likeness and logo checks).","type":"boolean"},"json_prompt":{"additionalProperties":true,"description":"Structured V4 prompt. Disables Magic Prompt; consumed directly. Supply exactly one of text_prompt or json_prompt.","type":"object"},"rendering_speed":{"$ref":"#/components/schemas/RenderingSpeed"},"resolution":{"description":"Output resolution in WIDTHxHEIGHT. Omit to let the model pick an aspect ratio. Supported 2K values: 2048x2048, 1440x2880, 2880x1440, 1664x2496, 2496x1664, 1792x2240, 2240x1792, 1440x2560, 2560x1440, 1600x2560, 2560x1600, 1728x2304, 2304x1728, 1296x3168, 3168x1296, 1152x2944, 2944x1152, 1248x3328, 3328x1248, 1280x3072, 3072x1280.","example":"2048x2048","type":"string"},"text_prompt":{"description":"Natural-language prompt. Enables Magic Prompt automatically. Supply exactly one of text_prompt or json_prompt.","type":"string"}},"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/IdeogramGenerateResponse"}],"description":"Comfy Router output schema for `ideogram/ideogram-v4`: Ideogram's generate response, forwarded unchanged. The generated images are in `data`, each with its URL at `data[].url`; `data[].is_image_safe` reports Ideogram's own moderation verdict for that image.\n`routerresult/classification.go` classifies the `ideogram /ideogram-v4/generate` operation this ID resolves to as direct-return, so `POST /v2/models/ideogram/ideogram-v4` answers this document on the original call. `/proxy/ideogram/ideogram-v4/generate` returns the same document, as it always has.","example":{"created":"2026-01-01T00:00:00Z","data":[{"is_image_safe":true,"prompt":"A poster for a jazz festival, bold typography, warm colours","resolution":"2048x2048","seed":918273645,"style_type":"REALISTIC","url":"https://example.invalid/ideogram/ideogram-v4/generated.png"}]}}}}}}}}},"components":{"schemas":{"IdeogramGenerateResponse":{"description":"Response from the Ideogram image generation API.","properties":{"created":{"description":"Timestamp when the generation was created.","format":"date-time","type":"string"},"data":{"description":"Array of generated image information.","items":{"properties":{"is_image_safe":{"description":"Indicates whether the image is considered safe.","type":"boolean"},"prompt":{"description":"The prompt used to generate this image.","type":"string"},"resolution":{"description":"The resolution of the generated image (e.g., '1024x1024').","type":"string"},"seed":{"description":"The seed value used for this generation.","type":"integer"},"style_type":{"description":"The style type used for generation (e.g., 'REALISTIC', 'ANIME').","type":"string"},"url":{"description":"URL to the generated image.","type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"RenderingSpeed":{"default":"DEFAULT","description":"The rendering speed setting that controls the trade-off between generation speed and quality","enum":["DEFAULT","TURBO","QUALITY"],"type":"string"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"ideogram/ideogram-v4","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-3.0-turbo.json b/router-schemas/kling/kling-3.0-turbo.json new file mode 100644 index 000000000..66f514980 --- /dev/null +++ b/router-schemas/kling/kling-3.0-turbo.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-3.0-turbo","description":"The request body Comfy Router accepts for the model \"kling/kling-3.0-turbo\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"8b6725c99193"},"paths":{"/v2/models/kling/kling-3.0-turbo":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-3.0-turbo synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingV2QueryTaskResponse"}],"description":"Comfy Router output schema for Kling 3.0 Turbo: the terminal `GET /tasks?task_ids={id}` document, forwarded unchanged. The operation is submit-and-poll — `routerresult/classification.go` records `{provider: kling, endpoint: /text-to-video/kling-3.0-turbo}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `data.id` handle the underlying submit answers with.\nThis is NOT the document the eleven Kling v1 video ids answer (`KlingVideoRouterOutput`), and the three differences are the reason it is its own component. `data` is a LIST — one element per task id the query named — rather than a single object, so the generated video's download URL is at `data[].outputs[].url`, under the element for the polled task. Router always polls ONE task (the poll route is built from the single id the submit returned, so the request is always `?task_ids=\u003cone id\u003e`), so that list carries one element in practice; a caller polling this route directly with several ids gets one element each and must match on `data[].id`. Second, the terminal status spells `succeeded`, where every v1 Kling route says `succeed`. Third, the outputs live under `outputs[]` rather than `task_result`.\nRead the ELEMENT field, not the `outputs` container: `KlingV2Output` is `{type, id, url, watermark_url, duration, ...}`, so a one-element list carrying an id and a type and no URL is a non-empty array holding nothing playable. `data[].outputs[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result. `mp3_url` and `wav_url` deliberately do NOT: the operation Router serves here is text-to-video, so a success carrying only audio is a shape anomaly. `routerpollstate.classifyKlingV2` draws exactly those lines.\n`url` is hotlink-protected and Kling clears it after 30 days, so download it rather than storing the link.","example":{"code":0,"data":[{"create_time":1798761600000,"id":"kling-v2-task-7c8d9e0f1a2b","message":"","outputs":[{"duration":"5","id":"kling-v2-output-2b1a0f9e8d7c","type":"video","url":"https://example.invalid/kling/kling-3.0-turbo/generated.mp4"}],"status":"succeeded","update_time":1798761820000}],"message":"SUCCEED","request_id":"3d7e5c91-0b42-4f68-9a13-8e2c6d4b0a75"}}}}}}}}},"components":{"schemas":{"KlingV2Output":{"description":"A generated output. The fields present depend on `type` (video, image, audio, voice or element).","properties":{"duration":{"description":"Duration of the generated video in seconds.","type":"string"},"group_id":{"description":"Grouping marker, present only for grouped images.","type":"string"},"id":{"description":"Output ID generated by the system.","type":"string"},"mp3_duration":{"description":"Duration of the generated MP3 audio in seconds.","type":"string"},"mp3_url":{"description":"MP3 URL of the generated audio (hotlink-protected).","type":"string"},"name":{"description":"Name of the generated material.","type":"string"},"owned_by":{"description":"Source of the material. \"kling\" denotes the official library; numbers are creator IDs.","type":"string"},"status":{"description":"Status of the material. One of \"succeeded\" or \"deleted\".","type":"string"},"type":{"description":"Output content type. One of \"video\", \"image\", \"audio\", \"voice\" or \"element\".","type":"string"},"url":{"description":"URL of the generated result (hotlink-protected). Cleared after 30 days.","type":"string"},"watermark_url":{"description":"URL of the watermarked result (hotlink-protected).","type":"string"},"wav_duration":{"description":"Duration of the generated WAV audio in seconds.","type":"string"},"wav_url":{"description":"WAV URL of the generated audio (hotlink-protected).","type":"string"}},"type":"object"},"KlingV2QueryTaskResponse":{"description":"Response returned when querying Kling 3.0 Turbo tasks by ID.","properties":{"code":{"description":"Error code. 0 indicates success.","type":"integer"},"data":{"description":"Tasks matching the query.","items":{"$ref":"#/components/schemas/KlingV2Task"},"type":"array"},"message":{"description":"Error message.","type":"string"},"request_id":{"description":"Request ID generated by the system.","type":"string"}},"type":"object"},"KlingV2Task":{"description":"A single Kling 3.0 Turbo task record.","properties":{"billing":{"description":"Billing details for the task.","items":{"properties":{"amount":{"description":"Consumption amount, accurate to two decimal places.","type":"string"},"charge_type":{"description":"Consumption account type. \"cash\" for balance, \"unit\" for a resource package.","type":"string"},"package_type":{"description":"Consumable resource bundle type (only present when charge_type is \"unit\"). One of \"video\", \"image\" or \"audio\".","type":"string"}},"type":"object"},"type":"array"},"create_time":{"description":"Task creation time. Unix timestamp in milliseconds.","format":"int64","type":"integer"},"external_id":{"description":"The custom task ID for this task, if any.","type":"string"},"id":{"description":"The task ID.","type":"string"},"message":{"description":"Task status information, displaying the failure reason when the task fails.","type":"string"},"outputs":{"description":"Generated outputs for the task.","items":{"$ref":"#/components/schemas/KlingV2Output"},"type":"array"},"status":{"description":"Task status. One of \"submitted\", \"processing\", \"succeeded\" or \"failed\".","type":"string"},"update_time":{"description":"Task update time. Unix timestamp in milliseconds.","format":"int64","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-3.0-turbo","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-image-o1.json b/router-schemas/kling/kling-image-o1.json new file mode 100644 index 000000000..5781a742d --- /dev/null +++ b/router-schemas/kling/kling-image-o1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-image-o1","description":"The request body Comfy Router accepts for the model \"kling/kling-image-o1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"bdf9de1a9aa8"},"paths":{"/v2/models/kling/kling-image-o1":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-image-o1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingOmniImageResponse"}],"description":"Comfy Router output schema for Kling Omni Image: the terminal `GET /v1/images/omni-image/{id}` task-query document, forwarded unchanged. The operation is submit-and-poll — `routerresult/classification.go` records `{provider: kling, endpoint: /v1/images/omni-image}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `data.task_id` handle the underlying submit answers with.\nThe envelope is the same one the Kling v1 VIDEO ids carry (`KlingVideoRouterOutput`) and `data.task_status` is the same four-value vocabulary, terminal success spelled `succeed`. What differs — and the whole reason this is its own component — is the OUTPUT leaf: the generated image is at `data.task_result.images[].url`, typed `KlingImageResult {index, url}`, and `data.task_result.videos[]` is absent on every one of these documents. A caller reusing the video leaf here would read every terminal success as producing nothing.\nRead the ELEMENT `url`, not the `images` container or the element's `index`: `index` is a sequence number, so an element carrying only an index is a non-empty array holding no image. When the request set `result_type: series` the results arrive under `data.task_result.series_images[]` instead, same `{index, url}` shape; `routerpollstate.classifyKlingImage` accepts either leaf and calls a `succeed` with neither `success_without_output`.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-image-task-3c4d5e6f7a8b","task_result":{"images":[{"index":0,"url":"https://example.invalid/kling/kling-image-o1/generated.png"}],"result_type":"single"},"task_status":"succeed","task_status_msg":"","updated_at":1798761660000},"message":"SUCCEED","request_id":"6a4b2c80-1e93-4d57-b8f2-05c7e9a3d146"}}}}}}}}},"components":{"schemas":{"KlingImageResult":{"properties":{"index":{"description":"Image Number (0-9)","type":"integer"},"url":{"description":"URL for generated image","format":"uri","type":"string"}},"type":"object"},"KlingOmniImageResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"description":"Customer-defined task ID","type":"string"}},"type":"object"},"task_result":{"properties":{"images":{"items":{"$ref":"#/components/schemas/KlingImageResult"},"type":"array"},"result_type":{"description":"Whether the result is a single image or a series of images","enum":["single","series"],"type":"string"},"series_images":{"description":"Series images result list","items":{"properties":{"index":{"description":"Series-image sequence number","type":"integer"},"url":{"description":"URL for generated image","format":"uri","type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails (such as triggering the content risk control of the platform, etc.)","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-image-o1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v1-5.json b/router-schemas/kling/kling-v1-5.json new file mode 100644 index 000000000..c064e7803 --- /dev/null +++ b/router-schemas/kling/kling-v1-5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v1-5","description":"The request body Comfy Router accepts for the model \"kling/kling-v1-5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v1-5":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v1-5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v1-5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v1-6.json b/router-schemas/kling/kling-v1-6.json new file mode 100644 index 000000000..3c58abeef --- /dev/null +++ b/router-schemas/kling/kling-v1-6.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v1-6","description":"The request body Comfy Router accepts for the model \"kling/kling-v1-6\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v1-6":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v1-6 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v1-6","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v1.json b/router-schemas/kling/kling-v1.json new file mode 100644 index 000000000..50abccf84 --- /dev/null +++ b/router-schemas/kling/kling-v1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v1","description":"The request body Comfy Router accepts for the model \"kling/kling-v1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v1":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v2-1-master.json b/router-schemas/kling/kling-v2-1-master.json new file mode 100644 index 000000000..828150105 --- /dev/null +++ b/router-schemas/kling/kling-v2-1-master.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v2-1-master","description":"The request body Comfy Router accepts for the model \"kling/kling-v2-1-master\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v2-1-master":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v2-1-master synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v2-1-master","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v2-1.json b/router-schemas/kling/kling-v2-1.json new file mode 100644 index 000000000..d7cf945fa --- /dev/null +++ b/router-schemas/kling/kling-v2-1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v2-1","description":"The request body Comfy Router accepts for the model \"kling/kling-v2-1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v2-1":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v2-1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v2-1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v2-5-turbo.json b/router-schemas/kling/kling-v2-5-turbo.json new file mode 100644 index 000000000..facabe067 --- /dev/null +++ b/router-schemas/kling/kling-v2-5-turbo.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v2-5-turbo","description":"The request body Comfy Router accepts for the model \"kling/kling-v2-5-turbo\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v2-5-turbo":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v2-5-turbo synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v2-5-turbo","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v2-6.json b/router-schemas/kling/kling-v2-6.json new file mode 100644 index 000000000..b9eee7382 --- /dev/null +++ b/router-schemas/kling/kling-v2-6.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v2-6","description":"The request body Comfy Router accepts for the model \"kling/kling-v2-6\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v2-6":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v2-6 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v2-6","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v2-master.json b/router-schemas/kling/kling-v2-master.json new file mode 100644 index 000000000..cebe42d7a --- /dev/null +++ b/router-schemas/kling/kling-v2-master.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v2-master","description":"The request body Comfy Router accepts for the model \"kling/kling-v2-master\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v2-master":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v2-master synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v2-master","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v3-omni.json b/router-schemas/kling/kling-v3-omni.json new file mode 100644 index 000000000..5b18ea19e --- /dev/null +++ b/router-schemas/kling/kling-v3-omni.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v3-omni","description":"The request body Comfy Router accepts for the model \"kling/kling-v3-omni\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v3-omni":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v3-omni synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v3-omni","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-v3.json b/router-schemas/kling/kling-v3.json new file mode 100644 index 000000000..bdc6f7f83 --- /dev/null +++ b/router-schemas/kling/kling-v3.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-v3","description":"The request body Comfy Router accepts for the model \"kling/kling-v3\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-v3":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-v3 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-v3","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/kling/kling-video-o1.json b/router-schemas/kling/kling-video-o1.json new file mode 100644 index 000000000..e1bd4580f --- /dev/null +++ b/router-schemas/kling/kling-video-o1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"kling/kling-video-o1","description":"The request body Comfy Router accepts for the model \"kling/kling-video-o1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"339663e5dc19"},"paths":{"/v2/models/kling/kling-video-o1":{"post":{"operationId":"runRouterModel","summary":"Run kling/kling-video-o1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KlingQueryTaskResponse"}],"description":"Comfy Router output schema for the Kling v1 VIDEO models: the terminal `GET /v1/videos/{operation}/{id}` task-query document, forwarded unchanged. Every Kling v1 operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `/v1/videos/text2video`, `/v1/videos/image2video` and `/v1/videos/omni-video` as `ReturnModeSubmitPoll`, each polled on `\u003csubmit path\u003e/:id` — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{code, message, request_id, data.task_id}` handle the underlying `/proxy/kling/*` submit answers with.\nThe generated video's download URL is at `data.task_result.videos[].url`. Read the ELEMENT field, not the `videos` container: `KlingVideoResult` is `{id, url, watermark_url, duration}`, so a one-element list carrying an id and a duration and no URL is a non-empty array holding nothing playable. `data.task_result.videos[].watermark_url` is the same asset under Kling's hotlink-protection form and counts as a result; `routerpollstate.classifyKling` draws exactly that line, and a `succeed` carrying neither is `success_without_output` rather than a finished generation.\n`data.task_status` is Kling's own four-value vocabulary — `submitted`, `processing`, `succeed`, `failed` — and the terminal success spells it `succeed`, NOT `succeeded`. The `kling/kling-3.0-turbo` sibling (`KlingV2RouterOutput`) says `succeeded` and carries an entirely different document; the two vocabularies are deliberately not folded together.\nThree operations share this component because they share this document, and the ids are grouped by operation in the list above: text2video is the family's primary, so `kling/kling-v1-5` and `kling/kling-v2-1` — the two ids text2video's allowlist does not admit — resolve onto image2video, and `kling/kling-v3-omni` and `kling/kling-video-o1` resolve onto omni-video.","example":{"code":0,"data":{"created_at":1798761600000,"task_id":"kling-task-1a2b3c4d5e6f","task_result":{"videos":[{"duration":"5","id":"kling-video-6f5e4d3c2b1a","url":"https://example.invalid/kling/kling-v1/generated.mp4"}]},"task_status":"succeed","task_status_msg":"","updated_at":1798761840000},"message":"SUCCEED","request_id":"9f2c1a04-7b6e-4d38-8a51-3c0e7d9b2f46"}}}}}}}}},"components":{"schemas":{"KlingQueryTaskResponse":{"properties":{"code":{"description":"Error code","type":"integer"},"data":{"properties":{"created_at":{"description":"Task creation time, Unix timestamp in milliseconds","type":"integer"},"final_unit_deduction":{"description":"The deduction units of task","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_info":{"properties":{"external_task_id":{"type":"string"}},"type":"object"},"task_result":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/KlingVideoResult"},"type":"array"}},"type":"object"},"task_status":{"$ref":"#/components/schemas/KlingTaskStatus"},"task_status_msg":{"description":"Task status information, displaying the failure reason when the task fails","type":"string"},"updated_at":{"description":"Task update time, Unix timestamp in milliseconds","type":"integer"},"watermark_info":{"properties":{"enabled":{"type":"boolean"}},"type":"object"}},"type":"object"},"message":{"description":"Error message","type":"string"},"request_id":{"description":"Request ID","type":"string"}},"type":"object"},"KlingTaskStatus":{"description":"Task Status","enum":["submitted","processing","succeed","failed"],"type":"string"},"KlingVideoResult":{"properties":{"duration":{"description":"Total video duration in seconds","type":"string"},"id":{"description":"Generated video ID","type":"string"},"url":{"description":"URL for generated video","format":"uri","type":"string"},"watermark_url":{"description":"URL for generated video with watermark, hotlink protection format","format":"uri","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"kling/kling-video-o1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/krea/krea-2-large.json b/router-schemas/krea/krea-2-large.json new file mode 100644 index 000000000..930b929f4 --- /dev/null +++ b/router-schemas/krea/krea-2-large.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"krea/krea-2-large","description":"The request body Comfy Router accepts for the model \"krea/krea-2-large\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5de42bedeae4"},"paths":{"/v2/models/krea/krea-2-large":{"post":{"operationId":"runRouterModel","summary":"Run krea/krea-2-large synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KreaJob"},{"properties":{"result":{"description":"The finished generation. Not nullable here, unlike on `KreaJob`: a terminal job whose `result` carries no `urls` is answered as a Comfy Router error, so a `200` always carries it.","properties":{"style_id":{"description":"Set by loraTraining jobs rather than by a generation; a `200` for these models carries `urls`.","type":"string"},"urls":{"description":"The generated images, as downloadable links. At least one, because a `completed` job with an empty `urls` is `success_without_output` and never reaches this document.","items":{"format":"uri","type":"string"},"minItems":1,"type":"array"}},"required":["urls"],"type":"object"},"status":{"description":"Always `completed` on this document: it is the only status `classifyKrea` answers `succeeded` on, and Krea's vocabulary is matched without a case fold.","enum":["completed"],"type":"string"}},"required":["status","result"],"type":"object"}],"description":"Comfy Router output schema for the Krea 2 image models: the terminal `GET /proxy/krea/jobs/{job_id}` job document, forwarded unchanged. Every Krea generate operation is SUBMIT-AND-POLL — `POST /proxy/krea/generate/image/krea/krea-2/{size}` answers with this same `KreaJob` document in a NOT-YET-FINISHED state (a `job_id`, a pending `status`, a null `result`), and Router polls the status route on the caller's behalf (`routerresult/classification.go` records all three size operations as `ReturnModeSubmitPoll` on `GET /jobs/{job_id}`; `routerpollstate/families.go` `FamilyKrea` owns the terminal-state rule). So the body a caller receives is the finished job rather than that pending handle. The three sizes — medium, medium-turbo and large — share one status route and this one document shape, and `krea/krea-2` resolves to the same medium operation `krea/krea-2-medium` does.\nThe generated images are at `result.urls[]`, an array of downloadable links. `result` is null until the job finishes, and a caller must key completion off `result.urls` rather than off `result` being present: `KreaJobResult`'s other field, `style_id`, is what a loraTraining job returns, so a `result` carrying only a style id is a job that generated no image. `routerpollstate` draws the same line — a `completed` whose `result.urls` is empty is `success_without_output`, not a finished generation.\n`status` is Krea's own lowercase nine-value vocabulary — `backlogged`, `queued`, `scheduled`, `processing`, `sampling`, `intermediate-complete`, `completed`, `failed`, `cancelled` — forwarded unchanged and compared without a case fold. `completed` is the only terminal SUCCESS. `intermediate-complete` reads like a terminal state and is not: it is a partial preview of a generation that is still running, and polling continues through it — treating it as finished would return a preview as the result. `failed` and `cancelled` are both terminal FAILURES; a cancelled job is one the provider stopped, not a success that produced nothing. Through Router that distinction is already settled, because a terminal job carrying no `result.urls` is answered as a Comfy Router error rather than with the provider document.\nThe composition NARROWS `KreaJob` rather than republishing it, because `KreaJob` describes every poll of the job — including the pending ones the caller never sees — while this document describes only the body a `200` carries. `classifyKrea` (`routerpollstate/families.go`) answers `succeeded` on exactly one shape: `status` `completed` AND `nonEmptyAt(\"result\", \"urls\")`. Every other reading is a Comfy Router error rather than this document, so `status` is narrowed to the single terminal success and `result` is required non-null with a non-empty `urls`. Left unnarrowed, the schema would publish `intermediate-complete` with a null `result` as a valid `200` — the preview-as-result outcome the paragraph above says Router forbids — and a generated client would type the finished asset as optional. Narrowing `status` here is safe in a way it is NOT for BFL (see `BFLRouterResultOutput`, which declines the same narrowing): Krea's vocabulary is compared WITHOUT a case fold, so `completed` is the only spelling that can reach a `200`. `minItems: 1` and not a per-item `minLength`, because Router's rule is that at least ONE element is non-empty, not that every one is.","example":{"completed_at":"2027-01-01T00:00:37Z","created_at":"2027-01-01T00:00:00Z","job_id":"7f1c2e84-5b90-4a37-8d61-2c0f9ab4e153","result":{"urls":["https://example.invalid/krea/krea-2/generated.png"]},"status":"completed"}}}}}}}}},"components":{"schemas":{"KreaJob":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"job_id":{"format":"uuid","type":"string"},"result":{"allOf":[{"$ref":"#/components/schemas/KreaJobResult"}],"nullable":true},"status":{"description":"Available options: backlogged, queued, scheduled, processing, sampling, intermediate-complete, completed, failed, cancelled","enum":["backlogged","queued","scheduled","processing","sampling","intermediate-complete","completed","failed","cancelled"],"type":"string"}},"required":["job_id","status","created_at","completed_at","result"],"type":"object"},"KreaJobResult":{"properties":{"style_id":{"type":"string"},"urls":{"items":{"format":"uri","type":"string"},"type":"array"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"krea/krea-2-large","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/krea/krea-2-medium-turbo.json b/router-schemas/krea/krea-2-medium-turbo.json new file mode 100644 index 000000000..935f20871 --- /dev/null +++ b/router-schemas/krea/krea-2-medium-turbo.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"krea/krea-2-medium-turbo","description":"The request body Comfy Router accepts for the model \"krea/krea-2-medium-turbo\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5de42bedeae4"},"paths":{"/v2/models/krea/krea-2-medium-turbo":{"post":{"operationId":"runRouterModel","summary":"Run krea/krea-2-medium-turbo synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KreaJob"},{"properties":{"result":{"description":"The finished generation. Not nullable here, unlike on `KreaJob`: a terminal job whose `result` carries no `urls` is answered as a Comfy Router error, so a `200` always carries it.","properties":{"style_id":{"description":"Set by loraTraining jobs rather than by a generation; a `200` for these models carries `urls`.","type":"string"},"urls":{"description":"The generated images, as downloadable links. At least one, because a `completed` job with an empty `urls` is `success_without_output` and never reaches this document.","items":{"format":"uri","type":"string"},"minItems":1,"type":"array"}},"required":["urls"],"type":"object"},"status":{"description":"Always `completed` on this document: it is the only status `classifyKrea` answers `succeeded` on, and Krea's vocabulary is matched without a case fold.","enum":["completed"],"type":"string"}},"required":["status","result"],"type":"object"}],"description":"Comfy Router output schema for the Krea 2 image models: the terminal `GET /proxy/krea/jobs/{job_id}` job document, forwarded unchanged. Every Krea generate operation is SUBMIT-AND-POLL — `POST /proxy/krea/generate/image/krea/krea-2/{size}` answers with this same `KreaJob` document in a NOT-YET-FINISHED state (a `job_id`, a pending `status`, a null `result`), and Router polls the status route on the caller's behalf (`routerresult/classification.go` records all three size operations as `ReturnModeSubmitPoll` on `GET /jobs/{job_id}`; `routerpollstate/families.go` `FamilyKrea` owns the terminal-state rule). So the body a caller receives is the finished job rather than that pending handle. The three sizes — medium, medium-turbo and large — share one status route and this one document shape, and `krea/krea-2` resolves to the same medium operation `krea/krea-2-medium` does.\nThe generated images are at `result.urls[]`, an array of downloadable links. `result` is null until the job finishes, and a caller must key completion off `result.urls` rather than off `result` being present: `KreaJobResult`'s other field, `style_id`, is what a loraTraining job returns, so a `result` carrying only a style id is a job that generated no image. `routerpollstate` draws the same line — a `completed` whose `result.urls` is empty is `success_without_output`, not a finished generation.\n`status` is Krea's own lowercase nine-value vocabulary — `backlogged`, `queued`, `scheduled`, `processing`, `sampling`, `intermediate-complete`, `completed`, `failed`, `cancelled` — forwarded unchanged and compared without a case fold. `completed` is the only terminal SUCCESS. `intermediate-complete` reads like a terminal state and is not: it is a partial preview of a generation that is still running, and polling continues through it — treating it as finished would return a preview as the result. `failed` and `cancelled` are both terminal FAILURES; a cancelled job is one the provider stopped, not a success that produced nothing. Through Router that distinction is already settled, because a terminal job carrying no `result.urls` is answered as a Comfy Router error rather than with the provider document.\nThe composition NARROWS `KreaJob` rather than republishing it, because `KreaJob` describes every poll of the job — including the pending ones the caller never sees — while this document describes only the body a `200` carries. `classifyKrea` (`routerpollstate/families.go`) answers `succeeded` on exactly one shape: `status` `completed` AND `nonEmptyAt(\"result\", \"urls\")`. Every other reading is a Comfy Router error rather than this document, so `status` is narrowed to the single terminal success and `result` is required non-null with a non-empty `urls`. Left unnarrowed, the schema would publish `intermediate-complete` with a null `result` as a valid `200` — the preview-as-result outcome the paragraph above says Router forbids — and a generated client would type the finished asset as optional. Narrowing `status` here is safe in a way it is NOT for BFL (see `BFLRouterResultOutput`, which declines the same narrowing): Krea's vocabulary is compared WITHOUT a case fold, so `completed` is the only spelling that can reach a `200`. `minItems: 1` and not a per-item `minLength`, because Router's rule is that at least ONE element is non-empty, not that every one is.","example":{"completed_at":"2027-01-01T00:00:37Z","created_at":"2027-01-01T00:00:00Z","job_id":"7f1c2e84-5b90-4a37-8d61-2c0f9ab4e153","result":{"urls":["https://example.invalid/krea/krea-2/generated.png"]},"status":"completed"}}}}}}}}},"components":{"schemas":{"KreaJob":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"job_id":{"format":"uuid","type":"string"},"result":{"allOf":[{"$ref":"#/components/schemas/KreaJobResult"}],"nullable":true},"status":{"description":"Available options: backlogged, queued, scheduled, processing, sampling, intermediate-complete, completed, failed, cancelled","enum":["backlogged","queued","scheduled","processing","sampling","intermediate-complete","completed","failed","cancelled"],"type":"string"}},"required":["job_id","status","created_at","completed_at","result"],"type":"object"},"KreaJobResult":{"properties":{"style_id":{"type":"string"},"urls":{"items":{"format":"uri","type":"string"},"type":"array"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"krea/krea-2-medium-turbo","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/krea/krea-2-medium.json b/router-schemas/krea/krea-2-medium.json new file mode 100644 index 000000000..5044019eb --- /dev/null +++ b/router-schemas/krea/krea-2-medium.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"krea/krea-2-medium","description":"The request body Comfy Router accepts for the model \"krea/krea-2-medium\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5de42bedeae4"},"paths":{"/v2/models/krea/krea-2-medium":{"post":{"operationId":"runRouterModel","summary":"Run krea/krea-2-medium synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KreaJob"},{"properties":{"result":{"description":"The finished generation. Not nullable here, unlike on `KreaJob`: a terminal job whose `result` carries no `urls` is answered as a Comfy Router error, so a `200` always carries it.","properties":{"style_id":{"description":"Set by loraTraining jobs rather than by a generation; a `200` for these models carries `urls`.","type":"string"},"urls":{"description":"The generated images, as downloadable links. At least one, because a `completed` job with an empty `urls` is `success_without_output` and never reaches this document.","items":{"format":"uri","type":"string"},"minItems":1,"type":"array"}},"required":["urls"],"type":"object"},"status":{"description":"Always `completed` on this document: it is the only status `classifyKrea` answers `succeeded` on, and Krea's vocabulary is matched without a case fold.","enum":["completed"],"type":"string"}},"required":["status","result"],"type":"object"}],"description":"Comfy Router output schema for the Krea 2 image models: the terminal `GET /proxy/krea/jobs/{job_id}` job document, forwarded unchanged. Every Krea generate operation is SUBMIT-AND-POLL — `POST /proxy/krea/generate/image/krea/krea-2/{size}` answers with this same `KreaJob` document in a NOT-YET-FINISHED state (a `job_id`, a pending `status`, a null `result`), and Router polls the status route on the caller's behalf (`routerresult/classification.go` records all three size operations as `ReturnModeSubmitPoll` on `GET /jobs/{job_id}`; `routerpollstate/families.go` `FamilyKrea` owns the terminal-state rule). So the body a caller receives is the finished job rather than that pending handle. The three sizes — medium, medium-turbo and large — share one status route and this one document shape, and `krea/krea-2` resolves to the same medium operation `krea/krea-2-medium` does.\nThe generated images are at `result.urls[]`, an array of downloadable links. `result` is null until the job finishes, and a caller must key completion off `result.urls` rather than off `result` being present: `KreaJobResult`'s other field, `style_id`, is what a loraTraining job returns, so a `result` carrying only a style id is a job that generated no image. `routerpollstate` draws the same line — a `completed` whose `result.urls` is empty is `success_without_output`, not a finished generation.\n`status` is Krea's own lowercase nine-value vocabulary — `backlogged`, `queued`, `scheduled`, `processing`, `sampling`, `intermediate-complete`, `completed`, `failed`, `cancelled` — forwarded unchanged and compared without a case fold. `completed` is the only terminal SUCCESS. `intermediate-complete` reads like a terminal state and is not: it is a partial preview of a generation that is still running, and polling continues through it — treating it as finished would return a preview as the result. `failed` and `cancelled` are both terminal FAILURES; a cancelled job is one the provider stopped, not a success that produced nothing. Through Router that distinction is already settled, because a terminal job carrying no `result.urls` is answered as a Comfy Router error rather than with the provider document.\nThe composition NARROWS `KreaJob` rather than republishing it, because `KreaJob` describes every poll of the job — including the pending ones the caller never sees — while this document describes only the body a `200` carries. `classifyKrea` (`routerpollstate/families.go`) answers `succeeded` on exactly one shape: `status` `completed` AND `nonEmptyAt(\"result\", \"urls\")`. Every other reading is a Comfy Router error rather than this document, so `status` is narrowed to the single terminal success and `result` is required non-null with a non-empty `urls`. Left unnarrowed, the schema would publish `intermediate-complete` with a null `result` as a valid `200` — the preview-as-result outcome the paragraph above says Router forbids — and a generated client would type the finished asset as optional. Narrowing `status` here is safe in a way it is NOT for BFL (see `BFLRouterResultOutput`, which declines the same narrowing): Krea's vocabulary is compared WITHOUT a case fold, so `completed` is the only spelling that can reach a `200`. `minItems: 1` and not a per-item `minLength`, because Router's rule is that at least ONE element is non-empty, not that every one is.","example":{"completed_at":"2027-01-01T00:00:37Z","created_at":"2027-01-01T00:00:00Z","job_id":"7f1c2e84-5b90-4a37-8d61-2c0f9ab4e153","result":{"urls":["https://example.invalid/krea/krea-2/generated.png"]},"status":"completed"}}}}}}}}},"components":{"schemas":{"KreaJob":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"job_id":{"format":"uuid","type":"string"},"result":{"allOf":[{"$ref":"#/components/schemas/KreaJobResult"}],"nullable":true},"status":{"description":"Available options: backlogged, queued, scheduled, processing, sampling, intermediate-complete, completed, failed, cancelled","enum":["backlogged","queued","scheduled","processing","sampling","intermediate-complete","completed","failed","cancelled"],"type":"string"}},"required":["job_id","status","created_at","completed_at","result"],"type":"object"},"KreaJobResult":{"properties":{"style_id":{"type":"string"},"urls":{"items":{"format":"uri","type":"string"},"type":"array"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"krea/krea-2-medium","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/krea/krea-2.json b/router-schemas/krea/krea-2.json new file mode 100644 index 000000000..1e623af8d --- /dev/null +++ b/router-schemas/krea/krea-2.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"krea/krea-2","description":"The request body Comfy Router accepts for the model \"krea/krea-2\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5de42bedeae4"},"paths":{"/v2/models/krea/krea-2":{"post":{"operationId":"runRouterModel","summary":"Run krea/krea-2 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/KreaJob"},{"properties":{"result":{"description":"The finished generation. Not nullable here, unlike on `KreaJob`: a terminal job whose `result` carries no `urls` is answered as a Comfy Router error, so a `200` always carries it.","properties":{"style_id":{"description":"Set by loraTraining jobs rather than by a generation; a `200` for these models carries `urls`.","type":"string"},"urls":{"description":"The generated images, as downloadable links. At least one, because a `completed` job with an empty `urls` is `success_without_output` and never reaches this document.","items":{"format":"uri","type":"string"},"minItems":1,"type":"array"}},"required":["urls"],"type":"object"},"status":{"description":"Always `completed` on this document: it is the only status `classifyKrea` answers `succeeded` on, and Krea's vocabulary is matched without a case fold.","enum":["completed"],"type":"string"}},"required":["status","result"],"type":"object"}],"description":"Comfy Router output schema for the Krea 2 image models: the terminal `GET /proxy/krea/jobs/{job_id}` job document, forwarded unchanged. Every Krea generate operation is SUBMIT-AND-POLL — `POST /proxy/krea/generate/image/krea/krea-2/{size}` answers with this same `KreaJob` document in a NOT-YET-FINISHED state (a `job_id`, a pending `status`, a null `result`), and Router polls the status route on the caller's behalf (`routerresult/classification.go` records all three size operations as `ReturnModeSubmitPoll` on `GET /jobs/{job_id}`; `routerpollstate/families.go` `FamilyKrea` owns the terminal-state rule). So the body a caller receives is the finished job rather than that pending handle. The three sizes — medium, medium-turbo and large — share one status route and this one document shape, and `krea/krea-2` resolves to the same medium operation `krea/krea-2-medium` does.\nThe generated images are at `result.urls[]`, an array of downloadable links. `result` is null until the job finishes, and a caller must key completion off `result.urls` rather than off `result` being present: `KreaJobResult`'s other field, `style_id`, is what a loraTraining job returns, so a `result` carrying only a style id is a job that generated no image. `routerpollstate` draws the same line — a `completed` whose `result.urls` is empty is `success_without_output`, not a finished generation.\n`status` is Krea's own lowercase nine-value vocabulary — `backlogged`, `queued`, `scheduled`, `processing`, `sampling`, `intermediate-complete`, `completed`, `failed`, `cancelled` — forwarded unchanged and compared without a case fold. `completed` is the only terminal SUCCESS. `intermediate-complete` reads like a terminal state and is not: it is a partial preview of a generation that is still running, and polling continues through it — treating it as finished would return a preview as the result. `failed` and `cancelled` are both terminal FAILURES; a cancelled job is one the provider stopped, not a success that produced nothing. Through Router that distinction is already settled, because a terminal job carrying no `result.urls` is answered as a Comfy Router error rather than with the provider document.\nThe composition NARROWS `KreaJob` rather than republishing it, because `KreaJob` describes every poll of the job — including the pending ones the caller never sees — while this document describes only the body a `200` carries. `classifyKrea` (`routerpollstate/families.go`) answers `succeeded` on exactly one shape: `status` `completed` AND `nonEmptyAt(\"result\", \"urls\")`. Every other reading is a Comfy Router error rather than this document, so `status` is narrowed to the single terminal success and `result` is required non-null with a non-empty `urls`. Left unnarrowed, the schema would publish `intermediate-complete` with a null `result` as a valid `200` — the preview-as-result outcome the paragraph above says Router forbids — and a generated client would type the finished asset as optional. Narrowing `status` here is safe in a way it is NOT for BFL (see `BFLRouterResultOutput`, which declines the same narrowing): Krea's vocabulary is compared WITHOUT a case fold, so `completed` is the only spelling that can reach a `200`. `minItems: 1` and not a per-item `minLength`, because Router's rule is that at least ONE element is non-empty, not that every one is.","example":{"completed_at":"2027-01-01T00:00:37Z","created_at":"2027-01-01T00:00:00Z","job_id":"7f1c2e84-5b90-4a37-8d61-2c0f9ab4e153","result":{"urls":["https://example.invalid/krea/krea-2/generated.png"]},"status":"completed"}}}}}}}}},"components":{"schemas":{"KreaJob":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"job_id":{"format":"uuid","type":"string"},"result":{"allOf":[{"$ref":"#/components/schemas/KreaJobResult"}],"nullable":true},"status":{"description":"Available options: backlogged, queued, scheduled, processing, sampling, intermediate-complete, completed, failed, cancelled","enum":["backlogged","queued","scheduled","processing","sampling","intermediate-complete","completed","failed","cancelled"],"type":"string"}},"required":["job_id","status","created_at","completed_at","result"],"type":"object"},"KreaJobResult":{"properties":{"style_id":{"type":"string"},"urls":{"items":{"format":"uri","type":"string"},"type":"array"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"krea/krea-2","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/ltx/ltx-2-5-fast.json b/router-schemas/ltx/ltx-2-5-fast.json new file mode 100644 index 000000000..f581acc23 --- /dev/null +++ b/router-schemas/ltx/ltx-2-5-fast.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"ltx/ltx-2-5-fast","description":"The request body Comfy Router accepts for the model \"ltx/ltx-2-5-fast\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"4b9512f4908d"},"paths":{"/v2/models/ltx/ltx-2-5-fast":{"post":{"operationId":"runRouterModel","summary":"Run ltx/ltx-2-5-fast synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LTXJobStatusResponse"}],"description":"Comfy Router output schema for the LTX models: the terminal `LTXJobStatusResponse` poll document, forwarded unchanged. LTX v2 is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished job (`status: completed`) rather than the task handle the underlying submit returns.\nThe generated video is at `result.video_url`, and `result` is only present on a completed job. Output URLs expire 24 hours after completion, so download promptly rather than storing the link. A failed job carries `error.type` and `error.message` instead of `result`.","example":{"completed_at":"2026-01-01T00:02:10Z","created_at":"2026-01-01T00:00:00Z","id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","result":{"video_url":"https://example.invalid/ltx/generated.mp4"},"status":"completed"}}}}}}}}},"components":{"schemas":{"LTXJobStatusResponse":{"properties":{"completed_at":{"description":"Job completion timestamp (ISO 8601)","type":"string"},"created_at":{"description":"Job creation timestamp (ISO 8601)","type":"string"},"error":{"description":"Present when status is failed","properties":{"message":{"type":"string"},"type":{"type":"string"}},"type":"object"},"id":{"description":"Unique job identifier","type":"string"},"result":{"description":"Present when status is completed; output URLs expire 24 hours after completion","properties":{"video_url":{"description":"URL of the generated video","type":"string"}},"type":"object"},"status":{"description":"Job status (pending, processing, completed, failed)","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"ltx/ltx-2-5-fast","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/ltx/ltx-2-5-pro.json b/router-schemas/ltx/ltx-2-5-pro.json new file mode 100644 index 000000000..fb6111990 --- /dev/null +++ b/router-schemas/ltx/ltx-2-5-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"ltx/ltx-2-5-pro","description":"The request body Comfy Router accepts for the model \"ltx/ltx-2-5-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"4b9512f4908d"},"paths":{"/v2/models/ltx/ltx-2-5-pro":{"post":{"operationId":"runRouterModel","summary":"Run ltx/ltx-2-5-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LTXJobStatusResponse"}],"description":"Comfy Router output schema for the LTX models: the terminal `LTXJobStatusResponse` poll document, forwarded unchanged. LTX v2 is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished job (`status: completed`) rather than the task handle the underlying submit returns.\nThe generated video is at `result.video_url`, and `result` is only present on a completed job. Output URLs expire 24 hours after completion, so download promptly rather than storing the link. A failed job carries `error.type` and `error.message` instead of `result`.","example":{"completed_at":"2026-01-01T00:02:10Z","created_at":"2026-01-01T00:00:00Z","id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","result":{"video_url":"https://example.invalid/ltx/generated.mp4"},"status":"completed"}}}}}}}}},"components":{"schemas":{"LTXJobStatusResponse":{"properties":{"completed_at":{"description":"Job completion timestamp (ISO 8601)","type":"string"},"created_at":{"description":"Job creation timestamp (ISO 8601)","type":"string"},"error":{"description":"Present when status is failed","properties":{"message":{"type":"string"},"type":{"type":"string"}},"type":"object"},"id":{"description":"Unique job identifier","type":"string"},"result":{"description":"Present when status is completed; output URLs expire 24 hours after completion","properties":{"video_url":{"description":"URL of the generated video","type":"string"}},"type":"object"},"status":{"description":"Job status (pending, processing, completed, failed)","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"ltx/ltx-2-5-pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma/photon-1.json b/router-schemas/luma/photon-1.json new file mode 100644 index 000000000..6377dcf23 --- /dev/null +++ b/router-schemas/luma/photon-1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma/photon-1","description":"The request body Comfy Router accepts for the model \"luma/photon-1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"bc3e6bb865fd"},"paths":{"/v2/models/luma/photon-1":{"post":{"operationId":"runRouterModel","summary":"Run luma/photon-1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaImageGeneration"}],"description":"Comfy Router output schema for the Luma Dream Machine IMAGE models: the terminal `GET /proxy/luma/generations/{id}` document, forwarded unchanged. The submit is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma, endpoint: /generations/image}` as `ReturnModeSubmitPoll`, polled through the same status route the video models use — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated image is at `assets.image`. `assets.video` and `assets.progress_video` belong to the VIDEO operation that shares this status route and arrive here as explicit nulls, which is why they are documented as nullable rather than dropped: a caller decoding into a generated type will be handed the keys.\nREAD A LEAF'S VALUE, NOT ITS PRESENCE. Luma serialises its whole model on every generation, so a field it did not fill is `null` rather than absent — throughout `assets` and throughout the echoed `request`. `routerpollstate.classifyLuma` draws the same line: it accepts EITHER `assets.video` or `assets.image` as the output of a `completed`, precisely so that a terminal success of one operation is not reported as an output-less failure by a rule written for the other, and answers a `completed` carrying NEITHER as `success_without_output` rather than as a result. That shared classifier is why the nightly SDK cases for these two models assert the dotted leaf `assets.image` rather than the `assets` container: on this operation `assets.image` is the leaf that has to be there, and the classifier alone would not say so.\n`state` is Luma's own lowercase four-value vocabulary — `queued`, `dreaming` (Luma's word for in-progress), `completed`, `failed` — forwarded unchanged and compared without a case fold. `failure_reason` is populated only alongside `failed`.","example":{"assets":{"image":"https://example.invalid/luma/photon-1/generated.png","progress_video":null,"video":null},"created_at":"2027-01-01T00:00:00Z","failure_reason":null,"generation_type":"image","id":"5e8b06d7-91c4-4a2f-8b3d-6f7a8b9c0d1e","model":"photon-1","request":{"aspect_ratio":"1:1","callback_url":null,"character_ref":null,"generation_type":"image","image_ref":null,"model":"photon-1","modify_image_ref":null,"prompt":"a red circle on a plain white background","style_ref":null},"state":"completed"}}}}}}}}},"components":{"schemas":{"LumaAspectRatio":{"default":"16:9","description":"The aspect ratio of the generation","enum":["1:1","16:9","9:16","4:3","3:4","21:9","9:21"],"example":"16:9","type":"string"},"LumaImageAssets":{"description":"The assets of an image generation. `image` is the result; `video` and `progress_video` belong to the Dream Machine VIDEO operation, which shares this status route, and Luma sends them here as explicit nulls.","properties":{"image":{"description":"The URL of the generated image — the leaf a `luma/photon-*` generation fills. Null until the generation reaches `completed`.","format":"uri","nullable":true,"type":"string"},"progress_video":{"description":"Always null on an image generation; it is the in-flight preview Luma publishes while a VIDEO generation runs.","format":"uri","nullable":true,"type":"string"},"video":{"description":"Always null on an image generation; the `luma/ray-*` video models fill it instead.","format":"uri","nullable":true,"type":"string"}},"type":"object"},"LumaImageGeneration":{"description":"The image generation response object","example":{"assets":{"image":"https://example.invalid/luma/photon-1/generated.png","progress_video":null,"video":null},"created_at":"2027-01-01T00:00:00Z","failure_reason":null,"generation_type":"image","id":"7c9d41a5-2f68-4b03-9e57-0a1b2c3d4e5f","model":"photon-1","request":{"aspect_ratio":"1:1","callback_url":null,"character_ref":null,"generation_type":"image","image_ref":null,"model":"photon-1","modify_image_ref":null,"prompt":"a red circle on a plain white background","style_ref":null},"state":"completed"},"properties":{"assets":{"allOf":[{"$ref":"#/components/schemas/LumaImageAssets"}],"description":"The assets of the generation. `image` is the result; `video` and `progress_video` belong to the VIDEO operation that shares this status route and arrive as explicit nulls. The container itself is null on a generation that has produced nothing yet.","nullable":true},"created_at":{"description":"The date and time when the generation was created","format":"date-time","type":"string"},"failure_reason":{"description":"The reason for the state of the generation","nullable":true,"type":"string"},"generation_type":{"description":"The type of the generation — always `image` on this operation","enum":["image"],"type":"string"},"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"model":{"description":"The model used for the generation","type":"string"},"request":{"$ref":"#/components/schemas/LumaImageGenerationRequestEcho"},"state":{"$ref":"#/components/schemas/LumaState"}},"type":"object"},"LumaImageGenerationRequestEcho":{"description":"The image generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL for the generation","format":"uri","nullable":true,"type":"string"},"character_ref":{"nullable":true,"properties":{"identity0":{"allOf":[{"$ref":"#/components/schemas/LumaImageIdentityEcho"}],"description":"The image identity, echoed back — null when the caller sent none","nullable":true}},"type":"object"},"generation_type":{"description":"Always `image` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"image_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaImageModel"}],"nullable":true},"modify_image_ref":{"allOf":[{"$ref":"#/components/schemas/LumaModifyImageRefEcho"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"style_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageIdentityEcho":{"description":"An image identity as it comes BACK inside the terminal document. Same shape as `LumaImageIdentity`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"images":{"description":"The URLs of the image identity","items":{"format":"uri","type":"string"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageModel":{"default":"photon-1","description":"The image model used for the generation","enum":["photon-1","photon-flash-1"],"type":"string"},"LumaImageRefEcho":{"description":"An image reference as it comes BACK inside the terminal document. Same shape as `LumaImageRef`, with each member nullable because Luma echoes an unset one as an explicit `null` rather than omitting it.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the image reference","nullable":true,"type":"number"}},"type":"object"},"LumaModifyImageRefEcho":{"description":"A modify-image reference as it comes BACK inside the terminal document. Same shape as `LumaModifyImageRef`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the modify image reference","nullable":true,"type":"number"}},"type":"object"},"LumaState":{"description":"The state of the generation","enum":["queued","dreaming","completed","failed"],"example":"completed","type":"string"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma/photon-1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma/photon-flash-1.json b/router-schemas/luma/photon-flash-1.json new file mode 100644 index 000000000..28ff06f64 --- /dev/null +++ b/router-schemas/luma/photon-flash-1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma/photon-flash-1","description":"The request body Comfy Router accepts for the model \"luma/photon-flash-1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"bc3e6bb865fd"},"paths":{"/v2/models/luma/photon-flash-1":{"post":{"operationId":"runRouterModel","summary":"Run luma/photon-flash-1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaImageGeneration"}],"description":"Comfy Router output schema for the Luma Dream Machine IMAGE models: the terminal `GET /proxy/luma/generations/{id}` document, forwarded unchanged. The submit is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma, endpoint: /generations/image}` as `ReturnModeSubmitPoll`, polled through the same status route the video models use — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated image is at `assets.image`. `assets.video` and `assets.progress_video` belong to the VIDEO operation that shares this status route and arrive here as explicit nulls, which is why they are documented as nullable rather than dropped: a caller decoding into a generated type will be handed the keys.\nREAD A LEAF'S VALUE, NOT ITS PRESENCE. Luma serialises its whole model on every generation, so a field it did not fill is `null` rather than absent — throughout `assets` and throughout the echoed `request`. `routerpollstate.classifyLuma` draws the same line: it accepts EITHER `assets.video` or `assets.image` as the output of a `completed`, precisely so that a terminal success of one operation is not reported as an output-less failure by a rule written for the other, and answers a `completed` carrying NEITHER as `success_without_output` rather than as a result. That shared classifier is why the nightly SDK cases for these two models assert the dotted leaf `assets.image` rather than the `assets` container: on this operation `assets.image` is the leaf that has to be there, and the classifier alone would not say so.\n`state` is Luma's own lowercase four-value vocabulary — `queued`, `dreaming` (Luma's word for in-progress), `completed`, `failed` — forwarded unchanged and compared without a case fold. `failure_reason` is populated only alongside `failed`.","example":{"assets":{"image":"https://example.invalid/luma/photon-1/generated.png","progress_video":null,"video":null},"created_at":"2027-01-01T00:00:00Z","failure_reason":null,"generation_type":"image","id":"5e8b06d7-91c4-4a2f-8b3d-6f7a8b9c0d1e","model":"photon-1","request":{"aspect_ratio":"1:1","callback_url":null,"character_ref":null,"generation_type":"image","image_ref":null,"model":"photon-1","modify_image_ref":null,"prompt":"a red circle on a plain white background","style_ref":null},"state":"completed"}}}}}}}}},"components":{"schemas":{"LumaAspectRatio":{"default":"16:9","description":"The aspect ratio of the generation","enum":["1:1","16:9","9:16","4:3","3:4","21:9","9:21"],"example":"16:9","type":"string"},"LumaImageAssets":{"description":"The assets of an image generation. `image` is the result; `video` and `progress_video` belong to the Dream Machine VIDEO operation, which shares this status route, and Luma sends them here as explicit nulls.","properties":{"image":{"description":"The URL of the generated image — the leaf a `luma/photon-*` generation fills. Null until the generation reaches `completed`.","format":"uri","nullable":true,"type":"string"},"progress_video":{"description":"Always null on an image generation; it is the in-flight preview Luma publishes while a VIDEO generation runs.","format":"uri","nullable":true,"type":"string"},"video":{"description":"Always null on an image generation; the `luma/ray-*` video models fill it instead.","format":"uri","nullable":true,"type":"string"}},"type":"object"},"LumaImageGeneration":{"description":"The image generation response object","example":{"assets":{"image":"https://example.invalid/luma/photon-1/generated.png","progress_video":null,"video":null},"created_at":"2027-01-01T00:00:00Z","failure_reason":null,"generation_type":"image","id":"7c9d41a5-2f68-4b03-9e57-0a1b2c3d4e5f","model":"photon-1","request":{"aspect_ratio":"1:1","callback_url":null,"character_ref":null,"generation_type":"image","image_ref":null,"model":"photon-1","modify_image_ref":null,"prompt":"a red circle on a plain white background","style_ref":null},"state":"completed"},"properties":{"assets":{"allOf":[{"$ref":"#/components/schemas/LumaImageAssets"}],"description":"The assets of the generation. `image` is the result; `video` and `progress_video` belong to the VIDEO operation that shares this status route and arrive as explicit nulls. The container itself is null on a generation that has produced nothing yet.","nullable":true},"created_at":{"description":"The date and time when the generation was created","format":"date-time","type":"string"},"failure_reason":{"description":"The reason for the state of the generation","nullable":true,"type":"string"},"generation_type":{"description":"The type of the generation — always `image` on this operation","enum":["image"],"type":"string"},"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"model":{"description":"The model used for the generation","type":"string"},"request":{"$ref":"#/components/schemas/LumaImageGenerationRequestEcho"},"state":{"$ref":"#/components/schemas/LumaState"}},"type":"object"},"LumaImageGenerationRequestEcho":{"description":"The image generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL for the generation","format":"uri","nullable":true,"type":"string"},"character_ref":{"nullable":true,"properties":{"identity0":{"allOf":[{"$ref":"#/components/schemas/LumaImageIdentityEcho"}],"description":"The image identity, echoed back — null when the caller sent none","nullable":true}},"type":"object"},"generation_type":{"description":"Always `image` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"image_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaImageModel"}],"nullable":true},"modify_image_ref":{"allOf":[{"$ref":"#/components/schemas/LumaModifyImageRefEcho"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"style_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageIdentityEcho":{"description":"An image identity as it comes BACK inside the terminal document. Same shape as `LumaImageIdentity`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"images":{"description":"The URLs of the image identity","items":{"format":"uri","type":"string"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageModel":{"default":"photon-1","description":"The image model used for the generation","enum":["photon-1","photon-flash-1"],"type":"string"},"LumaImageRefEcho":{"description":"An image reference as it comes BACK inside the terminal document. Same shape as `LumaImageRef`, with each member nullable because Luma echoes an unset one as an explicit `null` rather than omitting it.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the image reference","nullable":true,"type":"number"}},"type":"object"},"LumaModifyImageRefEcho":{"description":"A modify-image reference as it comes BACK inside the terminal document. Same shape as `LumaModifyImageRef`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the modify image reference","nullable":true,"type":"number"}},"type":"object"},"LumaState":{"description":"The state of the generation","enum":["queued","dreaming","completed","failed"],"example":"completed","type":"string"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma/photon-flash-1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma/ray-1-6.json b/router-schemas/luma/ray-1-6.json new file mode 100644 index 000000000..4e0bb3039 --- /dev/null +++ b/router-schemas/luma/ray-1-6.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma/ray-1-6","description":"The request body Comfy Router accepts for the model \"luma/ray-1-6\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"4972faf4668b"},"paths":{"/v2/models/luma/ray-1-6":{"post":{"operationId":"runRouterModel","summary":"Run luma/ray-1-6 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaGeneration"}],"description":"Comfy Router output schema for the Luma Dream Machine VIDEO models: the terminal `GET /proxy/luma/generations/{id}` document, forwarded unchanged. The submit is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma, endpoint: /generations}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated video is at `assets.video`. The `luma/photon-*` IMAGE models answer on this same status route with a different document — the asset under `assets.image`, the video leaves null — so they carry a schema of their own rather than sharing this one; `routerpollstate.classifyLuma` accepts EITHER leaf as success, precisely so that a terminal success of one operation is not reported as an output-less failure by a rule written for the other.\n`assets.progress_video` is NOT the result. Luma publishes it as an in-flight preview while the generation is still running, so a document carrying only that leaf is a completed generation that produced nothing — which `classifyLuma` answers as `success_without_output` rather than as a result, and which is why the SDK cases for this family assert the dotted leaf `assets.video` rather than the `assets` container.\n`state` is Luma's own lowercase four-value vocabulary — `queued`, `dreaming` (Luma's word for in-progress), `completed`, `failed` — forwarded unchanged and compared without a case fold. `failure_reason` is populated only alongside `failed`.\nThe example below is a `luma/ray-2` VIDEO generation. `request` is omitted from it on purpose: it is any one of four request shapes and Router echoes back whichever one the caller sent — the `LumaGeneration` component this composes over carries a worked one of its own.","example":{"assets":{"video":"https://example.invalid/luma/ray-2/generated.mp4"},"created_at":"2027-01-01T00:00:00Z","generation_type":"video","id":"3f2a7c1e-8b40-4d59-9f6a-1c2d3e4f5a60","model":"ray-2","state":"completed"}}}}}}}}},"components":{"schemas":{"LumaAspectRatio":{"default":"16:9","description":"The aspect ratio of the generation","enum":["1:1","16:9","9:16","4:3","3:4","21:9","9:21"],"example":"16:9","type":"string"},"LumaAssets":{"description":"The assets of the generation","properties":{"image":{"description":"The URL of the image","format":"uri","type":"string"},"progress_video":{"description":"The URL of the progress video","format":"uri","nullable":true,"type":"string"},"video":{"description":"The URL of the video","format":"uri","type":"string"}},"type":"object"},"LumaAudioGenerationRequest":{"description":"The audio generation request object","properties":{"callback_url":{"description":"The callback URL for the audio","format":"uri","type":"string"},"generation_type":{"default":"add_audio","enum":["add_audio"],"type":"string"},"negative_prompt":{"description":"The negative prompt of the audio","type":"string"},"prompt":{"description":"The prompt of the audio","type":"string"}},"type":"object"},"LumaGeneration":{"description":"The generation response object","example":{"assets":{"video":"https://example.com/video.mp4"},"created_at":"2023-06-01T12:00:00Z","failure_reason":null,"generation_type":"video","id":"123e4567-e89b-12d3-a456-426614174000","model":"ray-2","request":{"aspect_ratio":"16:9","duration":"5s","generation_type":"video","keyframes":{"frame0":{"type":"image","url":"https://example.com/image.jpg"},"frame1":{"id":"123e4567-e89b-12d3-a456-426614174002","type":"generation"}},"loop":true,"model":"ray-2","prompt":"A serene lake surrounded by mountains at sunset","resolution":"720p"},"state":"completed"},"properties":{"assets":{"$ref":"#/components/schemas/LumaAssets"},"created_at":{"description":"The date and time when the generation was created","format":"date-time","type":"string"},"failure_reason":{"description":"The reason for the state of the generation","nullable":true,"type":"string"},"generation_type":{"$ref":"#/components/schemas/LumaGenerationType"},"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"model":{"description":"The model used for the generation","type":"string"},"request":{"anyOf":[{"$ref":"#/components/schemas/LumaGenerationRequestEcho"},{"$ref":"#/components/schemas/LumaImageGenerationRequestEcho"},{"$ref":"#/components/schemas/LumaUpscaleVideoGenerationRequest"},{"$ref":"#/components/schemas/LumaAudioGenerationRequest"}],"description":"The request of the generation"},"state":{"$ref":"#/components/schemas/LumaState"}},"type":"object"},"LumaGenerationReference":{"description":"The generation reference object","example":{"id":"123e4567-e89b-12d3-a456-426614174003","type":"generation"},"properties":{"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"type":{"default":"generation","enum":["generation"],"type":"string"}},"required":["type","id"],"type":"object"},"LumaGenerationRequestEcho":{"description":"The video generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL of the generation","format":"uri","nullable":true,"type":"string"},"duration":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModelOutputDuration"}],"nullable":true},"generation_type":{"description":"Always `video` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"keyframes":{"description":"The keyframes of the generation","nullable":true,"properties":{"frame0":{"allOf":[{"$ref":"#/components/schemas/LumaKeyframe"}],"nullable":true},"frame1":{"allOf":[{"$ref":"#/components/schemas/LumaKeyframe"}],"nullable":true}},"type":"object"},"loop":{"description":"Whether to loop the video","nullable":true,"type":"boolean"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModel"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"resolution":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModelOutputResolution"}],"nullable":true}},"type":"object"},"LumaGenerationType":{"enum":["video","image"],"type":"string"},"LumaImageGenerationRequestEcho":{"description":"The image generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL for the generation","format":"uri","nullable":true,"type":"string"},"character_ref":{"nullable":true,"properties":{"identity0":{"allOf":[{"$ref":"#/components/schemas/LumaImageIdentityEcho"}],"description":"The image identity, echoed back — null when the caller sent none","nullable":true}},"type":"object"},"generation_type":{"description":"Always `image` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"image_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaImageModel"}],"nullable":true},"modify_image_ref":{"allOf":[{"$ref":"#/components/schemas/LumaModifyImageRefEcho"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"style_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageIdentityEcho":{"description":"An image identity as it comes BACK inside the terminal document. Same shape as `LumaImageIdentity`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"images":{"description":"The URLs of the image identity","items":{"format":"uri","type":"string"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageModel":{"default":"photon-1","description":"The image model used for the generation","enum":["photon-1","photon-flash-1"],"type":"string"},"LumaImageRefEcho":{"description":"An image reference as it comes BACK inside the terminal document. Same shape as `LumaImageRef`, with each member nullable because Luma echoes an unset one as an explicit `null` rather than omitting it.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the image reference","nullable":true,"type":"number"}},"type":"object"},"LumaImageReference":{"description":"The image object","example":{"type":"image","url":"https://example.com/image.jpg"},"properties":{"type":{"default":"image","enum":["image"],"type":"string"},"url":{"description":"The URL of the image","format":"uri","type":"string"}},"required":["type","url"],"type":"object"},"LumaKeyframe":{"description":"A keyframe can be either a Generation reference, an Image, or a Video","discriminator":{"mapping":{"generation":"#/components/schemas/LumaGenerationReference","image":"#/components/schemas/LumaImageReference"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/LumaGenerationReference"},{"$ref":"#/components/schemas/LumaImageReference"}]},"LumaModifyImageRefEcho":{"description":"A modify-image reference as it comes BACK inside the terminal document. Same shape as `LumaModifyImageRef`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the modify image reference","nullable":true,"type":"number"}},"type":"object"},"LumaState":{"description":"The state of the generation","enum":["queued","dreaming","completed","failed"],"example":"completed","type":"string"},"LumaUpscaleVideoGenerationRequest":{"description":"The upscale generation request object","properties":{"callback_url":{"description":"The callback URL for the upscale","format":"uri","type":"string"},"generation_type":{"default":"upscale_video","enum":["upscale_video"],"type":"string"},"resolution":{"$ref":"#/components/schemas/LumaVideoModelOutputResolution"}},"type":"object"},"LumaVideoModel":{"default":"ray-2","description":"The video model used for the generation","enum":["ray-2","ray-flash-2","ray-1-6"],"example":"ray-2","type":"string"},"LumaVideoModelOutputDuration":{"anyOf":[{"enum":["5s","9s"],"type":"string"},{"type":"string"}]},"LumaVideoModelOutputResolution":{"anyOf":[{"enum":["540p","720p","1080p","4k"],"type":"string"},{"type":"string"}]}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma/ray-1-6","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma/ray-2.json b/router-schemas/luma/ray-2.json new file mode 100644 index 000000000..adc8ce8d6 --- /dev/null +++ b/router-schemas/luma/ray-2.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma/ray-2","description":"The request body Comfy Router accepts for the model \"luma/ray-2\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"4972faf4668b"},"paths":{"/v2/models/luma/ray-2":{"post":{"operationId":"runRouterModel","summary":"Run luma/ray-2 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaGeneration"}],"description":"Comfy Router output schema for the Luma Dream Machine VIDEO models: the terminal `GET /proxy/luma/generations/{id}` document, forwarded unchanged. The submit is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma, endpoint: /generations}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated video is at `assets.video`. The `luma/photon-*` IMAGE models answer on this same status route with a different document — the asset under `assets.image`, the video leaves null — so they carry a schema of their own rather than sharing this one; `routerpollstate.classifyLuma` accepts EITHER leaf as success, precisely so that a terminal success of one operation is not reported as an output-less failure by a rule written for the other.\n`assets.progress_video` is NOT the result. Luma publishes it as an in-flight preview while the generation is still running, so a document carrying only that leaf is a completed generation that produced nothing — which `classifyLuma` answers as `success_without_output` rather than as a result, and which is why the SDK cases for this family assert the dotted leaf `assets.video` rather than the `assets` container.\n`state` is Luma's own lowercase four-value vocabulary — `queued`, `dreaming` (Luma's word for in-progress), `completed`, `failed` — forwarded unchanged and compared without a case fold. `failure_reason` is populated only alongside `failed`.\nThe example below is a `luma/ray-2` VIDEO generation. `request` is omitted from it on purpose: it is any one of four request shapes and Router echoes back whichever one the caller sent — the `LumaGeneration` component this composes over carries a worked one of its own.","example":{"assets":{"video":"https://example.invalid/luma/ray-2/generated.mp4"},"created_at":"2027-01-01T00:00:00Z","generation_type":"video","id":"3f2a7c1e-8b40-4d59-9f6a-1c2d3e4f5a60","model":"ray-2","state":"completed"}}}}}}}}},"components":{"schemas":{"LumaAspectRatio":{"default":"16:9","description":"The aspect ratio of the generation","enum":["1:1","16:9","9:16","4:3","3:4","21:9","9:21"],"example":"16:9","type":"string"},"LumaAssets":{"description":"The assets of the generation","properties":{"image":{"description":"The URL of the image","format":"uri","type":"string"},"progress_video":{"description":"The URL of the progress video","format":"uri","nullable":true,"type":"string"},"video":{"description":"The URL of the video","format":"uri","type":"string"}},"type":"object"},"LumaAudioGenerationRequest":{"description":"The audio generation request object","properties":{"callback_url":{"description":"The callback URL for the audio","format":"uri","type":"string"},"generation_type":{"default":"add_audio","enum":["add_audio"],"type":"string"},"negative_prompt":{"description":"The negative prompt of the audio","type":"string"},"prompt":{"description":"The prompt of the audio","type":"string"}},"type":"object"},"LumaGeneration":{"description":"The generation response object","example":{"assets":{"video":"https://example.com/video.mp4"},"created_at":"2023-06-01T12:00:00Z","failure_reason":null,"generation_type":"video","id":"123e4567-e89b-12d3-a456-426614174000","model":"ray-2","request":{"aspect_ratio":"16:9","duration":"5s","generation_type":"video","keyframes":{"frame0":{"type":"image","url":"https://example.com/image.jpg"},"frame1":{"id":"123e4567-e89b-12d3-a456-426614174002","type":"generation"}},"loop":true,"model":"ray-2","prompt":"A serene lake surrounded by mountains at sunset","resolution":"720p"},"state":"completed"},"properties":{"assets":{"$ref":"#/components/schemas/LumaAssets"},"created_at":{"description":"The date and time when the generation was created","format":"date-time","type":"string"},"failure_reason":{"description":"The reason for the state of the generation","nullable":true,"type":"string"},"generation_type":{"$ref":"#/components/schemas/LumaGenerationType"},"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"model":{"description":"The model used for the generation","type":"string"},"request":{"anyOf":[{"$ref":"#/components/schemas/LumaGenerationRequestEcho"},{"$ref":"#/components/schemas/LumaImageGenerationRequestEcho"},{"$ref":"#/components/schemas/LumaUpscaleVideoGenerationRequest"},{"$ref":"#/components/schemas/LumaAudioGenerationRequest"}],"description":"The request of the generation"},"state":{"$ref":"#/components/schemas/LumaState"}},"type":"object"},"LumaGenerationReference":{"description":"The generation reference object","example":{"id":"123e4567-e89b-12d3-a456-426614174003","type":"generation"},"properties":{"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"type":{"default":"generation","enum":["generation"],"type":"string"}},"required":["type","id"],"type":"object"},"LumaGenerationRequestEcho":{"description":"The video generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL of the generation","format":"uri","nullable":true,"type":"string"},"duration":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModelOutputDuration"}],"nullable":true},"generation_type":{"description":"Always `video` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"keyframes":{"description":"The keyframes of the generation","nullable":true,"properties":{"frame0":{"allOf":[{"$ref":"#/components/schemas/LumaKeyframe"}],"nullable":true},"frame1":{"allOf":[{"$ref":"#/components/schemas/LumaKeyframe"}],"nullable":true}},"type":"object"},"loop":{"description":"Whether to loop the video","nullable":true,"type":"boolean"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModel"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"resolution":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModelOutputResolution"}],"nullable":true}},"type":"object"},"LumaGenerationType":{"enum":["video","image"],"type":"string"},"LumaImageGenerationRequestEcho":{"description":"The image generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL for the generation","format":"uri","nullable":true,"type":"string"},"character_ref":{"nullable":true,"properties":{"identity0":{"allOf":[{"$ref":"#/components/schemas/LumaImageIdentityEcho"}],"description":"The image identity, echoed back — null when the caller sent none","nullable":true}},"type":"object"},"generation_type":{"description":"Always `image` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"image_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaImageModel"}],"nullable":true},"modify_image_ref":{"allOf":[{"$ref":"#/components/schemas/LumaModifyImageRefEcho"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"style_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageIdentityEcho":{"description":"An image identity as it comes BACK inside the terminal document. Same shape as `LumaImageIdentity`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"images":{"description":"The URLs of the image identity","items":{"format":"uri","type":"string"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageModel":{"default":"photon-1","description":"The image model used for the generation","enum":["photon-1","photon-flash-1"],"type":"string"},"LumaImageRefEcho":{"description":"An image reference as it comes BACK inside the terminal document. Same shape as `LumaImageRef`, with each member nullable because Luma echoes an unset one as an explicit `null` rather than omitting it.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the image reference","nullable":true,"type":"number"}},"type":"object"},"LumaImageReference":{"description":"The image object","example":{"type":"image","url":"https://example.com/image.jpg"},"properties":{"type":{"default":"image","enum":["image"],"type":"string"},"url":{"description":"The URL of the image","format":"uri","type":"string"}},"required":["type","url"],"type":"object"},"LumaKeyframe":{"description":"A keyframe can be either a Generation reference, an Image, or a Video","discriminator":{"mapping":{"generation":"#/components/schemas/LumaGenerationReference","image":"#/components/schemas/LumaImageReference"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/LumaGenerationReference"},{"$ref":"#/components/schemas/LumaImageReference"}]},"LumaModifyImageRefEcho":{"description":"A modify-image reference as it comes BACK inside the terminal document. Same shape as `LumaModifyImageRef`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the modify image reference","nullable":true,"type":"number"}},"type":"object"},"LumaState":{"description":"The state of the generation","enum":["queued","dreaming","completed","failed"],"example":"completed","type":"string"},"LumaUpscaleVideoGenerationRequest":{"description":"The upscale generation request object","properties":{"callback_url":{"description":"The callback URL for the upscale","format":"uri","type":"string"},"generation_type":{"default":"upscale_video","enum":["upscale_video"],"type":"string"},"resolution":{"$ref":"#/components/schemas/LumaVideoModelOutputResolution"}},"type":"object"},"LumaVideoModel":{"default":"ray-2","description":"The video model used for the generation","enum":["ray-2","ray-flash-2","ray-1-6"],"example":"ray-2","type":"string"},"LumaVideoModelOutputDuration":{"anyOf":[{"enum":["5s","9s"],"type":"string"},{"type":"string"}]},"LumaVideoModelOutputResolution":{"anyOf":[{"enum":["540p","720p","1080p","4k"],"type":"string"},{"type":"string"}]}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma/ray-2","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma/ray-flash-2.json b/router-schemas/luma/ray-flash-2.json new file mode 100644 index 000000000..73e39dc80 --- /dev/null +++ b/router-schemas/luma/ray-flash-2.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma/ray-flash-2","description":"The request body Comfy Router accepts for the model \"luma/ray-flash-2\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"4972faf4668b"},"paths":{"/v2/models/luma/ray-flash-2":{"post":{"operationId":"runRouterModel","summary":"Run luma/ray-flash-2 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaGeneration"}],"description":"Comfy Router output schema for the Luma Dream Machine VIDEO models: the terminal `GET /proxy/luma/generations/{id}` document, forwarded unchanged. The submit is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma, endpoint: /generations}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated video is at `assets.video`. The `luma/photon-*` IMAGE models answer on this same status route with a different document — the asset under `assets.image`, the video leaves null — so they carry a schema of their own rather than sharing this one; `routerpollstate.classifyLuma` accepts EITHER leaf as success, precisely so that a terminal success of one operation is not reported as an output-less failure by a rule written for the other.\n`assets.progress_video` is NOT the result. Luma publishes it as an in-flight preview while the generation is still running, so a document carrying only that leaf is a completed generation that produced nothing — which `classifyLuma` answers as `success_without_output` rather than as a result, and which is why the SDK cases for this family assert the dotted leaf `assets.video` rather than the `assets` container.\n`state` is Luma's own lowercase four-value vocabulary — `queued`, `dreaming` (Luma's word for in-progress), `completed`, `failed` — forwarded unchanged and compared without a case fold. `failure_reason` is populated only alongside `failed`.\nThe example below is a `luma/ray-2` VIDEO generation. `request` is omitted from it on purpose: it is any one of four request shapes and Router echoes back whichever one the caller sent — the `LumaGeneration` component this composes over carries a worked one of its own.","example":{"assets":{"video":"https://example.invalid/luma/ray-2/generated.mp4"},"created_at":"2027-01-01T00:00:00Z","generation_type":"video","id":"3f2a7c1e-8b40-4d59-9f6a-1c2d3e4f5a60","model":"ray-2","state":"completed"}}}}}}}}},"components":{"schemas":{"LumaAspectRatio":{"default":"16:9","description":"The aspect ratio of the generation","enum":["1:1","16:9","9:16","4:3","3:4","21:9","9:21"],"example":"16:9","type":"string"},"LumaAssets":{"description":"The assets of the generation","properties":{"image":{"description":"The URL of the image","format":"uri","type":"string"},"progress_video":{"description":"The URL of the progress video","format":"uri","nullable":true,"type":"string"},"video":{"description":"The URL of the video","format":"uri","type":"string"}},"type":"object"},"LumaAudioGenerationRequest":{"description":"The audio generation request object","properties":{"callback_url":{"description":"The callback URL for the audio","format":"uri","type":"string"},"generation_type":{"default":"add_audio","enum":["add_audio"],"type":"string"},"negative_prompt":{"description":"The negative prompt of the audio","type":"string"},"prompt":{"description":"The prompt of the audio","type":"string"}},"type":"object"},"LumaGeneration":{"description":"The generation response object","example":{"assets":{"video":"https://example.com/video.mp4"},"created_at":"2023-06-01T12:00:00Z","failure_reason":null,"generation_type":"video","id":"123e4567-e89b-12d3-a456-426614174000","model":"ray-2","request":{"aspect_ratio":"16:9","duration":"5s","generation_type":"video","keyframes":{"frame0":{"type":"image","url":"https://example.com/image.jpg"},"frame1":{"id":"123e4567-e89b-12d3-a456-426614174002","type":"generation"}},"loop":true,"model":"ray-2","prompt":"A serene lake surrounded by mountains at sunset","resolution":"720p"},"state":"completed"},"properties":{"assets":{"$ref":"#/components/schemas/LumaAssets"},"created_at":{"description":"The date and time when the generation was created","format":"date-time","type":"string"},"failure_reason":{"description":"The reason for the state of the generation","nullable":true,"type":"string"},"generation_type":{"$ref":"#/components/schemas/LumaGenerationType"},"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"model":{"description":"The model used for the generation","type":"string"},"request":{"anyOf":[{"$ref":"#/components/schemas/LumaGenerationRequestEcho"},{"$ref":"#/components/schemas/LumaImageGenerationRequestEcho"},{"$ref":"#/components/schemas/LumaUpscaleVideoGenerationRequest"},{"$ref":"#/components/schemas/LumaAudioGenerationRequest"}],"description":"The request of the generation"},"state":{"$ref":"#/components/schemas/LumaState"}},"type":"object"},"LumaGenerationReference":{"description":"The generation reference object","example":{"id":"123e4567-e89b-12d3-a456-426614174003","type":"generation"},"properties":{"id":{"description":"The ID of the generation","format":"uuid","type":"string"},"type":{"default":"generation","enum":["generation"],"type":"string"}},"required":["type","id"],"type":"object"},"LumaGenerationRequestEcho":{"description":"The video generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL of the generation","format":"uri","nullable":true,"type":"string"},"duration":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModelOutputDuration"}],"nullable":true},"generation_type":{"description":"Always `video` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"keyframes":{"description":"The keyframes of the generation","nullable":true,"properties":{"frame0":{"allOf":[{"$ref":"#/components/schemas/LumaKeyframe"}],"nullable":true},"frame1":{"allOf":[{"$ref":"#/components/schemas/LumaKeyframe"}],"nullable":true}},"type":"object"},"loop":{"description":"Whether to loop the video","nullable":true,"type":"boolean"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModel"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"resolution":{"allOf":[{"$ref":"#/components/schemas/LumaVideoModelOutputResolution"}],"nullable":true}},"type":"object"},"LumaGenerationType":{"enum":["video","image"],"type":"string"},"LumaImageGenerationRequestEcho":{"description":"The image generation request, echoed back inside the terminal document. Luma serialises every field of its request model, writing an explicit `null` into each one the caller did not set, so read a field's presence from its VALUE rather than from the key.","properties":{"aspect_ratio":{"allOf":[{"$ref":"#/components/schemas/LumaAspectRatio"}],"nullable":true},"callback_url":{"description":"The callback URL for the generation","format":"uri","nullable":true,"type":"string"},"character_ref":{"nullable":true,"properties":{"identity0":{"allOf":[{"$ref":"#/components/schemas/LumaImageIdentityEcho"}],"description":"The image identity, echoed back — null when the caller sent none","nullable":true}},"type":"object"},"generation_type":{"description":"Always `image` when set, echoing the operation. Null when the caller did not send one, for the reason this whole echo object exists.","nullable":true,"type":"string"},"image_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"},"model":{"allOf":[{"$ref":"#/components/schemas/LumaImageModel"}],"nullable":true},"modify_image_ref":{"allOf":[{"$ref":"#/components/schemas/LumaModifyImageRefEcho"}],"nullable":true},"prompt":{"description":"The prompt of the generation","nullable":true,"type":"string"},"style_ref":{"items":{"$ref":"#/components/schemas/LumaImageRefEcho"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageIdentityEcho":{"description":"An image identity as it comes BACK inside the terminal document. Same shape as `LumaImageIdentity`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"images":{"description":"The URLs of the image identity","items":{"format":"uri","type":"string"},"nullable":true,"type":"array"}},"type":"object"},"LumaImageModel":{"default":"photon-1","description":"The image model used for the generation","enum":["photon-1","photon-flash-1"],"type":"string"},"LumaImageRefEcho":{"description":"An image reference as it comes BACK inside the terminal document. Same shape as `LumaImageRef`, with each member nullable because Luma echoes an unset one as an explicit `null` rather than omitting it.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the image reference","nullable":true,"type":"number"}},"type":"object"},"LumaImageReference":{"description":"The image object","example":{"type":"image","url":"https://example.com/image.jpg"},"properties":{"type":{"default":"image","enum":["image"],"type":"string"},"url":{"description":"The URL of the image","format":"uri","type":"string"}},"required":["type","url"],"type":"object"},"LumaKeyframe":{"description":"A keyframe can be either a Generation reference, an Image, or a Video","discriminator":{"mapping":{"generation":"#/components/schemas/LumaGenerationReference","image":"#/components/schemas/LumaImageReference"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/LumaGenerationReference"},{"$ref":"#/components/schemas/LumaImageReference"}]},"LumaModifyImageRefEcho":{"description":"A modify-image reference as it comes BACK inside the terminal document. Same shape as `LumaModifyImageRef`, with its members nullable for the reason `LumaImageRefEcho` gives.","properties":{"url":{"description":"The URL of the image reference","format":"uri","nullable":true,"type":"string"},"weight":{"description":"The weight of the modify image reference","nullable":true,"type":"number"}},"type":"object"},"LumaState":{"description":"The state of the generation","enum":["queued","dreaming","completed","failed"],"example":"completed","type":"string"},"LumaUpscaleVideoGenerationRequest":{"description":"The upscale generation request object","properties":{"callback_url":{"description":"The callback URL for the upscale","format":"uri","type":"string"},"generation_type":{"default":"upscale_video","enum":["upscale_video"],"type":"string"},"resolution":{"$ref":"#/components/schemas/LumaVideoModelOutputResolution"}},"type":"object"},"LumaVideoModel":{"default":"ray-2","description":"The video model used for the generation","enum":["ray-2","ray-flash-2","ray-1-6"],"example":"ray-2","type":"string"},"LumaVideoModelOutputDuration":{"anyOf":[{"enum":["5s","9s"],"type":"string"},{"type":"string"}]},"LumaVideoModelOutputResolution":{"anyOf":[{"enum":["540p","720p","1080p","4k"],"type":"string"},{"type":"string"}]}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma/ray-flash-2","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma_2/uni-1-max.json b/router-schemas/luma_2/uni-1-max.json new file mode 100644 index 000000000..939a79476 --- /dev/null +++ b/router-schemas/luma_2/uni-1-max.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma_2/uni-1-max","description":"The request body Comfy Router accepts for the model \"luma_2/uni-1-max\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"706f2f0f941c"},"paths":{"/v2/models/luma_2/uni-1-max":{"post":{"operationId":"runRouterModel","summary":"Run luma_2/uni-1-max synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaAgentsGeneration"}],"description":"Comfy Router output schema for the Luma Agents image models: the terminal `GET /proxy/luma_2/generations/{generation_id}` document, forwarded unchanged. The operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma_2, endpoint: /generations}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated images are in `output`, each with its URL at `output[].url`. Read the LEAF rather than the container: `LumaAgentsGenerationOutput.type` is a media-kind discriminator, so an element carrying only a `type` is a completed generation that produced nothing — which `routerpollstate.classifyLumaAgents` answers as `success_without_output` (`anyElementNonEmptyAt` over `output[].url`) rather than as a result.\n`output[].url` is a PRESIGNED link with roughly a one-hour expiry, so download it promptly rather than storing it.\n`state` is the Agents vocabulary — `queued`, `processing` (NOT the Dream Machine's `dreaming`), `completed`, `failed` — forwarded unchanged. A failed generation carries the machine-readable `failure_code` (`content_moderated`, `generation_failed`, `budget_exhausted`, `output_not_found`) alongside a human-readable `failure_reason`; `routererr`'s `luma_agents` adapter refines on that code, so a moderated generation reaches the caller as `content_policy_violation` rather than as a generic `provider_error`.\nBOTH FAILURE FIELDS ARE NULLABLE, and a successful generation is where you meet that: Luma emits `failure_code` and `failure_reason` as explicit `null`s rather than omitting them, so a caller must treat null and absent alike instead of reading either key as evidence of a failure. Branch on `state` — or on `output[].url` — never on the presence of these keys.\nThe `luma/*` Dream Machine models are described by `LumaRouterOutput` (video) and `LumaImageRouterOutput` (image) instead: same partner, a different document, and no `assets` key here at all.","example":{"created_at":"2027-01-01T00:00:00Z","failure_code":null,"failure_reason":null,"id":"gen-luma-agents-3f2a7c1e8b40","model":"uni-1","output":[{"type":"image","url":"https://example.invalid/luma_2/uni-1/generated.png"}],"state":"completed","type":"image"}}}}}}}}},"components":{"schemas":{"LumaAgentsFailureCode":{"description":"Machine-readable failure code for programmatic handling","enum":["content_moderated","generation_failed","budget_exhausted","output_not_found"],"type":"string"},"LumaAgentsGeneration":{"description":"Generation status and output","properties":{"created_at":{"description":"Creation timestamp","type":"string"},"failure_code":{"allOf":[{"$ref":"#/components/schemas/LumaAgentsFailureCode"}],"description":"Machine-readable failure code, populated only on a FAILED generation. `null` on a successful one.","nullable":true},"failure_reason":{"description":"Human-readable failure description, populated only on a FAILED generation. `null` on a successful one.","nullable":true,"type":"string"},"id":{"description":"Generation identifier","type":"string"},"model":{"description":"Model used","type":"string"},"output":{"items":{"$ref":"#/components/schemas/LumaAgentsGenerationOutput"},"type":"array"},"state":{"$ref":"#/components/schemas/LumaAgentsState"},"type":{"$ref":"#/components/schemas/LumaAgentsGenerationType"}},"type":"object"},"LumaAgentsGenerationOutput":{"description":"A generated output entry","properties":{"type":{"description":"Media type (e.g. image)","type":"string"},"url":{"description":"Presigned URL (1hr expiry)","type":"string"}},"type":"object"},"LumaAgentsGenerationType":{"description":"The kind of generation to perform. image/image_edit are produced by the uni-1 / uni-1-max models; video/video_edit/video_reframe are produced by the ray-3.2 model.","enum":["image","image_edit","video","video_edit","video_reframe"],"type":"string"},"LumaAgentsState":{"description":"Current state of the generation","enum":["queued","processing","completed","failed"],"type":"string"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma_2/uni-1-max","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/luma_2/uni-1.json b/router-schemas/luma_2/uni-1.json new file mode 100644 index 000000000..38802391d --- /dev/null +++ b/router-schemas/luma_2/uni-1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"luma_2/uni-1","description":"The request body Comfy Router accepts for the model \"luma_2/uni-1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"706f2f0f941c"},"paths":{"/v2/models/luma_2/uni-1":{"post":{"operationId":"runRouterModel","summary":"Run luma_2/uni-1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LumaAgentsGeneration"}],"description":"Comfy Router output schema for the Luma Agents image models: the terminal `GET /proxy/luma_2/generations/{generation_id}` document, forwarded unchanged. The operation is SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: luma_2, endpoint: /generations}` as `ReturnModeSubmitPoll` — and Router polls on the caller's behalf, so the body a caller receives is the finished generation rather than the `{id, state: queued}` handle the underlying submit answers with.\nThe generated images are in `output`, each with its URL at `output[].url`. Read the LEAF rather than the container: `LumaAgentsGenerationOutput.type` is a media-kind discriminator, so an element carrying only a `type` is a completed generation that produced nothing — which `routerpollstate.classifyLumaAgents` answers as `success_without_output` (`anyElementNonEmptyAt` over `output[].url`) rather than as a result.\n`output[].url` is a PRESIGNED link with roughly a one-hour expiry, so download it promptly rather than storing it.\n`state` is the Agents vocabulary — `queued`, `processing` (NOT the Dream Machine's `dreaming`), `completed`, `failed` — forwarded unchanged. A failed generation carries the machine-readable `failure_code` (`content_moderated`, `generation_failed`, `budget_exhausted`, `output_not_found`) alongside a human-readable `failure_reason`; `routererr`'s `luma_agents` adapter refines on that code, so a moderated generation reaches the caller as `content_policy_violation` rather than as a generic `provider_error`.\nBOTH FAILURE FIELDS ARE NULLABLE, and a successful generation is where you meet that: Luma emits `failure_code` and `failure_reason` as explicit `null`s rather than omitting them, so a caller must treat null and absent alike instead of reading either key as evidence of a failure. Branch on `state` — or on `output[].url` — never on the presence of these keys.\nThe `luma/*` Dream Machine models are described by `LumaRouterOutput` (video) and `LumaImageRouterOutput` (image) instead: same partner, a different document, and no `assets` key here at all.","example":{"created_at":"2027-01-01T00:00:00Z","failure_code":null,"failure_reason":null,"id":"gen-luma-agents-3f2a7c1e8b40","model":"uni-1","output":[{"type":"image","url":"https://example.invalid/luma_2/uni-1/generated.png"}],"state":"completed","type":"image"}}}}}}}}},"components":{"schemas":{"LumaAgentsFailureCode":{"description":"Machine-readable failure code for programmatic handling","enum":["content_moderated","generation_failed","budget_exhausted","output_not_found"],"type":"string"},"LumaAgentsGeneration":{"description":"Generation status and output","properties":{"created_at":{"description":"Creation timestamp","type":"string"},"failure_code":{"allOf":[{"$ref":"#/components/schemas/LumaAgentsFailureCode"}],"description":"Machine-readable failure code, populated only on a FAILED generation. `null` on a successful one.","nullable":true},"failure_reason":{"description":"Human-readable failure description, populated only on a FAILED generation. `null` on a successful one.","nullable":true,"type":"string"},"id":{"description":"Generation identifier","type":"string"},"model":{"description":"Model used","type":"string"},"output":{"items":{"$ref":"#/components/schemas/LumaAgentsGenerationOutput"},"type":"array"},"state":{"$ref":"#/components/schemas/LumaAgentsState"},"type":{"$ref":"#/components/schemas/LumaAgentsGenerationType"}},"type":"object"},"LumaAgentsGenerationOutput":{"description":"A generated output entry","properties":{"type":{"description":"Media type (e.g. image)","type":"string"},"url":{"description":"Presigned URL (1hr expiry)","type":"string"}},"type":"object"},"LumaAgentsGenerationType":{"description":"The kind of generation to perform. image/image_edit are produced by the uni-1 / uni-1-max models; video/video_edit/video_reframe are produced by the ray-3.2 model.","enum":["image","image_edit","video","video_edit","video_reframe"],"type":"string"},"LumaAgentsState":{"description":"Current state of the generation","enum":["queued","processing","completed","failed"],"type":"string"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"luma_2/uni-1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/meshy/meshy-5.json b/router-schemas/meshy/meshy-5.json new file mode 100644 index 000000000..f90256413 --- /dev/null +++ b/router-schemas/meshy/meshy-5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"meshy/meshy-5","description":"The request body Comfy Router accepts for the model \"meshy/meshy-5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"09bd559564ba"},"paths":{"/v2/models/meshy/meshy-5":{"post":{"operationId":"runRouterModel","summary":"Run meshy/meshy-5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MeshyTextTo3DTask"},{"properties":{"status":{"enum":["SUCCEEDED"],"type":"string"}},"required":["status"],"type":"object"}],"description":"Comfy Router output schema for the Meshy text-to-3D models: the terminal `GET /proxy/meshy/openapi/v2/text-to-3d/{task_id}` task document, forwarded unchanged. The operation is SUBMIT-AND-POLL — `POST /proxy/meshy/openapi/v2/text-to-3d` answers with a `MeshyTextTo3DCreateResponse` handle whose `result` is the task id, and Router polls on the caller's behalf — so the body a caller receives is the finished task document rather than that handle. The family's three other operations (image-to-3d, multi-image-to-3d and retexture) stay reachable on their own `/proxy/meshy/...` routes and answer closely related task documents; a `{provider}/{model}` call settles onto the text-to-3d generation, which is the one this schema describes.\nThe generated mesh is in `model_urls`, at whichever of `glb`, `fbx`, `usdz` and `obj` the request produced — which of them are populated depends on the request, so read the one that is present rather than keying completion off `glb` alone. `glb` is the rendition a `{\"mode\": \"preview\"}` task with no format selector answers with, but it is not the only shape a success can take. Two neighbouring fields are NOT interchangeable with a mesh and a caller must not key completion off either: `mtl` is a material sidecar for `obj` rather than a mesh in its own right, and `thumbnail_url` is a preview image rather than the asset the call generated. Comfy Router draws exactly that line — a `SUCCEEDED` task carrying none of those four mesh URLs is answered as a Comfy Router error rather than with this document — so through Router a `200` here always carries a mesh, but not always the same one.\n`status` is Meshy's own UPPERCASE vocabulary — `PENDING`, `IN_PROGRESS`, `SUCCEEDED`, `FAILED`, `CANCELED` — forwarded unchanged and not case-folded. `SUCCEEDED` is the one terminal success; `FAILED` and `CANCELED` are terminal failures and reach a Router caller as a Comfy Router error rather than as this document, with the provider's own reason in `task_error.message`. `progress` runs 0..100 and is 100 on a succeeded task.\n`type` says which of the operation's two stages produced the document. `text-to-3d-preview` is the geometry stage, requested with `{\"mode\": \"preview\"}` and a prompt. `text-to-3d-refine` is the separate, dearer texturing task, requested with `{\"mode\": \"refine\"}` against a finished preview's `id` as `preview_task_id`; it is what populates `texture_urls`.\n`negative_prompt` and `texture_richness` are fields Meshy keeps for backward compatibility and does not populate meaningfully; `video_url` is likewise deprecated.","example":{"art_style":"realistic","created_at":1767225600000,"finished_at":1767225719000,"id":"018f2c7a-4b1e-7c3d-9a05-6e2f8b41d0c9","model_urls":{"fbx":"https://example.invalid/meshy/text-to-3d/model.fbx","glb":"https://example.invalid/meshy/text-to-3d/model.glb","mtl":"https://example.invalid/meshy/text-to-3d/model.mtl","obj":"https://example.invalid/meshy/text-to-3d/model.obj","usdz":"https://example.invalid/meshy/text-to-3d/model.usdz"},"progress":100,"prompt":"a red cube","started_at":1767225601000,"status":"SUCCEEDED","thumbnail_url":"https://example.invalid/meshy/text-to-3d/thumbnail.png","type":"text-to-3d-preview"}}}}}}}}},"components":{"schemas":{"MeshyModelUrls":{"description":"Downloadable URLs to the textured 3D model files generated by Meshy.","properties":{"fbx":{"description":"Downloadable URL to the FBX file.","type":"string"},"glb":{"description":"Downloadable URL to the GLB file.","type":"string"},"mtl":{"description":"Downloadable URL to the MTL file.","type":"string"},"obj":{"description":"Downloadable URL to the OBJ file.","type":"string"},"usdz":{"description":"Downloadable URL to the USDZ file.","type":"string"}},"type":"object"},"MeshyTaskError":{"description":"Error object that contains the error message if the task failed.","properties":{"message":{"description":"Detailed error message.","type":"string"}},"type":"object"},"MeshyTaskStatus":{"description":"Status of the task.","enum":["PENDING","IN_PROGRESS","SUCCEEDED","FAILED","CANCELED"],"type":"string"},"MeshyTextTo3DTask":{"properties":{"art_style":{"description":"The unmodified art_style that was used to create the preview task.","type":"string"},"created_at":{"description":"Timestamp of when the task was created, in milliseconds.","format":"int64","type":"integer"},"finished_at":{"description":"Timestamp of when the task was finished, in milliseconds. 0 if not finished.","format":"int64","type":"integer"},"id":{"description":"Unique identifier for the task.","type":"string"},"model_urls":{"$ref":"#/components/schemas/MeshyModelUrls"},"negative_prompt":{"description":"Deprecated field maintained for backward compatibility.","type":"string"},"preceding_tasks":{"description":"The count of preceding tasks. Only meaningful when status is PENDING.","type":"integer"},"progress":{"description":"Progress of the task. 0 if not started, 100 when succeeded.","maximum":100,"minimum":0,"type":"integer"},"prompt":{"description":"The unmodified prompt that was used to create the task.","type":"string"},"started_at":{"description":"Timestamp of when the task was started, in milliseconds. 0 if not started.","format":"int64","type":"integer"},"status":{"$ref":"#/components/schemas/MeshyTaskStatus"},"task_error":{"allOf":[{"$ref":"#/components/schemas/MeshyTaskError"}],"nullable":true},"texture_image_url":{"description":"Downloadable URL to the texture image that was used to guide the texturing process.","type":"string"},"texture_prompt":{"description":"Additional text prompt provided to guide the texturing process during the refine stage.","type":"string"},"texture_richness":{"description":"Deprecated field maintained for backward compatibility.","type":"string"},"texture_urls":{"description":"An array of texture URL objects that are generated from the task.","items":{"$ref":"#/components/schemas/MeshyTextureUrls"},"type":"array"},"thumbnail_url":{"description":"Downloadable URL to the thumbnail image of the model file.","type":"string"},"type":{"description":"Type of the Text to 3D task.","enum":["text-to-3d-preview","text-to-3d-refine"],"type":"string"},"video_url":{"description":"Deprecated field returning the downloadable URL to the preview video.","type":"string"}},"required":["id","status"],"type":"object"},"MeshyTextureUrls":{"description":"Texture URL object containing PBR maps.","properties":{"base_color":{"description":"Downloadable URL to the base color map image.","type":"string"},"metallic":{"description":"Downloadable URL to the metallic map image.","type":"string"},"normal":{"description":"Downloadable URL to the normal map image.","type":"string"},"roughness":{"description":"Downloadable URL to the roughness map image.","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"meshy/meshy-5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/meshy/meshy-6.json b/router-schemas/meshy/meshy-6.json new file mode 100644 index 000000000..a9860cdb4 --- /dev/null +++ b/router-schemas/meshy/meshy-6.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"meshy/meshy-6","description":"The request body Comfy Router accepts for the model \"meshy/meshy-6\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"09bd559564ba"},"paths":{"/v2/models/meshy/meshy-6":{"post":{"operationId":"runRouterModel","summary":"Run meshy/meshy-6 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MeshyTextTo3DTask"},{"properties":{"status":{"enum":["SUCCEEDED"],"type":"string"}},"required":["status"],"type":"object"}],"description":"Comfy Router output schema for the Meshy text-to-3D models: the terminal `GET /proxy/meshy/openapi/v2/text-to-3d/{task_id}` task document, forwarded unchanged. The operation is SUBMIT-AND-POLL — `POST /proxy/meshy/openapi/v2/text-to-3d` answers with a `MeshyTextTo3DCreateResponse` handle whose `result` is the task id, and Router polls on the caller's behalf — so the body a caller receives is the finished task document rather than that handle. The family's three other operations (image-to-3d, multi-image-to-3d and retexture) stay reachable on their own `/proxy/meshy/...` routes and answer closely related task documents; a `{provider}/{model}` call settles onto the text-to-3d generation, which is the one this schema describes.\nThe generated mesh is in `model_urls`, at whichever of `glb`, `fbx`, `usdz` and `obj` the request produced — which of them are populated depends on the request, so read the one that is present rather than keying completion off `glb` alone. `glb` is the rendition a `{\"mode\": \"preview\"}` task with no format selector answers with, but it is not the only shape a success can take. Two neighbouring fields are NOT interchangeable with a mesh and a caller must not key completion off either: `mtl` is a material sidecar for `obj` rather than a mesh in its own right, and `thumbnail_url` is a preview image rather than the asset the call generated. Comfy Router draws exactly that line — a `SUCCEEDED` task carrying none of those four mesh URLs is answered as a Comfy Router error rather than with this document — so through Router a `200` here always carries a mesh, but not always the same one.\n`status` is Meshy's own UPPERCASE vocabulary — `PENDING`, `IN_PROGRESS`, `SUCCEEDED`, `FAILED`, `CANCELED` — forwarded unchanged and not case-folded. `SUCCEEDED` is the one terminal success; `FAILED` and `CANCELED` are terminal failures and reach a Router caller as a Comfy Router error rather than as this document, with the provider's own reason in `task_error.message`. `progress` runs 0..100 and is 100 on a succeeded task.\n`type` says which of the operation's two stages produced the document. `text-to-3d-preview` is the geometry stage, requested with `{\"mode\": \"preview\"}` and a prompt. `text-to-3d-refine` is the separate, dearer texturing task, requested with `{\"mode\": \"refine\"}` against a finished preview's `id` as `preview_task_id`; it is what populates `texture_urls`.\n`negative_prompt` and `texture_richness` are fields Meshy keeps for backward compatibility and does not populate meaningfully; `video_url` is likewise deprecated.","example":{"art_style":"realistic","created_at":1767225600000,"finished_at":1767225719000,"id":"018f2c7a-4b1e-7c3d-9a05-6e2f8b41d0c9","model_urls":{"fbx":"https://example.invalid/meshy/text-to-3d/model.fbx","glb":"https://example.invalid/meshy/text-to-3d/model.glb","mtl":"https://example.invalid/meshy/text-to-3d/model.mtl","obj":"https://example.invalid/meshy/text-to-3d/model.obj","usdz":"https://example.invalid/meshy/text-to-3d/model.usdz"},"progress":100,"prompt":"a red cube","started_at":1767225601000,"status":"SUCCEEDED","thumbnail_url":"https://example.invalid/meshy/text-to-3d/thumbnail.png","type":"text-to-3d-preview"}}}}}}}}},"components":{"schemas":{"MeshyModelUrls":{"description":"Downloadable URLs to the textured 3D model files generated by Meshy.","properties":{"fbx":{"description":"Downloadable URL to the FBX file.","type":"string"},"glb":{"description":"Downloadable URL to the GLB file.","type":"string"},"mtl":{"description":"Downloadable URL to the MTL file.","type":"string"},"obj":{"description":"Downloadable URL to the OBJ file.","type":"string"},"usdz":{"description":"Downloadable URL to the USDZ file.","type":"string"}},"type":"object"},"MeshyTaskError":{"description":"Error object that contains the error message if the task failed.","properties":{"message":{"description":"Detailed error message.","type":"string"}},"type":"object"},"MeshyTaskStatus":{"description":"Status of the task.","enum":["PENDING","IN_PROGRESS","SUCCEEDED","FAILED","CANCELED"],"type":"string"},"MeshyTextTo3DTask":{"properties":{"art_style":{"description":"The unmodified art_style that was used to create the preview task.","type":"string"},"created_at":{"description":"Timestamp of when the task was created, in milliseconds.","format":"int64","type":"integer"},"finished_at":{"description":"Timestamp of when the task was finished, in milliseconds. 0 if not finished.","format":"int64","type":"integer"},"id":{"description":"Unique identifier for the task.","type":"string"},"model_urls":{"$ref":"#/components/schemas/MeshyModelUrls"},"negative_prompt":{"description":"Deprecated field maintained for backward compatibility.","type":"string"},"preceding_tasks":{"description":"The count of preceding tasks. Only meaningful when status is PENDING.","type":"integer"},"progress":{"description":"Progress of the task. 0 if not started, 100 when succeeded.","maximum":100,"minimum":0,"type":"integer"},"prompt":{"description":"The unmodified prompt that was used to create the task.","type":"string"},"started_at":{"description":"Timestamp of when the task was started, in milliseconds. 0 if not started.","format":"int64","type":"integer"},"status":{"$ref":"#/components/schemas/MeshyTaskStatus"},"task_error":{"allOf":[{"$ref":"#/components/schemas/MeshyTaskError"}],"nullable":true},"texture_image_url":{"description":"Downloadable URL to the texture image that was used to guide the texturing process.","type":"string"},"texture_prompt":{"description":"Additional text prompt provided to guide the texturing process during the refine stage.","type":"string"},"texture_richness":{"description":"Deprecated field maintained for backward compatibility.","type":"string"},"texture_urls":{"description":"An array of texture URL objects that are generated from the task.","items":{"$ref":"#/components/schemas/MeshyTextureUrls"},"type":"array"},"thumbnail_url":{"description":"Downloadable URL to the thumbnail image of the model file.","type":"string"},"type":{"description":"Type of the Text to 3D task.","enum":["text-to-3d-preview","text-to-3d-refine"],"type":"string"},"video_url":{"description":"Deprecated field returning the downloadable URL to the preview video.","type":"string"}},"required":["id","status"],"type":"object"},"MeshyTextureUrls":{"description":"Texture URL object containing PBR maps.","properties":{"base_color":{"description":"Downloadable URL to the base color map image.","type":"string"},"metallic":{"description":"Downloadable URL to the metallic map image.","type":"string"},"normal":{"description":"Downloadable URL to the normal map image.","type":"string"},"roughness":{"description":"Downloadable URL to the roughness map image.","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"meshy/meshy-6","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/meshy/meshy-7.json b/router-schemas/meshy/meshy-7.json new file mode 100644 index 000000000..63b787a69 --- /dev/null +++ b/router-schemas/meshy/meshy-7.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"meshy/meshy-7","description":"The request body Comfy Router accepts for the model \"meshy/meshy-7\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"09bd559564ba"},"paths":{"/v2/models/meshy/meshy-7":{"post":{"operationId":"runRouterModel","summary":"Run meshy/meshy-7 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MeshyTextTo3DTask"},{"properties":{"status":{"enum":["SUCCEEDED"],"type":"string"}},"required":["status"],"type":"object"}],"description":"Comfy Router output schema for the Meshy text-to-3D models: the terminal `GET /proxy/meshy/openapi/v2/text-to-3d/{task_id}` task document, forwarded unchanged. The operation is SUBMIT-AND-POLL — `POST /proxy/meshy/openapi/v2/text-to-3d` answers with a `MeshyTextTo3DCreateResponse` handle whose `result` is the task id, and Router polls on the caller's behalf — so the body a caller receives is the finished task document rather than that handle. The family's three other operations (image-to-3d, multi-image-to-3d and retexture) stay reachable on their own `/proxy/meshy/...` routes and answer closely related task documents; a `{provider}/{model}` call settles onto the text-to-3d generation, which is the one this schema describes.\nThe generated mesh is in `model_urls`, at whichever of `glb`, `fbx`, `usdz` and `obj` the request produced — which of them are populated depends on the request, so read the one that is present rather than keying completion off `glb` alone. `glb` is the rendition a `{\"mode\": \"preview\"}` task with no format selector answers with, but it is not the only shape a success can take. Two neighbouring fields are NOT interchangeable with a mesh and a caller must not key completion off either: `mtl` is a material sidecar for `obj` rather than a mesh in its own right, and `thumbnail_url` is a preview image rather than the asset the call generated. Comfy Router draws exactly that line — a `SUCCEEDED` task carrying none of those four mesh URLs is answered as a Comfy Router error rather than with this document — so through Router a `200` here always carries a mesh, but not always the same one.\n`status` is Meshy's own UPPERCASE vocabulary — `PENDING`, `IN_PROGRESS`, `SUCCEEDED`, `FAILED`, `CANCELED` — forwarded unchanged and not case-folded. `SUCCEEDED` is the one terminal success; `FAILED` and `CANCELED` are terminal failures and reach a Router caller as a Comfy Router error rather than as this document, with the provider's own reason in `task_error.message`. `progress` runs 0..100 and is 100 on a succeeded task.\n`type` says which of the operation's two stages produced the document. `text-to-3d-preview` is the geometry stage, requested with `{\"mode\": \"preview\"}` and a prompt. `text-to-3d-refine` is the separate, dearer texturing task, requested with `{\"mode\": \"refine\"}` against a finished preview's `id` as `preview_task_id`; it is what populates `texture_urls`.\n`negative_prompt` and `texture_richness` are fields Meshy keeps for backward compatibility and does not populate meaningfully; `video_url` is likewise deprecated.","example":{"art_style":"realistic","created_at":1767225600000,"finished_at":1767225719000,"id":"018f2c7a-4b1e-7c3d-9a05-6e2f8b41d0c9","model_urls":{"fbx":"https://example.invalid/meshy/text-to-3d/model.fbx","glb":"https://example.invalid/meshy/text-to-3d/model.glb","mtl":"https://example.invalid/meshy/text-to-3d/model.mtl","obj":"https://example.invalid/meshy/text-to-3d/model.obj","usdz":"https://example.invalid/meshy/text-to-3d/model.usdz"},"progress":100,"prompt":"a red cube","started_at":1767225601000,"status":"SUCCEEDED","thumbnail_url":"https://example.invalid/meshy/text-to-3d/thumbnail.png","type":"text-to-3d-preview"}}}}}}}}},"components":{"schemas":{"MeshyModelUrls":{"description":"Downloadable URLs to the textured 3D model files generated by Meshy.","properties":{"fbx":{"description":"Downloadable URL to the FBX file.","type":"string"},"glb":{"description":"Downloadable URL to the GLB file.","type":"string"},"mtl":{"description":"Downloadable URL to the MTL file.","type":"string"},"obj":{"description":"Downloadable URL to the OBJ file.","type":"string"},"usdz":{"description":"Downloadable URL to the USDZ file.","type":"string"}},"type":"object"},"MeshyTaskError":{"description":"Error object that contains the error message if the task failed.","properties":{"message":{"description":"Detailed error message.","type":"string"}},"type":"object"},"MeshyTaskStatus":{"description":"Status of the task.","enum":["PENDING","IN_PROGRESS","SUCCEEDED","FAILED","CANCELED"],"type":"string"},"MeshyTextTo3DTask":{"properties":{"art_style":{"description":"The unmodified art_style that was used to create the preview task.","type":"string"},"created_at":{"description":"Timestamp of when the task was created, in milliseconds.","format":"int64","type":"integer"},"finished_at":{"description":"Timestamp of when the task was finished, in milliseconds. 0 if not finished.","format":"int64","type":"integer"},"id":{"description":"Unique identifier for the task.","type":"string"},"model_urls":{"$ref":"#/components/schemas/MeshyModelUrls"},"negative_prompt":{"description":"Deprecated field maintained for backward compatibility.","type":"string"},"preceding_tasks":{"description":"The count of preceding tasks. Only meaningful when status is PENDING.","type":"integer"},"progress":{"description":"Progress of the task. 0 if not started, 100 when succeeded.","maximum":100,"minimum":0,"type":"integer"},"prompt":{"description":"The unmodified prompt that was used to create the task.","type":"string"},"started_at":{"description":"Timestamp of when the task was started, in milliseconds. 0 if not started.","format":"int64","type":"integer"},"status":{"$ref":"#/components/schemas/MeshyTaskStatus"},"task_error":{"allOf":[{"$ref":"#/components/schemas/MeshyTaskError"}],"nullable":true},"texture_image_url":{"description":"Downloadable URL to the texture image that was used to guide the texturing process.","type":"string"},"texture_prompt":{"description":"Additional text prompt provided to guide the texturing process during the refine stage.","type":"string"},"texture_richness":{"description":"Deprecated field maintained for backward compatibility.","type":"string"},"texture_urls":{"description":"An array of texture URL objects that are generated from the task.","items":{"$ref":"#/components/schemas/MeshyTextureUrls"},"type":"array"},"thumbnail_url":{"description":"Downloadable URL to the thumbnail image of the model file.","type":"string"},"type":{"description":"Type of the Text to 3D task.","enum":["text-to-3d-preview","text-to-3d-refine"],"type":"string"},"video_url":{"description":"Deprecated field returning the downloadable URL to the preview video.","type":"string"}},"required":["id","status"],"type":"object"},"MeshyTextureUrls":{"description":"Texture URL object containing PBR maps.","properties":{"base_color":{"description":"Downloadable URL to the base color map image.","type":"string"},"metallic":{"description":"Downloadable URL to the metallic map image.","type":"string"},"normal":{"description":"Downloadable URL to the normal map image.","type":"string"},"roughness":{"description":"Downloadable URL to the roughness map image.","type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"meshy/meshy-7","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/minimax/minimax-h3.json b/router-schemas/minimax/minimax-h3.json new file mode 100644 index 000000000..f1fc5ff85 --- /dev/null +++ b/router-schemas/minimax/minimax-h3.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"minimax/minimax-h3","description":"The request body Comfy Router accepts for the model \"minimax/minimax-h3\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0868c9d117d2"},"paths":{"/v2/models/minimax/minimax-h3":{"post":{"operationId":"runRouterModel","summary":"Run minimax/minimax-h3 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MinimaxV2TaskResultResponse"}],"description":"Comfy Router output schema for the MiniMax v2 (Hailuo 03) models: the terminal `MinimaxV2TaskResultResponse` poll document. The operation is submit-and-poll and Router polls on the caller's behalf, so the body a caller receives is the finished task (`task.status: succeeded`) rather than the `{task_id}` handle the underlying submit returns.\nThe generated video is at `task.content.url`, and `task.content` is only present on a succeeded task. Every other field is MiniMax's own, forwarded unchanged, but that URL is not: the proxy re-hosts the MP4 and answers a Comfy-signed URL valid for 12 hours (falling back to MiniMax's own, shorter-lived link if the re-host fails). Either way it expires, and Router publishes no task-query route to refresh it with, so download promptly rather than storing the link. A failed task carries `task.error` (code and message) instead of `task.content`, and `task.status` also takes the terminal values `failed`, `cancelled` and `expired`.","example":{"task":{"content":{"url":"https://example.invalid/minimax/minimax-h3/generated.mp4"},"duration":6,"id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","model":"MiniMax-H3","ratio":"16:9","resolution":"768P","status":"succeeded","usage":{"output_seconds":6,"total_seconds":6}}}}}}}}}}},"components":{"schemas":{"MinimaxV2TaskResult":{"description":"A Minimax V2 video generation task.","properties":{"content":{"description":"Generated output; present when status is succeeded.","properties":{"prompt":{"description":"The enhanced video prompt produced by a succeeded h3_context_ir task.","type":"string"},"url":{"description":"Time-limited URL of the generated MP4. Query again for a refreshed URL.","type":"string"}},"type":"object"},"duration":{"description":"The duration of the generated video in seconds.","type":"number"},"error":{"additionalProperties":true,"description":"Error details when status is failed; carries code and message.","type":"object"},"id":{"description":"The task ID.","type":"string"},"model":{"description":"The model used for the task.","type":"string"},"ratio":{"description":"The actual aspect ratio of the generated video.","type":"string"},"resolution":{"description":"The resolution of the generated video.","type":"string"},"status":{"description":"Task status. Options: queued, running, succeeded, failed, cancelled, expired.","type":"string"},"task_type":{"description":"The type of the task.","type":"string"},"usage":{"description":"Usage recorded for the task.","properties":{"completion_tokens":{"type":"integer"},"input_image_count":{"type":"integer"},"input_seconds":{"type":"number"},"output_seconds":{"type":"number"},"prompt_tokens":{"type":"integer"},"total_seconds":{"type":"number"},"total_tokens":{"type":"integer"}},"type":"object"}},"type":"object"},"MinimaxV2TaskResultResponse":{"description":"Response from querying a Minimax V2 video generation task status.","properties":{"task":{"$ref":"#/components/schemas/MinimaxV2TaskResult"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"minimax/minimax-h3","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-4.1-mini.json b/router-schemas/openai/gpt-4.1-mini.json new file mode 100644 index 000000000..1387530a2 --- /dev/null +++ b/router-schemas/openai/gpt-4.1-mini.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-4.1-mini","description":"The request body Comfy Router accepts for the model \"openai/gpt-4.1-mini\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-4.1-mini":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-4.1-mini synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-4.1-mini","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-4.1-nano.json b/router-schemas/openai/gpt-4.1-nano.json new file mode 100644 index 000000000..32c2b1720 --- /dev/null +++ b/router-schemas/openai/gpt-4.1-nano.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-4.1-nano","description":"The request body Comfy Router accepts for the model \"openai/gpt-4.1-nano\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-4.1-nano":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-4.1-nano synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-4.1-nano","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-4.1.json b/router-schemas/openai/gpt-4.1.json new file mode 100644 index 000000000..356879cad --- /dev/null +++ b/router-schemas/openai/gpt-4.1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-4.1","description":"The request body Comfy Router accepts for the model \"openai/gpt-4.1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-4.1":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-4.1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-4.1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-4o.json b/router-schemas/openai/gpt-4o.json new file mode 100644 index 000000000..97bdc86ae --- /dev/null +++ b/router-schemas/openai/gpt-4o.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-4o","description":"The request body Comfy Router accepts for the model \"openai/gpt-4o\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-4o":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-4o synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-4o","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5-mini.json b/router-schemas/openai/gpt-5-mini.json new file mode 100644 index 000000000..6dc71606a --- /dev/null +++ b/router-schemas/openai/gpt-5-mini.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5-mini","description":"The request body Comfy Router accepts for the model \"openai/gpt-5-mini\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5-mini":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5-mini synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5-mini","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5-nano.json b/router-schemas/openai/gpt-5-nano.json new file mode 100644 index 000000000..317ea40f0 --- /dev/null +++ b/router-schemas/openai/gpt-5-nano.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5-nano","description":"The request body Comfy Router accepts for the model \"openai/gpt-5-nano\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5-nano":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5-nano synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5-nano","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5.5-pro.json b/router-schemas/openai/gpt-5.5-pro.json new file mode 100644 index 000000000..37e035cb4 --- /dev/null +++ b/router-schemas/openai/gpt-5.5-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5.5-pro","description":"The request body Comfy Router accepts for the model \"openai/gpt-5.5-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5.5-pro":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5.5-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5.5-pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5.5.json b/router-schemas/openai/gpt-5.5.json new file mode 100644 index 000000000..756cac50d --- /dev/null +++ b/router-schemas/openai/gpt-5.5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5.5","description":"The request body Comfy Router accepts for the model \"openai/gpt-5.5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5.5":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5.5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5.5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5.6-luna.json b/router-schemas/openai/gpt-5.6-luna.json new file mode 100644 index 000000000..d93d5cd30 --- /dev/null +++ b/router-schemas/openai/gpt-5.6-luna.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5.6-luna","description":"The request body Comfy Router accepts for the model \"openai/gpt-5.6-luna\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5.6-luna":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5.6-luna synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5.6-luna","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5.6-sol.json b/router-schemas/openai/gpt-5.6-sol.json new file mode 100644 index 000000000..09001ffef --- /dev/null +++ b/router-schemas/openai/gpt-5.6-sol.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5.6-sol","description":"The request body Comfy Router accepts for the model \"openai/gpt-5.6-sol\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5.6-sol":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5.6-sol synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5.6-sol","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5.6-terra.json b/router-schemas/openai/gpt-5.6-terra.json new file mode 100644 index 000000000..fd9101b42 --- /dev/null +++ b/router-schemas/openai/gpt-5.6-terra.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5.6-terra","description":"The request body Comfy Router accepts for the model \"openai/gpt-5.6-terra\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5.6-terra":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5.6-terra synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5.6-terra","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-5.json b/router-schemas/openai/gpt-5.json new file mode 100644 index 000000000..c731c6247 --- /dev/null +++ b/router-schemas/openai/gpt-5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-5","description":"The request body Comfy Router accepts for the model \"openai/gpt-5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/gpt-5":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-image-1.5.json b/router-schemas/openai/gpt-image-1.5.json new file mode 100644 index 000000000..c7f16320b --- /dev/null +++ b/router-schemas/openai/gpt-image-1.5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-image-1.5","description":"The request body Comfy Router accepts for the model \"openai/gpt-image-1.5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"7b19db5129b9"},"paths":{"/v2/models/openai/gpt-image-1.5":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-image-1.5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIImageGenerationResponse"},{"properties":{"background":{"description":"Whether the generated image's background is opaque or transparent. Populated on the fal-served branch only, which reports `opaque`.","type":"string"},"created":{"description":"Unix timestamp, in seconds, of when the generation completed. Declared `int64` because a present-day epoch value is close enough to 2^31 that an unformatted `integer` generates a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"output_format":{"description":"The encoding of the bytes in `data[].b64_json` (for example `png`). Populated on the fal-served branch; absent on the OpenAI-served one, where the caller's requested `output_format` is authoritative.","type":"string"},"quality":{"description":"The quality tier the generation actually ran at. Populated on the fal-served branch when it can resolve one; absent otherwise.","type":"string"},"size":{"description":"The pixel dimensions the generation actually ran at, as `\u003cwidth\u003ex\u003cheight\u003e`. Populated on the fal-served branch when it can resolve one; absent otherwise.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the OpenAI gpt-image models: OpenAI's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `openai /images/generations` as ReturnModeDirect with no poll route), so the body a caller receives is this finished document from the one call. That row also raises the read limit to `BinaryResultMaxBytes`, because the images can arrive INLINE and `n` admits up to 10 of them at a `size` and `quality` the operation does not bound.\nThe generated images are in `data`, always INLINE at `data[].b64_json`: these three ids have no `url` response format at all, so `url` is never populated on this surface even though the shared response component declares it. That also means the bytes are durable in the only sense that matters here — they are in the body, not behind a link that expires. `revised_prompt` is the prompt OpenAI rewrote rather than an asset, and it is present on a response whose image never arrived, so it must not be read as one. `usage` is OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`openai/gpt-image-2` has a SECOND producer for this document. When a request is eligible, Comfy may serve it through fal rather than through OpenAI, synthesising this envelope rather than forwarding one. Both producers answer the same asset leaf — one `data` entry per image, each carrying `b64_json` and never `url` — which is what makes the contract above hold whichever partner ran. The bodies are not byte-identical, though, and this document does not promise they are: the fal-served envelope omits `usage` and populates the top-level `created`, `output_format` and `background` fields (plus `quality` and `size` when it can resolve them), while the OpenAI-served one forwards `usage` through. Branch on the keys a response actually carries rather than on an assumed producer; which partner serves a given request is a Comfy routing decision that can change without notice, and is not part of this contract.","example":{"created":1767225600,"data":[{"b64_json":"PGJhc2U2ND4="}],"usage":{"input_tokens":12,"output_tokens":1056,"total_tokens":1068}}}}}}}}}},"components":{"schemas":{"OpenAIImageGenerationResponse":{"properties":{"data":{"items":{"properties":{"b64_json":{"description":"Base64 encoded image data","type":"string"},"revised_prompt":{"description":"Revised prompt","type":"string"},"url":{"description":"URL of the image","type":"string"}},"type":"object"},"type":"array"},"usage":{"properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-image-1.5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-image-1.json b/router-schemas/openai/gpt-image-1.json new file mode 100644 index 000000000..b61694171 --- /dev/null +++ b/router-schemas/openai/gpt-image-1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-image-1","description":"The request body Comfy Router accepts for the model \"openai/gpt-image-1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"7b19db5129b9"},"paths":{"/v2/models/openai/gpt-image-1":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-image-1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIImageGenerationResponse"},{"properties":{"background":{"description":"Whether the generated image's background is opaque or transparent. Populated on the fal-served branch only, which reports `opaque`.","type":"string"},"created":{"description":"Unix timestamp, in seconds, of when the generation completed. Declared `int64` because a present-day epoch value is close enough to 2^31 that an unformatted `integer` generates a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"output_format":{"description":"The encoding of the bytes in `data[].b64_json` (for example `png`). Populated on the fal-served branch; absent on the OpenAI-served one, where the caller's requested `output_format` is authoritative.","type":"string"},"quality":{"description":"The quality tier the generation actually ran at. Populated on the fal-served branch when it can resolve one; absent otherwise.","type":"string"},"size":{"description":"The pixel dimensions the generation actually ran at, as `\u003cwidth\u003ex\u003cheight\u003e`. Populated on the fal-served branch when it can resolve one; absent otherwise.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the OpenAI gpt-image models: OpenAI's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `openai /images/generations` as ReturnModeDirect with no poll route), so the body a caller receives is this finished document from the one call. That row also raises the read limit to `BinaryResultMaxBytes`, because the images can arrive INLINE and `n` admits up to 10 of them at a `size` and `quality` the operation does not bound.\nThe generated images are in `data`, always INLINE at `data[].b64_json`: these three ids have no `url` response format at all, so `url` is never populated on this surface even though the shared response component declares it. That also means the bytes are durable in the only sense that matters here — they are in the body, not behind a link that expires. `revised_prompt` is the prompt OpenAI rewrote rather than an asset, and it is present on a response whose image never arrived, so it must not be read as one. `usage` is OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`openai/gpt-image-2` has a SECOND producer for this document. When a request is eligible, Comfy may serve it through fal rather than through OpenAI, synthesising this envelope rather than forwarding one. Both producers answer the same asset leaf — one `data` entry per image, each carrying `b64_json` and never `url` — which is what makes the contract above hold whichever partner ran. The bodies are not byte-identical, though, and this document does not promise they are: the fal-served envelope omits `usage` and populates the top-level `created`, `output_format` and `background` fields (plus `quality` and `size` when it can resolve them), while the OpenAI-served one forwards `usage` through. Branch on the keys a response actually carries rather than on an assumed producer; which partner serves a given request is a Comfy routing decision that can change without notice, and is not part of this contract.","example":{"created":1767225600,"data":[{"b64_json":"PGJhc2U2ND4="}],"usage":{"input_tokens":12,"output_tokens":1056,"total_tokens":1068}}}}}}}}}},"components":{"schemas":{"OpenAIImageGenerationResponse":{"properties":{"data":{"items":{"properties":{"b64_json":{"description":"Base64 encoded image data","type":"string"},"revised_prompt":{"description":"Revised prompt","type":"string"},"url":{"description":"URL of the image","type":"string"}},"type":"object"},"type":"array"},"usage":{"properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-image-1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/gpt-image-2.json b/router-schemas/openai/gpt-image-2.json new file mode 100644 index 000000000..3d89c6b9e --- /dev/null +++ b/router-schemas/openai/gpt-image-2.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/gpt-image-2","description":"The request body Comfy Router accepts for the model \"openai/gpt-image-2\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"7b19db5129b9"},"paths":{"/v2/models/openai/gpt-image-2":{"post":{"operationId":"runRouterModel","summary":"Run openai/gpt-image-2 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIImageGenerationResponse"},{"properties":{"background":{"description":"Whether the generated image's background is opaque or transparent. Populated on the fal-served branch only, which reports `opaque`.","type":"string"},"created":{"description":"Unix timestamp, in seconds, of when the generation completed. Declared `int64` because a present-day epoch value is close enough to 2^31 that an unformatted `integer` generates a 32-bit field in many SDK generators.","format":"int64","type":"integer"},"output_format":{"description":"The encoding of the bytes in `data[].b64_json` (for example `png`). Populated on the fal-served branch; absent on the OpenAI-served one, where the caller's requested `output_format` is authoritative.","type":"string"},"quality":{"description":"The quality tier the generation actually ran at. Populated on the fal-served branch when it can resolve one; absent otherwise.","type":"string"},"size":{"description":"The pixel dimensions the generation actually ran at, as `\u003cwidth\u003ex\u003cheight\u003e`. Populated on the fal-served branch when it can resolve one; absent otherwise.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the OpenAI gpt-image models: OpenAI's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `openai /images/generations` as ReturnModeDirect with no poll route), so the body a caller receives is this finished document from the one call. That row also raises the read limit to `BinaryResultMaxBytes`, because the images can arrive INLINE and `n` admits up to 10 of them at a `size` and `quality` the operation does not bound.\nThe generated images are in `data`, always INLINE at `data[].b64_json`: these three ids have no `url` response format at all, so `url` is never populated on this surface even though the shared response component declares it. That also means the bytes are durable in the only sense that matters here — they are in the body, not behind a link that expires. `revised_prompt` is the prompt OpenAI rewrote rather than an asset, and it is present on a response whose image never arrived, so it must not be read as one. `usage` is OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`openai/gpt-image-2` has a SECOND producer for this document. When a request is eligible, Comfy may serve it through fal rather than through OpenAI, synthesising this envelope rather than forwarding one. Both producers answer the same asset leaf — one `data` entry per image, each carrying `b64_json` and never `url` — which is what makes the contract above hold whichever partner ran. The bodies are not byte-identical, though, and this document does not promise they are: the fal-served envelope omits `usage` and populates the top-level `created`, `output_format` and `background` fields (plus `quality` and `size` when it can resolve them), while the OpenAI-served one forwards `usage` through. Branch on the keys a response actually carries rather than on an assumed producer; which partner serves a given request is a Comfy routing decision that can change without notice, and is not part of this contract.","example":{"created":1767225600,"data":[{"b64_json":"PGJhc2U2ND4="}],"usage":{"input_tokens":12,"output_tokens":1056,"total_tokens":1068}}}}}}}}}},"components":{"schemas":{"OpenAIImageGenerationResponse":{"properties":{"data":{"items":{"properties":{"b64_json":{"description":"Base64 encoded image data","type":"string"},"revised_prompt":{"description":"Revised prompt","type":"string"},"url":{"description":"URL of the image","type":"string"}},"type":"object"},"type":"array"},"usage":{"properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/gpt-image-2","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/o1-pro.json b/router-schemas/openai/o1-pro.json new file mode 100644 index 000000000..6720598b2 --- /dev/null +++ b/router-schemas/openai/o1-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/o1-pro","description":"The request body Comfy Router accepts for the model \"openai/o1-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/o1-pro":{"post":{"operationId":"runRouterModel","summary":"Run openai/o1-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/o1-pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/o1.json b/router-schemas/openai/o1.json new file mode 100644 index 000000000..a342b2e8f --- /dev/null +++ b/router-schemas/openai/o1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/o1","description":"The request body Comfy Router accepts for the model \"openai/o1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/o1":{"post":{"operationId":"runRouterModel","summary":"Run openai/o1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/o1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/o3.json b/router-schemas/openai/o3.json new file mode 100644 index 000000000..7837a7510 --- /dev/null +++ b/router-schemas/openai/o3.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/o3","description":"The request body Comfy Router accepts for the model \"openai/o3\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/o3":{"post":{"operationId":"runRouterModel","summary":"Run openai/o3 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/o3","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/openai/o4-mini.json b/router-schemas/openai/o4-mini.json new file mode 100644 index 000000000..563a0b6ab --- /dev/null +++ b/router-schemas/openai/o4-mini.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"openai/o4-mini","description":"The request body Comfy Router accepts for the model \"openai/o4-mini\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"53b9d6734385"},"paths":{"/v2/models/openai/o4-mini":{"post":{"operationId":"runRouterModel","summary":"Run openai/o4-mini synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OpenAIResponse"}],"description":"Comfy Router output schema for the OpenAI Responses text models: the `OpenAIResponse` document `POST /proxy/openai/v1/responses` answers with, forwarded unchanged. The operation is DIRECT-RETURN — `routerresult/classification.go` classifies `{provider: openai, endpoint: /v1/responses}` as ReturnModeDirect with no poll route — so the body a caller receives is this finished document from the one call, not a task handle Router polls on.\nThe generated text is at `output[].content[].text`. Neither container above it is the result on its own: `output` is an array of `OutputItem`, which is a `oneOf` over six item types (`OutputMessage`, the four tool calls, and `ReasoningItem`), so a response whose only items are a `ReasoningItem` or a `web_search_call` carries an `output` that is non-empty and no text at all. Only the `OutputMessage` branch has `content`, and only its `output_text` content part (`OutputTextContent`) has `text` — which is why the nightly SDK case for this family asserts the leaf path `output[].content[].text` rather than the `output` container (`testing/e2e/router_sdk/cases.d/openai_responses.json`). `output_text` at the root is the same text aggregated, but it is an SDK-only convenience field rather than something every client sees, so it is not the leaf to key off.\n`status` is OpenAI's own vocabulary (`completed`, `failed`, `in_progress`, `cancelled`, `queued`, `incomplete`), forwarded unchanged. An `incomplete` response still carries whatever text was produced before the cut, with the reason at `incomplete_details.reason` — `max_output_tokens` is the expected one for a request that caps the budget. `error` is populated instead when `status` is `failed`, and `usage` reports OpenAI's own token accounting — OpenAI's numbers, not the Comfy charge.\n`stream` and `background` are SETTLED TO FALSE, not merely discouraged. Router CAPTURES a /proxy/ response rather than streaming it and answers a direct-return operation out of that one response, so neither a live stream nor a queued handle can be served here: a streamed request would be answered a document that is not this one AND would go unmetered (the Rewrite's ModifyResponse cannot decode an SSE payload), and `background: true` returns a queued 200 carrying no token counts that the same ModifyResponse would meter off usage the document does not have. Both are therefore forced to `false` on a Router-dispatched request — `routerSettledBoolFields` (`server/middleware/router_model_catalog.go`), the same treatment the Anthropic messages and Gemini Interactions routes already get — so a caller who names either one is answered the document below rather than refused. Both stay fully reachable at `POST /proxy/openai/v1/responses`, which the settlement does not touch.\n`model` on the RESULT is the RESOLVED provider-side snapshot OpenAI actually ran (`gpt-4.1` in, `gpt-4.1-2025-04-14` back), which is a different vocabulary from the request's allowlist — see the note on `ResponseProperties` for why the allowlist enum constrains only the request half.","example":{"completed_at":1767225601,"created_at":1767225600,"id":"resp_0a1b2c3d4e5f6a7b8c9d0e1f","object":"response","output":[{"content":[{"annotations":[],"text":"ok","type":"output_text"}],"id":"msg_0a1b2c3d4e5f6a7b8c9d0e1f","role":"assistant","status":"completed","type":"message"}],"output_text":"ok","status":"completed","usage":{"input_tokens":14,"input_tokens_details":{"cached_tokens":0},"output_tokens":2,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16}}}}}}}}}},"components":{"schemas":{"ComputerToolCall":{"description":"A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n","properties":{"action":{"type":"object"},"call_id":{"description":"An identifier used when responding to the tool call with output.\n","type":"string"},"id":{"description":"The unique ID of the computer call.","type":"string"},"pending_safety_checks":{"description":"The pending safety checks for the computer call.\n","items":{"additionalProperties":true,"type":"object"},"type":"array"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"default":"computer_call","description":"The type of the computer call. Always `computer_call`.","enum":["computer_call"],"type":"string"}},"required":["type","id","action","call_id","pending_safety_checks","status"],"title":"Computer tool call","type":"object"},"ComputerUsePreviewTool":{"description":"A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use).","properties":{"display_height":{"description":"The height of the computer display.","type":"integer"},"display_width":{"description":"The width of the computer display.","type":"integer"},"environment":{"description":"The type of computer environment to control.","enum":["windows","mac","linux","ubuntu","browser"],"type":"string"},"type":{"default":"computer_use_preview","description":"The type of the computer use tool. Always `computer_use_preview`.","enum":["computer_use_preview"],"type":"string","x-stainless-const":true}},"required":["type","environment","display_width","display_height"],"title":"Computer use preview","type":"object"},"FileSearchTool":{"properties":{"type":{"description":"The type of tool","enum":["file_search"],"type":"string"},"vector_store_ids":{"description":"IDs of vector stores to search in","items":{"type":"string"},"type":"array"}},"required":["type","vector_store_ids"],"type":"object"},"FileSearchToolCall":{"description":"The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n","properties":{"id":{"description":"The unique ID of the file search tool call.\n","type":"string"},"queries":{"description":"The queries used to search for files.\n","items":{"type":"string"},"type":"array"},"results":{"description":"The results of the file search tool call.\n","items":{"properties":{"file_id":{"description":"The unique ID of the file.\n","type":"string"},"filename":{"description":"The name of the file.\n","type":"string"},"score":{"description":"The relevance score of the file - a value between 0 and 1.\n","format":"float","type":"number"},"text":{"description":"The text that was retrieved from the file.\n","type":"string"}},"type":"object"},"type":"array"},"status":{"description":"The status of the file search tool call. One of `in_progress`,\n`searching`, `incomplete` or `failed`,\n","enum":["in_progress","searching","completed","incomplete","failed"],"type":"string"},"type":{"description":"The type of the file search tool call. Always `file_search_call`.\n","enum":["file_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status","queries"],"title":"File search tool call","type":"object"},"FunctionTool":{"properties":{"description":{"description":"Description of what the function does","type":"string"},"name":{"description":"Name of the function","type":"string"},"parameters":{"description":"JSON Schema object describing the function parameters","type":"object"},"type":{"description":"The type of tool","enum":["function"],"type":"string"}},"required":["type","name","parameters"],"type":"object"},"FunctionToolCall":{"description":"A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n","properties":{"arguments":{"description":"A JSON string of the arguments to pass to the function.\n","type":"string"},"call_id":{"description":"The unique ID of the function tool call generated by the model.\n","type":"string"},"id":{"description":"The unique ID of the function tool call.\n","type":"string"},"name":{"description":"The name of the function to run.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"type":{"description":"The type of the function tool call. Always `function_call`.\n","enum":["function_call"],"type":"string","x-stainless-const":true}},"required":["type","call_id","name","arguments"],"title":"Function tool call","type":"object"},"ImageGenerationCall":{"description":"An image generation tool call. `result` carries the generated image as base64 bytes on a completed call and is null while the call is still running or if it produced nothing.\n","properties":{"id":{"description":"The unique ID of the image generation call.","type":"string"},"result":{"description":"The generated image, base64-encoded.","nullable":true,"type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`,\n`generating` or `failed`.\n","type":"string"},"type":{"description":"The type of the item. Always `image_generation_call`.","enum":["image_generation_call"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Image generation call","type":"object"},"ModelResponseProperties":{"description":"Common properties for model responses","properties":{"instructions":{"description":"Instructions for the model on how to generate the response","nullable":true,"type":"string"},"max_output_tokens":{"description":"Maximum number of tokens to generate","type":"integer"},"model":{"description":"The model used to generate the response","type":"string"},"temperature":{"default":1,"description":"Controls randomness in the response","maximum":2,"minimum":0,"type":"number"},"top_p":{"default":1,"description":"Controls diversity of the response via nucleus sampling","maximum":1,"minimum":0,"type":"number"},"truncation":{"default":"disabled","description":"How to handle truncation of the response","enum":["disabled","auto"],"type":"string"}},"type":"object"},"OpenAIResponse":{"allOf":[{"$ref":"#/components/schemas/ModelResponseProperties"},{"$ref":"#/components/schemas/ResponseProperties"},{"properties":{"background":{"description":"Whether the model response runs in the background.","type":"boolean"},"billing":{"description":"Billing information for the response.","properties":{"payer":{"description":"The party responsible for paying for the response.","type":"string"}},"type":"object"},"completed_at":{"description":"Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`.","nullable":true,"type":"number"},"created_at":{"description":"Unix timestamp (in seconds) of when this Response was created.","type":"number"},"error":{"allOf":[{"$ref":"#/components/schemas/ResponseError"}],"nullable":true},"frequency_penalty":{"description":"Penalizes new tokens based on their existing frequency in the text so far.","type":"number"},"id":{"description":"Unique identifier for this Response.","type":"string"},"incomplete_details":{"description":"Details about why the response is incomplete.\n","nullable":true,"properties":{"reason":{"description":"The reason why the response is incomplete.","enum":["max_output_tokens","content_filter"],"type":"string"}},"type":"object"},"max_tool_calls":{"description":"The maximum number of total calls to built-in tools that can be processed in a response.","nullable":true,"type":"integer"},"metadata":{"additionalProperties":{"type":"string"},"description":"Set of key-value pairs that can be attached to the response.","nullable":true,"type":"object"},"moderation":{"additionalProperties":true,"description":"Moderation results for the response input and output, if moderated completions were requested.","nullable":true,"type":"object"},"object":{"description":"The object type of this resource - always set to `response`.","enum":["response"],"type":"string","x-stainless-const":true},"output":{"description":"An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n","items":{"$ref":"#/components/schemas/OutputItem"},"type":"array"},"output_text":{"description":"SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n","nullable":true,"type":"string","x-oaiSupportedSDKs":["python","javascript"]},"parallel_tool_calls":{"default":true,"description":"Whether to allow the model to run tool calls in parallel.\n","type":"boolean"},"presence_penalty":{"description":"Penalizes new tokens based on whether they appear in the text so far.","type":"number"},"prompt_cache_key":{"description":"Used by OpenAI to cache responses for similar requests to optimize cache hit rates. Replaces the `user` field.","nullable":true,"type":"string"},"prompt_cache_retention":{"description":"The retention policy for the prompt cache, e.g. `in_memory` or `24h`.","nullable":true,"type":"string"},"safety_identifier":{"description":"A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.","nullable":true,"type":"string"},"service_tier":{"description":"The processing tier used to serve the request, e.g. `auto`, `default`, `flex`, `scale`, or `priority`.","nullable":true,"type":"string"},"status":{"description":"The status of the response generation. One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or `incomplete`.","enum":["completed","failed","in_progress","cancelled","queued","incomplete"],"type":"string"},"store":{"description":"Whether the response is stored for later retrieval via the API.","type":"boolean"},"tool_usage":{"description":"Token and request usage broken down by built-in tool.","properties":{"image_gen":{"description":"Image generation tool token usage.","properties":{"input_tokens":{"type":"integer"},"input_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"output_tokens":{"type":"integer"},"output_tokens_details":{"properties":{"image_tokens":{"type":"integer"},"text_tokens":{"type":"integer"}},"type":"object"},"total_tokens":{"type":"integer"}},"type":"object"},"web_search":{"description":"Web search tool usage.","properties":{"num_requests":{"type":"integer"}},"type":"object"}},"type":"object"},"top_logprobs":{"description":"The maximum number of most likely tokens to return at each token position, each with an associated log probability.","nullable":true,"type":"integer"},"usage":{"$ref":"#/components/schemas/ResponseUsage"},"user":{"description":"Deprecated identifier for the end-user. Replaced by `safety_identifier` and `prompt_cache_key`.","nullable":true,"type":"string"}},"type":"object"}],"description":"A response from the model","type":"object"},"OutputAudioContent":{"properties":{"data":{"description":"Base64-encoded audio data","type":"string"},"transcript":{"description":"Transcript of the audio","type":"string"},"type":{"description":"The type of output content","enum":["output_audio"],"type":"string"}},"required":["type","data","transcript"],"type":"object"},"OutputContent":{"oneOf":[{"$ref":"#/components/schemas/OutputTextContent"},{"$ref":"#/components/schemas/OutputAudioContent"},{"$ref":"#/components/schemas/RefusalContent"}]},"OutputItem":{"oneOf":[{"$ref":"#/components/schemas/OutputMessage"},{"$ref":"#/components/schemas/FileSearchToolCall"},{"$ref":"#/components/schemas/FunctionToolCall"},{"$ref":"#/components/schemas/WebSearchToolCall"},{"$ref":"#/components/schemas/ComputerToolCall"},{"$ref":"#/components/schemas/ReasoningItem"},{"$ref":"#/components/schemas/ImageGenerationCall"}]},"OutputMessage":{"properties":{"content":{"description":"The content of the message","items":{"$ref":"#/components/schemas/OutputContent"},"type":"array"},"id":{"description":"The unique ID of the output message","type":"string"},"phase":{"description":"Labels an assistant message as intermediate commentary (`commentary`) or the final answer (`final_answer`)","type":"string"},"role":{"description":"The role of the message","enum":["assistant"],"type":"string"},"status":{"description":"The status of the message, e.g. `in_progress`, `completed`, or `incomplete`","type":"string"},"type":{"description":"The type of output item","enum":["message"],"type":"string"}},"required":["type","role","content"],"type":"object"},"OutputTextContent":{"properties":{"annotations":{"description":"Annotations attached to the text content, such as file citations or URL citations","items":{"additionalProperties":true,"type":"object"},"type":"array"},"logprobs":{"description":"Log probability information for the output tokens","items":{"additionalProperties":true,"type":"object"},"type":"array"},"text":{"description":"The text content","type":"string"},"type":{"description":"The type of output content","enum":["output_text"],"type":"string"}},"required":["type","text"],"type":"object"},"Reasoning":{"description":"**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n","properties":{"context":{"description":"Controls which reasoning items are rendered back to the model on later turns, e.g. `auto`, `current_turn`, or `all_turns`.","nullable":true,"type":"string"},"effort":{"allOf":[{"$ref":"#/components/schemas/ReasoningEffort"}],"nullable":true},"generate_summary":{"deprecated":true,"description":"**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"type":"string"},"mode":{"description":"The reasoning mode used for the response.","type":"string"},"summary":{"description":"A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n","enum":["auto","concise","detailed"],"nullable":true,"type":"string"}},"title":"Reasoning","type":"object"},"ReasoningEffort":{"default":"medium","description":"**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n","enum":["low","medium","high"],"type":"string"},"ReasoningItem":{"description":"A description of the chain of thought used by a reasoning model while generating\na response.\n","properties":{"id":{"description":"The unique identifier of the reasoning content.\n","type":"string"},"status":{"description":"The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n","enum":["in_progress","completed","incomplete"],"type":"string"},"summary":{"description":"Reasoning text contents.\n","items":{"properties":{"text":{"description":"A short summary of the reasoning used by the model when generating\nthe response.\n","type":"string"},"type":{"description":"The type of the object. Always `summary_text`.\n","enum":["summary_text"],"type":"string","x-stainless-const":true}},"required":["type","text"],"type":"object"},"type":"array"},"type":{"description":"The type of the object. Always `reasoning`.\n","enum":["reasoning"],"type":"string","x-stainless-const":true}},"required":["id","summary","type"],"title":"Reasoning","type":"object"},"RefusalContent":{"description":"A refusal emitted by the model in place of generated content. It arrives inside an `OutputMessage`, exactly where an `output_text` part would, and the response's `status` is still `completed`.\n","properties":{"refusal":{"description":"The refusal explanation from the model.","type":"string"},"type":{"description":"The type of output content. Always `refusal`.","enum":["refusal"],"type":"string","x-stainless-const":true}},"required":["type","refusal"],"title":"Refusal","type":"object"},"ResponseError":{"description":"An error object returned when the model fails to generate a Response.","properties":{"code":{"$ref":"#/components/schemas/ResponseErrorCode"},"message":{"description":"A human-readable description of the error.","type":"string"}},"required":["code","message"],"type":"object"},"ResponseErrorCode":{"description":"The error code for the response.","enum":["server_error","rate_limit_exceeded","invalid_prompt","vector_store_timeout","invalid_image","invalid_image_format","invalid_base64_image","invalid_image_url","image_too_large","image_too_small","image_parse_error","image_content_policy_violation","invalid_image_mode","image_file_too_large","unsupported_image_media_type","empty_image_file","failed_to_download_image","image_file_not_found"],"type":"string"},"ResponseFormatJsonObject":{"description":"JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n","properties":{"type":{"description":"The type of response format being defined. Always `json_object`.","enum":["json_object"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"JSON object","type":"object"},"ResponseFormatJsonSchemaSchema":{"additionalProperties":true,"description":"The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n","title":"JSON schema","type":"object"},"ResponseFormatText":{"description":"Default response format. Used to generate text responses.\n","properties":{"type":{"description":"The type of response format being defined. Always `text`.","enum":["text"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Text","type":"object"},"ResponseProperties":{"properties":{"instructions":{"description":"Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n","nullable":true,"type":"string"},"max_output_tokens":{"description":"An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n","type":"integer"},"previous_response_id":{"description":"The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state).\n","nullable":true,"type":"string"},"reasoning":{"$ref":"#/components/schemas/Reasoning"},"text":{"properties":{"format":{"$ref":"#/components/schemas/TextResponseFormatConfiguration"},"verbosity":{"description":"Constrains the verbosity of the model's response. One of `low`, `medium`, or `high`.","type":"string"}},"type":"object"},"tool_choice":{"description":"How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n","oneOf":[{"$ref":"#/components/schemas/ToolChoiceOptions"},{"$ref":"#/components/schemas/ToolChoiceTypes"},{"$ref":"#/components/schemas/ToolChoiceFunction"}]},"tools":{"items":{"$ref":"#/components/schemas/Tool"},"type":"array"},"truncation":{"default":"disabled","description":"The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.\n","enum":["auto","disabled"],"type":"string"}},"type":"object"},"ResponseUsage":{"description":"Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n","properties":{"input_tokens":{"description":"The number of input tokens.","type":"integer"},"input_tokens_details":{"description":"A detailed breakdown of the input tokens.","properties":{"cache_write_tokens":{"description":"The number of input tokens that were written to the cache.","type":"integer"},"cached_tokens":{"description":"The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching).\n","type":"integer"}},"required":["cached_tokens"],"type":"object"},"output_tokens":{"description":"The number of output tokens.","type":"integer"},"output_tokens_details":{"description":"A detailed breakdown of the output tokens.","properties":{"reasoning_tokens":{"description":"The number of reasoning tokens.","type":"integer"}},"required":["reasoning_tokens"],"type":"object"},"total_tokens":{"description":"The total number of tokens used.","type":"integer"}},"required":["input_tokens","input_tokens_details","output_tokens","output_tokens_details","total_tokens"],"type":"object"},"TextResponseFormatConfiguration":{"description":"An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n","oneOf":[{"$ref":"#/components/schemas/ResponseFormatText"},{"$ref":"#/components/schemas/TextResponseFormatJsonSchema"},{"$ref":"#/components/schemas/ResponseFormatJsonObject"}]},"TextResponseFormatJsonSchema":{"description":"JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n","properties":{"description":{"description":"A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n","type":"string"},"name":{"description":"The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n","type":"string"},"schema":{"$ref":"#/components/schemas/ResponseFormatJsonSchemaSchema"},"strict":{"default":false,"description":"Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n","type":"boolean"},"type":{"description":"The type of response format being defined. Always `json_schema`.","enum":["json_schema"],"type":"string","x-stainless-const":true}},"required":["type","schema","name"],"title":"JSON schema","type":"object"},"Tool":{"discriminator":{"mapping":{"computer_use_preview":"#/components/schemas/ComputerUsePreviewTool","file_search":"#/components/schemas/FileSearchTool","function":"#/components/schemas/FunctionTool","web_search_preview":"#/components/schemas/WebSearchPreviewTool","web_search_preview_2025_03_11":"#/components/schemas/WebSearchPreviewTool"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/FileSearchTool"},{"$ref":"#/components/schemas/FunctionTool"},{"$ref":"#/components/schemas/WebSearchPreviewTool"},{"$ref":"#/components/schemas/ComputerUsePreviewTool"}]},"ToolChoiceFunction":{"description":"Use this option to force the model to call a specific function.\n","properties":{"name":{"description":"The name of the function to call.","type":"string"},"type":{"description":"For function calling, the type is always `function`.","enum":["function"],"type":"string","x-stainless-const":true}},"required":["type","name"],"title":"Function tool","type":"object"},"ToolChoiceOptions":{"description":"Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n","enum":["none","auto","required"],"title":"Tool choice mode","type":"string"},"ToolChoiceTypes":{"description":"Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n","properties":{"type":{"description":"The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n","enum":["file_search","web_search_preview","computer_use_preview","web_search_preview_2025_03_11"],"type":"string"}},"required":["type"],"title":"Hosted tool","type":"object"},"WebSearchPreviewTool":{"description":"This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search).","properties":{"search_context_size":{"description":"High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default.","enum":["low","medium","high"],"type":"string"},"type":{"default":"web_search_preview","description":"The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`.","enum":["web_search_preview","web_search_preview_2025_03_11"],"type":"string","x-stainless-const":true}},"required":["type"],"title":"Web search preview","type":"object"},"WebSearchToolCall":{"description":"The results of a web search tool call. See the\n[web search guide](/docs/guides/tools-web-search) for more information.\n","properties":{"id":{"description":"The unique ID of the web search tool call.\n","type":"string"},"status":{"description":"The status of the web search tool call.\n","enum":["in_progress","searching","completed","failed"],"type":"string"},"type":{"description":"The type of the web search tool call. Always `web_search_call`.\n","enum":["web_search_call"],"type":"string","x-stainless-const":true}},"required":["id","type","status"],"title":"Web search tool call","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"openai/o4-mini","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/qwen/qwen-image-3.0-pro.json b/router-schemas/qwen/qwen-image-3.0-pro.json new file mode 100644 index 000000000..c522766c5 --- /dev/null +++ b/router-schemas/qwen/qwen-image-3.0-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"qwen/qwen-image-3.0-pro","description":"The request body Comfy Router accepts for the model \"qwen/qwen-image-3.0-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58be368bd0ed"},"paths":{"/v2/models/qwen/qwen-image-3.0-pro":{"post":{"operationId":"runRouterModel","summary":"Run qwen/qwen-image-3.0-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/QwenMultimodalGenerationResponse"}],"description":"Comfy Router output schema for the Qwen Image 3.0 models: the DashScope multimodal-generation response, forwarded unchanged. The operation is DIRECT-RETURN (`routerresult/classification.go` records `{provider: qwen, endpoint: /api/v1/services/aigc/multimodal-generation/generation}` as `ReturnModeDirect`), so the body a caller receives is this finished document from the one call rather than a task handle. `multimodalGenerationProxy`'s `ModifyResponse` meters it synchronously through `TrackUsage`, `QWEN_TASK_TYPE` registers no task checker, and the rewrite DELETES `X-DashScope-Async` — there is nothing to poll.\nThe generated image is at `output.choices[].message.content[].image`. DashScope documents that URL as valid for 24 HOURS, so download it promptly rather than storing it. Both `choices` and `content` are ARRAYS — `parameters.n` admits up to six images — so a caller reads every element rather than only the first, and that LEAF rather than the `output` container is what says a generation finished: a `content` element carrying only `text` produced no asset.\n`usage` is DashScope's own accounting — `output_image_count` plus the `qima_input_*` / `qima_output_*` billing tiers it derives from the resolved resolution — and NOT the Comfy charge. Router reads those same fields to meter the call, but what a caller is charged is Comfy's rate card. DashScope documents `usage` as returned only on success.\n`code` and `message` are DashScope's failure pair, absent on a success. Neither they nor `output` are constrained here, and `output` is deliberately NOT required: this operation's `Classification` names no `DirectState` reader, so Router treats a 2xx status as the whole success test and never inspects the body (`routerresult/extract.go`). A caller must key completion off the image leaf above rather than off the `200` alone.","example":{"output":{"choices":[{"finish_reason":"stop","message":{"content":[{"image":"https://example.invalid/qwen/generated.png"}],"role":"assistant"}}]},"request_id":"9f2c1b3a-5d4e-4a67-8b90-1c2d3e4f5a6b","usage":{"input_image_count":0,"output_height":512,"output_image_count":1,"output_image_type":"qima_output_1k","output_width":512}}}}}}}}}},"components":{"schemas":{"QwenMultimodalGenerationResponse":{"properties":{"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"output":{"description":"Contains the model generation results","properties":{"choices":{"description":"The list of result options","items":{"properties":{"finish_reason":{"description":"The reason why the task stopped. The value is stop when the task completes normally","type":"string"},"message":{"description":"The message returned by the model","properties":{"content":{"description":"The message content containing the generated image information","items":{"properties":{"image":{"description":"The URL of the generated image in PNG format. The link is valid for 24 hours","type":"string"},"text":{"description":"A textual element returned in place of an image. An element carrying only this field produced NO asset, so a caller keys completion off `image` rather than off the presence of a content element","type":"string"}},"type":"object"},"type":"array"},"role":{"description":"The role of the message. Fixed as assistant","type":"string"}},"type":"object"}},"type":"object"},"type":"array"}},"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"The resource usage of this call. Only returned on success","properties":{"input_image_count":{"description":"The number of input images in the request. Returns 0 for text-to-image","type":"integer"},"input_image_type":{"description":"The input image billing tier, qima_input_1k or qima_input_2k, determined by the output resolution pixel area","type":"string"},"output_height":{"description":"The height of the final output image in pixels","type":"integer"},"output_image_count":{"description":"The actual number of output images returned","type":"integer"},"output_image_type":{"description":"The output image billing tier, qima_output_1k or qima_output_2k, determined by the output resolution pixel area","type":"string"},"output_width":{"description":"The width of the final output image in pixels","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"qwen/qwen-image-3.0-pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/qwen/qwen-image-3.0.json b/router-schemas/qwen/qwen-image-3.0.json new file mode 100644 index 000000000..2f81a9b36 --- /dev/null +++ b/router-schemas/qwen/qwen-image-3.0.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"qwen/qwen-image-3.0","description":"The request body Comfy Router accepts for the model \"qwen/qwen-image-3.0\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58be368bd0ed"},"paths":{"/v2/models/qwen/qwen-image-3.0":{"post":{"operationId":"runRouterModel","summary":"Run qwen/qwen-image-3.0 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/QwenMultimodalGenerationResponse"}],"description":"Comfy Router output schema for the Qwen Image 3.0 models: the DashScope multimodal-generation response, forwarded unchanged. The operation is DIRECT-RETURN (`routerresult/classification.go` records `{provider: qwen, endpoint: /api/v1/services/aigc/multimodal-generation/generation}` as `ReturnModeDirect`), so the body a caller receives is this finished document from the one call rather than a task handle. `multimodalGenerationProxy`'s `ModifyResponse` meters it synchronously through `TrackUsage`, `QWEN_TASK_TYPE` registers no task checker, and the rewrite DELETES `X-DashScope-Async` — there is nothing to poll.\nThe generated image is at `output.choices[].message.content[].image`. DashScope documents that URL as valid for 24 HOURS, so download it promptly rather than storing it. Both `choices` and `content` are ARRAYS — `parameters.n` admits up to six images — so a caller reads every element rather than only the first, and that LEAF rather than the `output` container is what says a generation finished: a `content` element carrying only `text` produced no asset.\n`usage` is DashScope's own accounting — `output_image_count` plus the `qima_input_*` / `qima_output_*` billing tiers it derives from the resolved resolution — and NOT the Comfy charge. Router reads those same fields to meter the call, but what a caller is charged is Comfy's rate card. DashScope documents `usage` as returned only on success.\n`code` and `message` are DashScope's failure pair, absent on a success. Neither they nor `output` are constrained here, and `output` is deliberately NOT required: this operation's `Classification` names no `DirectState` reader, so Router treats a 2xx status as the whole success test and never inspects the body (`routerresult/extract.go`). A caller must key completion off the image leaf above rather than off the `200` alone.","example":{"output":{"choices":[{"finish_reason":"stop","message":{"content":[{"image":"https://example.invalid/qwen/generated.png"}],"role":"assistant"}}]},"request_id":"9f2c1b3a-5d4e-4a67-8b90-1c2d3e4f5a6b","usage":{"input_image_count":0,"output_height":512,"output_image_count":1,"output_image_type":"qima_output_1k","output_width":512}}}}}}}}}},"components":{"schemas":{"QwenMultimodalGenerationResponse":{"properties":{"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"output":{"description":"Contains the model generation results","properties":{"choices":{"description":"The list of result options","items":{"properties":{"finish_reason":{"description":"The reason why the task stopped. The value is stop when the task completes normally","type":"string"},"message":{"description":"The message returned by the model","properties":{"content":{"description":"The message content containing the generated image information","items":{"properties":{"image":{"description":"The URL of the generated image in PNG format. The link is valid for 24 hours","type":"string"},"text":{"description":"A textual element returned in place of an image. An element carrying only this field produced NO asset, so a caller keys completion off `image` rather than off the presence of a content element","type":"string"}},"type":"object"},"type":"array"},"role":{"description":"The role of the message. Fixed as assistant","type":"string"}},"type":"object"}},"type":"object"},"type":"array"}},"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"The resource usage of this call. Only returned on success","properties":{"input_image_count":{"description":"The number of input images in the request. Returns 0 for text-to-image","type":"integer"},"input_image_type":{"description":"The input image billing tier, qima_input_1k or qima_input_2k, determined by the output resolution pixel area","type":"string"},"output_height":{"description":"The height of the final output image in pixels","type":"integer"},"output_image_count":{"description":"The actual number of output images returned","type":"integer"},"output_image_type":{"description":"The output image billing tier, qima_output_1k or qima_output_2k, determined by the output resolution pixel area","type":"string"},"output_width":{"description":"The width of the final output image in pixels","type":"integer"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"qwen/qwen-image-3.0","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv2.json b/router-schemas/recraft/recraftv2.json new file mode 100644 index 000000000..2d51e713e --- /dev/null +++ b/router-schemas/recraft/recraftv2.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv2","description":"The request body Comfy Router accepts for the model \"recraft/recraftv2\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv2":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv2 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv2","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv3.json b/router-schemas/recraft/recraftv3.json new file mode 100644 index 000000000..fb6943567 --- /dev/null +++ b/router-schemas/recraft/recraftv3.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv3","description":"The request body Comfy Router accepts for the model \"recraft/recraftv3\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv3":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv3 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv3","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4.json b/router-schemas/recraft/recraftv4.json new file mode 100644 index 000000000..8f6d38d8a --- /dev/null +++ b/router-schemas/recraft/recraftv4.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1.json b/router-schemas/recraft/recraftv4_1.json new file mode 100644 index 000000000..d902b438a --- /dev/null +++ b/router-schemas/recraft/recraftv4_1.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_pro.json b/router-schemas/recraft/recraftv4_1_pro.json new file mode 100644 index 000000000..da20f1c26 --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_pro","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_pro":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_pro_vector.json b/router-schemas/recraft/recraftv4_1_pro_vector.json new file mode 100644 index 000000000..1af4bc97f --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_pro_vector.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_pro_vector","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_pro_vector\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_pro_vector":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_pro_vector synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_pro_vector","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_utility.json b/router-schemas/recraft/recraftv4_1_utility.json new file mode 100644 index 000000000..7dbb77908 --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_utility.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_utility","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_utility\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_utility":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_utility synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_utility","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_utility_pro.json b/router-schemas/recraft/recraftv4_1_utility_pro.json new file mode 100644 index 000000000..8fe5cef55 --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_utility_pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_utility_pro","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_utility_pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_utility_pro":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_utility_pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_utility_pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_utility_pro_vector.json b/router-schemas/recraft/recraftv4_1_utility_pro_vector.json new file mode 100644 index 000000000..4b145b24a --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_utility_pro_vector.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_utility_pro_vector","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_utility_pro_vector\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_utility_pro_vector":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_utility_pro_vector synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_utility_pro_vector","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_utility_vector.json b/router-schemas/recraft/recraftv4_1_utility_vector.json new file mode 100644 index 000000000..eb8c82a39 --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_utility_vector.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_utility_vector","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_utility_vector\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_utility_vector":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_utility_vector synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_utility_vector","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_1_vector.json b/router-schemas/recraft/recraftv4_1_vector.json new file mode 100644 index 000000000..676ec2c4f --- /dev/null +++ b/router-schemas/recraft/recraftv4_1_vector.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_1_vector","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_1_vector\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_1_vector":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_1_vector synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_1_vector","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_pro.json b/router-schemas/recraft/recraftv4_pro.json new file mode 100644 index 000000000..bf0596a01 --- /dev/null +++ b/router-schemas/recraft/recraftv4_pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_pro","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_pro":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_styles.json b/router-schemas/recraft/recraftv4_styles.json new file mode 100644 index 000000000..22931c4ea --- /dev/null +++ b/router-schemas/recraft/recraftv4_styles.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_styles","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_styles\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_styles":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_styles synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_styles","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_styles_pro.json b/router-schemas/recraft/recraftv4_styles_pro.json new file mode 100644 index 000000000..f1cfdb9e7 --- /dev/null +++ b/router-schemas/recraft/recraftv4_styles_pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_styles_pro","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_styles_pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_styles_pro":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_styles_pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_styles_pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_styles_pro_vector.json b/router-schemas/recraft/recraftv4_styles_pro_vector.json new file mode 100644 index 000000000..ab7bbb625 --- /dev/null +++ b/router-schemas/recraft/recraftv4_styles_pro_vector.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_styles_pro_vector","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_styles_pro_vector\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_styles_pro_vector":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_styles_pro_vector synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_styles_pro_vector","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/recraft/recraftv4_styles_vector.json b/router-schemas/recraft/recraftv4_styles_vector.json new file mode 100644 index 000000000..ac43d903d --- /dev/null +++ b/router-schemas/recraft/recraftv4_styles_vector.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"recraft/recraftv4_styles_vector","description":"The request body Comfy Router accepts for the model \"recraft/recraftv4_styles_vector\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"897be418109a"},"paths":{"/v2/models/recraft/recraftv4_styles_vector":{"post":{"operationId":"runRouterModel","summary":"Run recraft/recraftv4_styles_vector synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecraftImageGenerationResponse"}],"description":"Comfy Router output schema for the Recraft generation models: Recraft's own image-generation response, forwarded unchanged. The operation is direct-return (`routerresult/classification.go` classifies `recraft /image_generation` as ReturnModeDirect), so the body a caller receives is this finished document from the one call — there is no task handle and no poll.\nThe generated images are in `data`, each with its URL at `data[].url` and Recraft's identifier at `data[].image_id`. `created` is the generation's Unix timestamp, and `credits` is Recraft's own cost figure for the call — Recraft's number, not the Comfy charge.","example":{"created":1767225600,"credits":1,"data":[{"image_id":"3f7a1b28-5c0d-4e91-8a6f-1b2c3d4e5f60","url":"https://example.invalid/recraft/recraftv3/generated.png"}]}}}}}}}}},"components":{"schemas":{"RecraftImageGenerationResponse":{"description":"Response from the Recraft image generation API.","properties":{"created":{"description":"Unix timestamp when the generation was created","type":"integer"},"credits":{"description":"Number of credits used for the generation","type":"integer"},"data":{"description":"Array of generated image information","items":{"properties":{"image_id":{"description":"Unique identifier for the generated image","type":"string"},"url":{"description":"URL to access the generated image","type":"string"}},"type":"object"},"type":"array"}},"required":["created","credits","data"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"recraft/recraftv4_styles_vector","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/runway/aleph2.json b/router-schemas/runway/aleph2.json new file mode 100644 index 000000000..05cf998b1 --- /dev/null +++ b/router-schemas/runway/aleph2.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"runway/aleph2","description":"The request body Comfy Router accepts for the model \"runway/aleph2\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"e8cc9113416c"},"paths":{"/v2/models/runway/aleph2":{"post":{"operationId":"runRouterModel","summary":"Run runway/aleph2 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RunwayTaskStatusResponse"},{"properties":{"output":{"description":"The finished generation's asset URLs — for `runway/gen4_turbo` and `runway/aleph2` each element is a VIDEO URL. Present once the task terminates successfully; this list IS the result.","items":{"type":"string"},"type":"array"}},"type":"object"}],"description":"Comfy Router output schema for the Runway video models: the terminal `GET /v1/tasks/{id}` document, forwarded unchanged. Both ids are SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: runway, endpoint: /image_to_video}` and `{provider: runway, endpoint: /video_to_video}` as `ReturnModeSubmitPoll`, both sharing the one `GET /proxy/runway/tasks/{task_id}` poll route — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{\"id\": ...}` handle each underlying submit answers with.\nTHE ASSET IS THE `output` LIST ITSELF. `output` is an array of URL STRINGS — not objects with a URL inside — so the leaf a caller reads is `output[]`, and there is no deeper field to key completion off. For these models its element is a GENERATED VIDEO URL. The image model of the same family answers the same document with an image URL in that list; see `RunwayImageRouterOutput`.\n`output` is absent until the task reaches a terminal state, and a `SUCCEEDED` task carrying an empty `output` is a success that produced nothing — `routerpollstate.classifyRunway` (`FamilyRunway`) draws exactly that line, keying success on a non-empty `output` and answering `success_without_output` otherwise. Through Router that distinction is already settled, because such a task is answered as a Comfy Router error rather than with this document, so a `200` here always carries at least one URL.\nThe URLs are served from Runway's own asset host and are NOT permanent — download them promptly rather than storing them. `status` is Runway's own UPPERCASE vocabulary (`PENDING`, `RUNNING`, `THROTTLED`, `SUCCEEDED`, `FAILED`, `CANCELLED`); `SUCCEEDED` is the one terminal success, `FAILED` and `CANCELLED` are terminal failures, and the classifier upper-cases before comparing. `progress` is populated only while `status` is `RUNNING`, so it is not present on the document Router returns.","example":{"createdAt":"2027-01-01T00:00:00Z","id":"5f7c1b28-9a03-4e61-8d2f-1b2c3d4e5f60","output":["https://example.invalid/runway/gen4_turbo/generated.mp4"],"status":"SUCCEEDED"}}}}}}}}},"components":{"schemas":{"RunwayTaskStatusEnum":{"description":"Possible statuses for a Runway task.","enum":["SUCCEEDED","RUNNING","FAILED","PENDING","CANCELLED","THROTTLED"],"type":"string"},"RunwayTaskStatusResponse":{"properties":{"createdAt":{"description":"Task creation timestamp","format":"date-time","type":"string"},"id":{"description":"Task ID","type":"string"},"output":{"description":"Array of the finished task's output asset URLs. This route is shared by every Runway model, so the medium follows the model that was submitted - a video URL for the image_to_video and video_to_video models, an image URL for the text_to_image model.","items":{"type":"string"},"type":"array"},"progress":{"description":"Float value between 0 and 1 representing the progress of the task. Only available if status is RUNNING.","format":"float","maximum":1,"minimum":0,"type":"number"},"status":{"$ref":"#/components/schemas/RunwayTaskStatusEnum"}},"required":["id","status","createdAt"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"runway/aleph2","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/runway/gen4_image.json b/router-schemas/runway/gen4_image.json new file mode 100644 index 000000000..9500a9e8e --- /dev/null +++ b/router-schemas/runway/gen4_image.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"runway/gen4_image","description":"The request body Comfy Router accepts for the model \"runway/gen4_image\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"5bf3c5def9be"},"paths":{"/v2/models/runway/gen4_image":{"post":{"operationId":"runRouterModel","summary":"Run runway/gen4_image synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RunwayTaskStatusResponse"},{"properties":{"output":{"description":"The finished generation's asset URLs — for `runway/gen4_image` each element is an IMAGE URL. Present once the task terminates successfully; this list IS the result.","items":{"type":"string"},"type":"array"}},"type":"object"}],"description":"Comfy Router output schema for the Runway image model: the terminal `GET /v1/tasks/{id}` document, forwarded unchanged. `runway/gen4_image` is SUBMIT-AND-POLL DESPITE being an image generation — `routerresult/classification.go` records `{provider: runway, endpoint: /text_to_image}` as `ReturnModeSubmitPoll`, sharing the one `GET /proxy/runway/tasks/{task_id}` poll route its video siblings use — because Runway's images go through the same task queue its videos do and are collected from the same status route. Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{\"id\": ...}` handle the underlying submit answers with.\nTHE ASSET IS THE `output` LIST ITSELF. `output` is an array of URL STRINGS — not objects with a URL inside — so the leaf a caller reads is `output[]`, and there is no deeper field to key completion off. For this model its element is a GENERATED IMAGE URL. The video models of the same family answer the same document with a video URL in that list; see `RunwayVideoRouterOutput`.\n`output` is absent until the task reaches a terminal state, and a `SUCCEEDED` task carrying an empty `output` is a success that produced nothing — `routerpollstate.classifyRunway` (`FamilyRunway`) draws exactly that line, keying success on a non-empty `output` and answering `success_without_output` otherwise. Through Router that distinction is already settled, because such a task is answered as a Comfy Router error rather than with this document, so a `200` here always carries at least one URL.\nThe URLs are served from Runway's own asset host and are NOT permanent — download them promptly rather than storing them. `status` is Runway's own UPPERCASE vocabulary (`PENDING`, `RUNNING`, `THROTTLED`, `SUCCEEDED`, `FAILED`, `CANCELLED`); `SUCCEEDED` is the one terminal success, `FAILED` and `CANCELLED` are terminal failures, and the classifier upper-cases before comparing. `progress` is populated only while `status` is `RUNNING`, so it is not present on the document Router returns.","example":{"createdAt":"2027-01-01T00:00:00Z","id":"5f7c1b28-9a03-4e61-8d2f-1b2c3d4e5f60","output":["https://example.invalid/runway/gen4_image/generated.png"],"status":"SUCCEEDED"}}}}}}}}},"components":{"schemas":{"RunwayTaskStatusEnum":{"description":"Possible statuses for a Runway task.","enum":["SUCCEEDED","RUNNING","FAILED","PENDING","CANCELLED","THROTTLED"],"type":"string"},"RunwayTaskStatusResponse":{"properties":{"createdAt":{"description":"Task creation timestamp","format":"date-time","type":"string"},"id":{"description":"Task ID","type":"string"},"output":{"description":"Array of the finished task's output asset URLs. This route is shared by every Runway model, so the medium follows the model that was submitted - a video URL for the image_to_video and video_to_video models, an image URL for the text_to_image model.","items":{"type":"string"},"type":"array"},"progress":{"description":"Float value between 0 and 1 representing the progress of the task. Only available if status is RUNNING.","format":"float","maximum":1,"minimum":0,"type":"number"},"status":{"$ref":"#/components/schemas/RunwayTaskStatusEnum"}},"required":["id","status","createdAt"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"runway/gen4_image","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/runway/gen4_turbo.json b/router-schemas/runway/gen4_turbo.json new file mode 100644 index 000000000..6a0289fbf --- /dev/null +++ b/router-schemas/runway/gen4_turbo.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"runway/gen4_turbo","description":"The request body Comfy Router accepts for the model \"runway/gen4_turbo\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"e8cc9113416c"},"paths":{"/v2/models/runway/gen4_turbo":{"post":{"operationId":"runRouterModel","summary":"Run runway/gen4_turbo synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RunwayTaskStatusResponse"},{"properties":{"output":{"description":"The finished generation's asset URLs — for `runway/gen4_turbo` and `runway/aleph2` each element is a VIDEO URL. Present once the task terminates successfully; this list IS the result.","items":{"type":"string"},"type":"array"}},"type":"object"}],"description":"Comfy Router output schema for the Runway video models: the terminal `GET /v1/tasks/{id}` document, forwarded unchanged. Both ids are SUBMIT-AND-POLL — `routerresult/classification.go` records `{provider: runway, endpoint: /image_to_video}` and `{provider: runway, endpoint: /video_to_video}` as `ReturnModeSubmitPoll`, both sharing the one `GET /proxy/runway/tasks/{task_id}` poll route — and Router polls on the caller's behalf, so the body a caller receives is the finished task rather than the `{\"id\": ...}` handle each underlying submit answers with.\nTHE ASSET IS THE `output` LIST ITSELF. `output` is an array of URL STRINGS — not objects with a URL inside — so the leaf a caller reads is `output[]`, and there is no deeper field to key completion off. For these models its element is a GENERATED VIDEO URL. The image model of the same family answers the same document with an image URL in that list; see `RunwayImageRouterOutput`.\n`output` is absent until the task reaches a terminal state, and a `SUCCEEDED` task carrying an empty `output` is a success that produced nothing — `routerpollstate.classifyRunway` (`FamilyRunway`) draws exactly that line, keying success on a non-empty `output` and answering `success_without_output` otherwise. Through Router that distinction is already settled, because such a task is answered as a Comfy Router error rather than with this document, so a `200` here always carries at least one URL.\nThe URLs are served from Runway's own asset host and are NOT permanent — download them promptly rather than storing them. `status` is Runway's own UPPERCASE vocabulary (`PENDING`, `RUNNING`, `THROTTLED`, `SUCCEEDED`, `FAILED`, `CANCELLED`); `SUCCEEDED` is the one terminal success, `FAILED` and `CANCELLED` are terminal failures, and the classifier upper-cases before comparing. `progress` is populated only while `status` is `RUNNING`, so it is not present on the document Router returns.","example":{"createdAt":"2027-01-01T00:00:00Z","id":"5f7c1b28-9a03-4e61-8d2f-1b2c3d4e5f60","output":["https://example.invalid/runway/gen4_turbo/generated.mp4"],"status":"SUCCEEDED"}}}}}}}}},"components":{"schemas":{"RunwayTaskStatusEnum":{"description":"Possible statuses for a Runway task.","enum":["SUCCEEDED","RUNNING","FAILED","PENDING","CANCELLED","THROTTLED"],"type":"string"},"RunwayTaskStatusResponse":{"properties":{"createdAt":{"description":"Task creation timestamp","format":"date-time","type":"string"},"id":{"description":"Task ID","type":"string"},"output":{"description":"Array of the finished task's output asset URLs. This route is shared by every Runway model, so the medium follows the model that was submitted - a video URL for the image_to_video and video_to_video models, an image URL for the text_to_image model.","items":{"type":"string"},"type":"array"},"progress":{"description":"Float value between 0 and 1 representing the progress of the task. Only available if status is RUNNING.","format":"float","maximum":1,"minimum":0,"type":"number"},"status":{"$ref":"#/components/schemas/RunwayTaskStatusEnum"}},"required":["id","status","createdAt"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"runway/gen4_turbo","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/veo/veo-2.0-generate-001.json b/router-schemas/veo/veo-2.0-generate-001.json new file mode 100644 index 000000000..2d54597e4 --- /dev/null +++ b/router-schemas/veo/veo-2.0-generate-001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"veo/veo-2.0-generate-001","description":"The request body Comfy Router accepts for the model \"veo/veo-2.0-generate-001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1308ed4a14ef"},"paths":{"/v2/models/veo/veo-2.0-generate-001":{"post":{"operationId":"runRouterModel","summary":"Run veo/veo-2.0-generate-001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/VeoGenVidPollResponse"}],"description":"Comfy Router output schema for the Veo models: the terminal Vertex AI long-running operation document, forwarded unchanged. Veo is submit-and-poll, and Router polls on the caller's behalf, so the body a caller receives is the finished operation (`done: true`) rather than the operation handle the submit returns.\nThe generated videos are in `response.videos`. Each carries either `bytesBase64Encoded` (the video inline, which is what Comfy's proxy configuration produces) or `gcsUri`, and `mimeType` names the container. `response.raiMediaFilteredCount` and `response.raiMediaFilteredReasons` report videos Vertex's responsible-AI filters removed, so a `done: true` operation can legitimately carry fewer videos than were requested — or none at all.\nThe example's `bytesBase64Encoded` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e`, spelled as valid base64 for the same reason the Gemini image example is so the two placeholders read alike.\nONE MEMBER IS AUTHORED AHEAD OF ENABLEMENT. `veo/veo-2.0-generate-001` resolves to the literal `POST /proxy/veo/generate` operation rather than the collapsed `/:modelId/generate` one, and `routerresult/classification.go` records a return mode only for the latter — so `runRouterModel` refuses it before dispatch until that row lands. The other five members are classified and run today.","example":{"done":true,"name":"projects/example-project/locations/us-central1/publishers/google/models/veo-3.1-fast-generate-001/operations/1a2b3c4d","response":{"@type":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","raiMediaFilteredCount":0,"videos":[{"bytesBase64Encoded":"PGJhc2U2ND4=","mimeType":"video/mp4"}]}}}}}}}}}},"components":{"schemas":{"VeoGenVidPollResponse":{"description":"Response from polling a Veo video generation operation","properties":{"done":{"description":"Whether the operation has completed","type":"boolean"},"error":{"description":"Error details, present if the operation failed","properties":{"code":{"description":"gRPC error code","type":"integer"},"message":{"description":"Error message","type":"string"}},"type":"object"},"name":{"description":"Operation resource name","type":"string"},"response":{"description":"The prediction response, present when done is true","properties":{"@type":{"example":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","type":"string"},"raiMediaFilteredCount":{"description":"Number of videos filtered by responsible AI policies","type":"integer"},"raiMediaFilteredReasons":{"description":"Reasons why videos were filtered by responsible AI policies","items":{"type":"string"},"type":"array"},"videos":{"items":{"properties":{"bytesBase64Encoded":{"description":"Base64-encoded video content","type":"string"},"gcsUri":{"description":"Cloud Storage URI of the generated video","type":"string"},"mimeType":{"description":"Video MIME type (video/mp4)","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"veo/veo-2.0-generate-001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/veo/veo-3.0-fast-generate-001.json b/router-schemas/veo/veo-3.0-fast-generate-001.json new file mode 100644 index 000000000..d923e8e86 --- /dev/null +++ b/router-schemas/veo/veo-3.0-fast-generate-001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"veo/veo-3.0-fast-generate-001","description":"The request body Comfy Router accepts for the model \"veo/veo-3.0-fast-generate-001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1308ed4a14ef"},"paths":{"/v2/models/veo/veo-3.0-fast-generate-001":{"post":{"operationId":"runRouterModel","summary":"Run veo/veo-3.0-fast-generate-001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/VeoGenVidPollResponse"}],"description":"Comfy Router output schema for the Veo models: the terminal Vertex AI long-running operation document, forwarded unchanged. Veo is submit-and-poll, and Router polls on the caller's behalf, so the body a caller receives is the finished operation (`done: true`) rather than the operation handle the submit returns.\nThe generated videos are in `response.videos`. Each carries either `bytesBase64Encoded` (the video inline, which is what Comfy's proxy configuration produces) or `gcsUri`, and `mimeType` names the container. `response.raiMediaFilteredCount` and `response.raiMediaFilteredReasons` report videos Vertex's responsible-AI filters removed, so a `done: true` operation can legitimately carry fewer videos than were requested — or none at all.\nThe example's `bytesBase64Encoded` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e`, spelled as valid base64 for the same reason the Gemini image example is so the two placeholders read alike.\nONE MEMBER IS AUTHORED AHEAD OF ENABLEMENT. `veo/veo-2.0-generate-001` resolves to the literal `POST /proxy/veo/generate` operation rather than the collapsed `/:modelId/generate` one, and `routerresult/classification.go` records a return mode only for the latter — so `runRouterModel` refuses it before dispatch until that row lands. The other five members are classified and run today.","example":{"done":true,"name":"projects/example-project/locations/us-central1/publishers/google/models/veo-3.1-fast-generate-001/operations/1a2b3c4d","response":{"@type":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","raiMediaFilteredCount":0,"videos":[{"bytesBase64Encoded":"PGJhc2U2ND4=","mimeType":"video/mp4"}]}}}}}}}}}},"components":{"schemas":{"VeoGenVidPollResponse":{"description":"Response from polling a Veo video generation operation","properties":{"done":{"description":"Whether the operation has completed","type":"boolean"},"error":{"description":"Error details, present if the operation failed","properties":{"code":{"description":"gRPC error code","type":"integer"},"message":{"description":"Error message","type":"string"}},"type":"object"},"name":{"description":"Operation resource name","type":"string"},"response":{"description":"The prediction response, present when done is true","properties":{"@type":{"example":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","type":"string"},"raiMediaFilteredCount":{"description":"Number of videos filtered by responsible AI policies","type":"integer"},"raiMediaFilteredReasons":{"description":"Reasons why videos were filtered by responsible AI policies","items":{"type":"string"},"type":"array"},"videos":{"items":{"properties":{"bytesBase64Encoded":{"description":"Base64-encoded video content","type":"string"},"gcsUri":{"description":"Cloud Storage URI of the generated video","type":"string"},"mimeType":{"description":"Video MIME type (video/mp4)","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"veo/veo-3.0-fast-generate-001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/veo/veo-3.0-generate-001.json b/router-schemas/veo/veo-3.0-generate-001.json new file mode 100644 index 000000000..b344741e0 --- /dev/null +++ b/router-schemas/veo/veo-3.0-generate-001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"veo/veo-3.0-generate-001","description":"The request body Comfy Router accepts for the model \"veo/veo-3.0-generate-001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1308ed4a14ef"},"paths":{"/v2/models/veo/veo-3.0-generate-001":{"post":{"operationId":"runRouterModel","summary":"Run veo/veo-3.0-generate-001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/VeoGenVidPollResponse"}],"description":"Comfy Router output schema for the Veo models: the terminal Vertex AI long-running operation document, forwarded unchanged. Veo is submit-and-poll, and Router polls on the caller's behalf, so the body a caller receives is the finished operation (`done: true`) rather than the operation handle the submit returns.\nThe generated videos are in `response.videos`. Each carries either `bytesBase64Encoded` (the video inline, which is what Comfy's proxy configuration produces) or `gcsUri`, and `mimeType` names the container. `response.raiMediaFilteredCount` and `response.raiMediaFilteredReasons` report videos Vertex's responsible-AI filters removed, so a `done: true` operation can legitimately carry fewer videos than were requested — or none at all.\nThe example's `bytesBase64Encoded` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e`, spelled as valid base64 for the same reason the Gemini image example is so the two placeholders read alike.\nONE MEMBER IS AUTHORED AHEAD OF ENABLEMENT. `veo/veo-2.0-generate-001` resolves to the literal `POST /proxy/veo/generate` operation rather than the collapsed `/:modelId/generate` one, and `routerresult/classification.go` records a return mode only for the latter — so `runRouterModel` refuses it before dispatch until that row lands. The other five members are classified and run today.","example":{"done":true,"name":"projects/example-project/locations/us-central1/publishers/google/models/veo-3.1-fast-generate-001/operations/1a2b3c4d","response":{"@type":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","raiMediaFilteredCount":0,"videos":[{"bytesBase64Encoded":"PGJhc2U2ND4=","mimeType":"video/mp4"}]}}}}}}}}}},"components":{"schemas":{"VeoGenVidPollResponse":{"description":"Response from polling a Veo video generation operation","properties":{"done":{"description":"Whether the operation has completed","type":"boolean"},"error":{"description":"Error details, present if the operation failed","properties":{"code":{"description":"gRPC error code","type":"integer"},"message":{"description":"Error message","type":"string"}},"type":"object"},"name":{"description":"Operation resource name","type":"string"},"response":{"description":"The prediction response, present when done is true","properties":{"@type":{"example":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","type":"string"},"raiMediaFilteredCount":{"description":"Number of videos filtered by responsible AI policies","type":"integer"},"raiMediaFilteredReasons":{"description":"Reasons why videos were filtered by responsible AI policies","items":{"type":"string"},"type":"array"},"videos":{"items":{"properties":{"bytesBase64Encoded":{"description":"Base64-encoded video content","type":"string"},"gcsUri":{"description":"Cloud Storage URI of the generated video","type":"string"},"mimeType":{"description":"Video MIME type (video/mp4)","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"veo/veo-3.0-generate-001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/veo/veo-3.1-fast-generate-001.json b/router-schemas/veo/veo-3.1-fast-generate-001.json new file mode 100644 index 000000000..a7dd493ab --- /dev/null +++ b/router-schemas/veo/veo-3.1-fast-generate-001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"veo/veo-3.1-fast-generate-001","description":"The request body Comfy Router accepts for the model \"veo/veo-3.1-fast-generate-001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1308ed4a14ef"},"paths":{"/v2/models/veo/veo-3.1-fast-generate-001":{"post":{"operationId":"runRouterModel","summary":"Run veo/veo-3.1-fast-generate-001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/VeoGenVidPollResponse"}],"description":"Comfy Router output schema for the Veo models: the terminal Vertex AI long-running operation document, forwarded unchanged. Veo is submit-and-poll, and Router polls on the caller's behalf, so the body a caller receives is the finished operation (`done: true`) rather than the operation handle the submit returns.\nThe generated videos are in `response.videos`. Each carries either `bytesBase64Encoded` (the video inline, which is what Comfy's proxy configuration produces) or `gcsUri`, and `mimeType` names the container. `response.raiMediaFilteredCount` and `response.raiMediaFilteredReasons` report videos Vertex's responsible-AI filters removed, so a `done: true` operation can legitimately carry fewer videos than were requested — or none at all.\nThe example's `bytesBase64Encoded` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e`, spelled as valid base64 for the same reason the Gemini image example is so the two placeholders read alike.\nONE MEMBER IS AUTHORED AHEAD OF ENABLEMENT. `veo/veo-2.0-generate-001` resolves to the literal `POST /proxy/veo/generate` operation rather than the collapsed `/:modelId/generate` one, and `routerresult/classification.go` records a return mode only for the latter — so `runRouterModel` refuses it before dispatch until that row lands. The other five members are classified and run today.","example":{"done":true,"name":"projects/example-project/locations/us-central1/publishers/google/models/veo-3.1-fast-generate-001/operations/1a2b3c4d","response":{"@type":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","raiMediaFilteredCount":0,"videos":[{"bytesBase64Encoded":"PGJhc2U2ND4=","mimeType":"video/mp4"}]}}}}}}}}}},"components":{"schemas":{"VeoGenVidPollResponse":{"description":"Response from polling a Veo video generation operation","properties":{"done":{"description":"Whether the operation has completed","type":"boolean"},"error":{"description":"Error details, present if the operation failed","properties":{"code":{"description":"gRPC error code","type":"integer"},"message":{"description":"Error message","type":"string"}},"type":"object"},"name":{"description":"Operation resource name","type":"string"},"response":{"description":"The prediction response, present when done is true","properties":{"@type":{"example":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","type":"string"},"raiMediaFilteredCount":{"description":"Number of videos filtered by responsible AI policies","type":"integer"},"raiMediaFilteredReasons":{"description":"Reasons why videos were filtered by responsible AI policies","items":{"type":"string"},"type":"array"},"videos":{"items":{"properties":{"bytesBase64Encoded":{"description":"Base64-encoded video content","type":"string"},"gcsUri":{"description":"Cloud Storage URI of the generated video","type":"string"},"mimeType":{"description":"Video MIME type (video/mp4)","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"veo/veo-3.1-fast-generate-001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/veo/veo-3.1-generate-001.json b/router-schemas/veo/veo-3.1-generate-001.json new file mode 100644 index 000000000..618a5b3a9 --- /dev/null +++ b/router-schemas/veo/veo-3.1-generate-001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"veo/veo-3.1-generate-001","description":"The request body Comfy Router accepts for the model \"veo/veo-3.1-generate-001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1308ed4a14ef"},"paths":{"/v2/models/veo/veo-3.1-generate-001":{"post":{"operationId":"runRouterModel","summary":"Run veo/veo-3.1-generate-001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/VeoGenVidPollResponse"}],"description":"Comfy Router output schema for the Veo models: the terminal Vertex AI long-running operation document, forwarded unchanged. Veo is submit-and-poll, and Router polls on the caller's behalf, so the body a caller receives is the finished operation (`done: true`) rather than the operation handle the submit returns.\nThe generated videos are in `response.videos`. Each carries either `bytesBase64Encoded` (the video inline, which is what Comfy's proxy configuration produces) or `gcsUri`, and `mimeType` names the container. `response.raiMediaFilteredCount` and `response.raiMediaFilteredReasons` report videos Vertex's responsible-AI filters removed, so a `done: true` operation can legitimately carry fewer videos than were requested — or none at all.\nThe example's `bytesBase64Encoded` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e`, spelled as valid base64 for the same reason the Gemini image example is so the two placeholders read alike.\nONE MEMBER IS AUTHORED AHEAD OF ENABLEMENT. `veo/veo-2.0-generate-001` resolves to the literal `POST /proxy/veo/generate` operation rather than the collapsed `/:modelId/generate` one, and `routerresult/classification.go` records a return mode only for the latter — so `runRouterModel` refuses it before dispatch until that row lands. The other five members are classified and run today.","example":{"done":true,"name":"projects/example-project/locations/us-central1/publishers/google/models/veo-3.1-fast-generate-001/operations/1a2b3c4d","response":{"@type":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","raiMediaFilteredCount":0,"videos":[{"bytesBase64Encoded":"PGJhc2U2ND4=","mimeType":"video/mp4"}]}}}}}}}}}},"components":{"schemas":{"VeoGenVidPollResponse":{"description":"Response from polling a Veo video generation operation","properties":{"done":{"description":"Whether the operation has completed","type":"boolean"},"error":{"description":"Error details, present if the operation failed","properties":{"code":{"description":"gRPC error code","type":"integer"},"message":{"description":"Error message","type":"string"}},"type":"object"},"name":{"description":"Operation resource name","type":"string"},"response":{"description":"The prediction response, present when done is true","properties":{"@type":{"example":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","type":"string"},"raiMediaFilteredCount":{"description":"Number of videos filtered by responsible AI policies","type":"integer"},"raiMediaFilteredReasons":{"description":"Reasons why videos were filtered by responsible AI policies","items":{"type":"string"},"type":"array"},"videos":{"items":{"properties":{"bytesBase64Encoded":{"description":"Base64-encoded video content","type":"string"},"gcsUri":{"description":"Cloud Storage URI of the generated video","type":"string"},"mimeType":{"description":"Video MIME type (video/mp4)","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"veo/veo-3.1-generate-001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/veo/veo-3.1-lite-generate-001.json b/router-schemas/veo/veo-3.1-lite-generate-001.json new file mode 100644 index 000000000..ccffb9db8 --- /dev/null +++ b/router-schemas/veo/veo-3.1-lite-generate-001.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"veo/veo-3.1-lite-generate-001","description":"The request body Comfy Router accepts for the model \"veo/veo-3.1-lite-generate-001\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"1308ed4a14ef"},"paths":{"/v2/models/veo/veo-3.1-lite-generate-001":{"post":{"operationId":"runRouterModel","summary":"Run veo/veo-3.1-lite-generate-001 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/VeoGenVidPollResponse"}],"description":"Comfy Router output schema for the Veo models: the terminal Vertex AI long-running operation document, forwarded unchanged. Veo is submit-and-poll, and Router polls on the caller's behalf, so the body a caller receives is the finished operation (`done: true`) rather than the operation handle the submit returns.\nThe generated videos are in `response.videos`. Each carries either `bytesBase64Encoded` (the video inline, which is what Comfy's proxy configuration produces) or `gcsUri`, and `mimeType` names the container. `response.raiMediaFilteredCount` and `response.raiMediaFilteredReasons` report videos Vertex's responsible-AI filters removed, so a `done: true` operation can legitimately carry fewer videos than were requested — or none at all.\nThe example's `bytesBase64Encoded` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e`, spelled as valid base64 for the same reason the Gemini image example is so the two placeholders read alike.\nONE MEMBER IS AUTHORED AHEAD OF ENABLEMENT. `veo/veo-2.0-generate-001` resolves to the literal `POST /proxy/veo/generate` operation rather than the collapsed `/:modelId/generate` one, and `routerresult/classification.go` records a return mode only for the latter — so `runRouterModel` refuses it before dispatch until that row lands. The other five members are classified and run today.","example":{"done":true,"name":"projects/example-project/locations/us-central1/publishers/google/models/veo-3.1-fast-generate-001/operations/1a2b3c4d","response":{"@type":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","raiMediaFilteredCount":0,"videos":[{"bytesBase64Encoded":"PGJhc2U2ND4=","mimeType":"video/mp4"}]}}}}}}}}}},"components":{"schemas":{"VeoGenVidPollResponse":{"description":"Response from polling a Veo video generation operation","properties":{"done":{"description":"Whether the operation has completed","type":"boolean"},"error":{"description":"Error details, present if the operation failed","properties":{"code":{"description":"gRPC error code","type":"integer"},"message":{"description":"Error message","type":"string"}},"type":"object"},"name":{"description":"Operation resource name","type":"string"},"response":{"description":"The prediction response, present when done is true","properties":{"@type":{"example":"type.googleapis.com/cloud.ai.large_models.vision.GenerateVideoResponse","type":"string"},"raiMediaFilteredCount":{"description":"Number of videos filtered by responsible AI policies","type":"integer"},"raiMediaFilteredReasons":{"description":"Reasons why videos were filtered by responsible AI policies","items":{"type":"string"},"type":"array"},"videos":{"items":{"properties":{"bytesBase64Encoded":{"description":"Base64-encoded video content","type":"string"},"gcsUri":{"description":"Cloud Storage URI of the generated video","type":"string"},"mimeType":{"description":"Video MIME type (video/mp4)","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"veo/veo-3.1-lite-generate-001","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-2.5-flash-image.json b/router-schemas/vertexai/gemini-2.5-flash-image.json new file mode 100644 index 000000000..8fa73e47d --- /dev/null +++ b/router-schemas/vertexai/gemini-2.5-flash-image.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-2.5-flash-image","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-2.5-flash-image\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"646da6f6d7a9"},"paths":{"/v2/models/vertexai/gemini-2.5-flash-image":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-2.5-flash-image synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the image-returning Gemini models: the same Vertex AI `generateContent` response the text models return, forwarded unchanged, but the generated image arrives as base64 bytes at `candidates[0].content.parts[0].inlineData.data`, with its media type at `inlineData.mimeType`. A response may carry a text part alongside the image part, so select the part by the field you need rather than by index.\n`uploadImagesToStorage` CHANGES THIS SHAPE. When the request sets it, the proxy uploads each generated image to Comfy storage and replaces the part's `inlineData` with `fileData`, carrying a Comfy-signed URL at `fileData.fileUri` and the media type at `fileData.mimeType`; `inlineData` is then absent for that part. The URL expires 24 hours after it is minted, so download the image rather than storing the link. An image whose upload fails is left as `inlineData`, so ONE response can mix both shapes - branch on which key is present rather than assuming either.\nRouter does not re-host a partner link for this family: Gemini returns the image as bytes and no URL of its own, so `uploadImagesToStorage` is an upload of those bytes that the caller asks for, not a rewrite of somebody else's link. A request that leaves the field unset is answered as base64 throughout.\nAs for the text models, `promptFeedback` and `usageMetadata` are present on a safety-blocked response that generated nothing; `candidates` is the field to branch on.\nThe example's `inlineData.data` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e` - a real image would be megabytes, and a real provider response does not belong in a published spec. It is spelled as valid base64 rather than as a bare `\u003cbase64\u003e` marker because `GeminiInlineData.data` declares `format: byte`, which this document's own example validation enforces.","example":{"candidates":[{"content":{"parts":[{"inlineData":{"data":"PGJhc2U2ND4=","mimeType":"image/png"}}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash-image","responseId":"7c6b5a49-3827-1605-f4e3-d2c1b0a99887","usageMetadata":{"candidatesTokenCount":1290,"promptTokenCount":11,"totalTokenCount":1301}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-2.5-flash-image","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-2.5-flash.json b/router-schemas/vertexai/gemini-2.5-flash.json new file mode 100644 index 000000000..8e7bc2444 --- /dev/null +++ b/router-schemas/vertexai/gemini-2.5-flash.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-2.5-flash","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-2.5-flash\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f86b13873c94"},"paths":{"/v2/models/vertexai/gemini-2.5-flash":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-2.5-flash synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the text-returning Gemini models: Vertex AI's `generateContent` response, forwarded unchanged, with the generated text at `candidates[0].content.parts[0].text`.\n`candidates` is where a completed generation lands and is the field to branch on. `promptFeedback` and `usageMetadata` are BOTH present on a safety-blocked response that carries no candidate at all, so neither is evidence that anything was generated.","example":{"candidates":[{"content":{"parts":[{"text":"A lighthouse stands at the edge of the harbour, its lamp still turning as the sun comes up."}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash","responseId":"0d1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b","usageMetadata":{"candidatesTokenCount":21,"promptTokenCount":12,"totalTokenCount":33}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-2.5-flash","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-2.5-pro.json b/router-schemas/vertexai/gemini-2.5-pro.json new file mode 100644 index 000000000..a69a22966 --- /dev/null +++ b/router-schemas/vertexai/gemini-2.5-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-2.5-pro","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-2.5-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f86b13873c94"},"paths":{"/v2/models/vertexai/gemini-2.5-pro":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-2.5-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the text-returning Gemini models: Vertex AI's `generateContent` response, forwarded unchanged, with the generated text at `candidates[0].content.parts[0].text`.\n`candidates` is where a completed generation lands and is the field to branch on. `promptFeedback` and `usageMetadata` are BOTH present on a safety-blocked response that carries no candidate at all, so neither is evidence that anything was generated.","example":{"candidates":[{"content":{"parts":[{"text":"A lighthouse stands at the edge of the harbour, its lamp still turning as the sun comes up."}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash","responseId":"0d1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b","usageMetadata":{"candidatesTokenCount":21,"promptTokenCount":12,"totalTokenCount":33}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-2.5-pro","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3-pro-image.json b/router-schemas/vertexai/gemini-3-pro-image.json new file mode 100644 index 000000000..5a0accf32 --- /dev/null +++ b/router-schemas/vertexai/gemini-3-pro-image.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3-pro-image","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3-pro-image\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"c2b9ea9e5dd9"},"paths":{"/v2/models/vertexai/gemini-3-pro-image":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3-pro-image synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the image-returning Gemini models: the same Vertex AI `generateContent` response the text models return, forwarded unchanged, but the generated image arrives as base64 bytes at `candidates[0].content.parts[0].inlineData.data`, with its media type at `inlineData.mimeType`. A response may carry a text part alongside the image part, so select the part by the field you need rather than by index.\n`uploadImagesToStorage` CHANGES THIS SHAPE. When the request sets it, the proxy uploads each generated image to Comfy storage and replaces the part's `inlineData` with `fileData`, carrying a Comfy-signed URL at `fileData.fileUri` and the media type at `fileData.mimeType`; `inlineData` is then absent for that part. The URL expires 24 hours after it is minted, so download the image rather than storing the link. An image whose upload fails is left as `inlineData`, so ONE response can mix both shapes - branch on which key is present rather than assuming either.\nRouter does not re-host a partner link for this family: Gemini returns the image as bytes and no URL of its own, so `uploadImagesToStorage` is an upload of those bytes that the caller asks for, not a rewrite of somebody else's link. A request that leaves the field unset is answered as base64 throughout.\nAs for the text models, `promptFeedback` and `usageMetadata` are present on a safety-blocked response that generated nothing; `candidates` is the field to branch on.\nThe example's `inlineData.data` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e` - a real image would be megabytes, and a real provider response does not belong in a published spec. It is spelled as valid base64 rather than as a bare `\u003cbase64\u003e` marker because `GeminiInlineData.data` declares `format: byte`, which this document's own example validation enforces.","example":{"candidates":[{"content":{"parts":[{"inlineData":{"data":"PGJhc2U2ND4=","mimeType":"image/png"}}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash-image","responseId":"7c6b5a49-3827-1605-f4e3-d2c1b0a99887","usageMetadata":{"candidatesTokenCount":1290,"promptTokenCount":11,"totalTokenCount":1301}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3-pro-image","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3.1-flash-image.json b/router-schemas/vertexai/gemini-3.1-flash-image.json new file mode 100644 index 000000000..3c65d04ee --- /dev/null +++ b/router-schemas/vertexai/gemini-3.1-flash-image.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3.1-flash-image","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3.1-flash-image\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"c2b9ea9e5dd9"},"paths":{"/v2/models/vertexai/gemini-3.1-flash-image":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3.1-flash-image synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the image-returning Gemini models: the same Vertex AI `generateContent` response the text models return, forwarded unchanged, but the generated image arrives as base64 bytes at `candidates[0].content.parts[0].inlineData.data`, with its media type at `inlineData.mimeType`. A response may carry a text part alongside the image part, so select the part by the field you need rather than by index.\n`uploadImagesToStorage` CHANGES THIS SHAPE. When the request sets it, the proxy uploads each generated image to Comfy storage and replaces the part's `inlineData` with `fileData`, carrying a Comfy-signed URL at `fileData.fileUri` and the media type at `fileData.mimeType`; `inlineData` is then absent for that part. The URL expires 24 hours after it is minted, so download the image rather than storing the link. An image whose upload fails is left as `inlineData`, so ONE response can mix both shapes - branch on which key is present rather than assuming either.\nRouter does not re-host a partner link for this family: Gemini returns the image as bytes and no URL of its own, so `uploadImagesToStorage` is an upload of those bytes that the caller asks for, not a rewrite of somebody else's link. A request that leaves the field unset is answered as base64 throughout.\nAs for the text models, `promptFeedback` and `usageMetadata` are present on a safety-blocked response that generated nothing; `candidates` is the field to branch on.\nThe example's `inlineData.data` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e` - a real image would be megabytes, and a real provider response does not belong in a published spec. It is spelled as valid base64 rather than as a bare `\u003cbase64\u003e` marker because `GeminiInlineData.data` declares `format: byte`, which this document's own example validation enforces.","example":{"candidates":[{"content":{"parts":[{"inlineData":{"data":"PGJhc2U2ND4=","mimeType":"image/png"}}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash-image","responseId":"7c6b5a49-3827-1605-f4e3-d2c1b0a99887","usageMetadata":{"candidatesTokenCount":1290,"promptTokenCount":11,"totalTokenCount":1301}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3.1-flash-image","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3.1-flash-lite-image.json b/router-schemas/vertexai/gemini-3.1-flash-lite-image.json new file mode 100644 index 000000000..0dae8520f --- /dev/null +++ b/router-schemas/vertexai/gemini-3.1-flash-lite-image.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3.1-flash-lite-image","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3.1-flash-lite-image\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"c2b9ea9e5dd9"},"paths":{"/v2/models/vertexai/gemini-3.1-flash-lite-image":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3.1-flash-lite-image synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the image-returning Gemini models: the same Vertex AI `generateContent` response the text models return, forwarded unchanged, but the generated image arrives as base64 bytes at `candidates[0].content.parts[0].inlineData.data`, with its media type at `inlineData.mimeType`. A response may carry a text part alongside the image part, so select the part by the field you need rather than by index.\n`uploadImagesToStorage` CHANGES THIS SHAPE. When the request sets it, the proxy uploads each generated image to Comfy storage and replaces the part's `inlineData` with `fileData`, carrying a Comfy-signed URL at `fileData.fileUri` and the media type at `fileData.mimeType`; `inlineData` is then absent for that part. The URL expires 24 hours after it is minted, so download the image rather than storing the link. An image whose upload fails is left as `inlineData`, so ONE response can mix both shapes - branch on which key is present rather than assuming either.\nRouter does not re-host a partner link for this family: Gemini returns the image as bytes and no URL of its own, so `uploadImagesToStorage` is an upload of those bytes that the caller asks for, not a rewrite of somebody else's link. A request that leaves the field unset is answered as base64 throughout.\nAs for the text models, `promptFeedback` and `usageMetadata` are present on a safety-blocked response that generated nothing; `candidates` is the field to branch on.\nThe example's `inlineData.data` is the PLACEHOLDER `PGJhc2U2ND4=`, which decodes to the literal text `\u003cbase64\u003e` - a real image would be megabytes, and a real provider response does not belong in a published spec. It is spelled as valid base64 rather than as a bare `\u003cbase64\u003e` marker because `GeminiInlineData.data` declares `format: byte`, which this document's own example validation enforces.","example":{"candidates":[{"content":{"parts":[{"inlineData":{"data":"PGJhc2U2ND4=","mimeType":"image/png"}}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash-image","responseId":"7c6b5a49-3827-1605-f4e3-d2c1b0a99887","usageMetadata":{"candidatesTokenCount":1290,"promptTokenCount":11,"totalTokenCount":1301}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3.1-flash-lite-image","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3.1-flash-lite.json b/router-schemas/vertexai/gemini-3.1-flash-lite.json new file mode 100644 index 000000000..b57cac7d2 --- /dev/null +++ b/router-schemas/vertexai/gemini-3.1-flash-lite.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3.1-flash-lite","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3.1-flash-lite\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"63d290559279"},"paths":{"/v2/models/vertexai/gemini-3.1-flash-lite":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3.1-flash-lite synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the text-returning Gemini models: Vertex AI's `generateContent` response, forwarded unchanged, with the generated text at `candidates[0].content.parts[0].text`.\n`candidates` is where a completed generation lands and is the field to branch on. `promptFeedback` and `usageMetadata` are BOTH present on a safety-blocked response that carries no candidate at all, so neither is evidence that anything was generated.","example":{"candidates":[{"content":{"parts":[{"text":"A lighthouse stands at the edge of the harbour, its lamp still turning as the sun comes up."}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash","responseId":"0d1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b","usageMetadata":{"candidatesTokenCount":21,"promptTokenCount":12,"totalTokenCount":33}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3.1-flash-lite","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3.1-pro-preview.json b/router-schemas/vertexai/gemini-3.1-pro-preview.json new file mode 100644 index 000000000..d542fe8ab --- /dev/null +++ b/router-schemas/vertexai/gemini-3.1-pro-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3.1-pro-preview","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3.1-pro-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f86b13873c94"},"paths":{"/v2/models/vertexai/gemini-3.1-pro-preview":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3.1-pro-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the text-returning Gemini models: Vertex AI's `generateContent` response, forwarded unchanged, with the generated text at `candidates[0].content.parts[0].text`.\n`candidates` is where a completed generation lands and is the field to branch on. `promptFeedback` and `usageMetadata` are BOTH present on a safety-blocked response that carries no candidate at all, so neither is evidence that anything was generated.","example":{"candidates":[{"content":{"parts":[{"text":"A lighthouse stands at the edge of the harbour, its lamp still turning as the sun comes up."}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash","responseId":"0d1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b","usageMetadata":{"candidatesTokenCount":21,"promptTokenCount":12,"totalTokenCount":33}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3.1-pro-preview","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3.5-flash.json b/router-schemas/vertexai/gemini-3.5-flash.json new file mode 100644 index 000000000..3cd9df40a --- /dev/null +++ b/router-schemas/vertexai/gemini-3.5-flash.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3.5-flash","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3.5-flash\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"f86b13873c94"},"paths":{"/v2/models/vertexai/gemini-3.5-flash":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3.5-flash synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"example":{"contents":[{"parts":[{"text":"Describe a robot learning to paint, in two sentences."}],"role":"user"}]},"properties":{"contents":{"items":{"$ref":"#/components/schemas/GeminiContent"},"type":"array"},"generationConfig":{"$ref":"#/components/schemas/GeminiGenerationConfig"},"safetySettings":{"items":{"$ref":"#/components/schemas/GeminiSafetySetting"},"type":"array"},"systemInstruction":{"$ref":"#/components/schemas/GeminiSystemInstructionContent"},"tools":{"items":{"$ref":"#/components/schemas/GeminiTool"},"type":"array"},"uploadImagesToStorage":{"description":"If true, generated images will be uploaded to cloud storage and returned as signed URLs instead of inline base64 data. The URLs expire after 24 hours.","type":"boolean"},"videoMetadata":{"$ref":"#/components/schemas/GeminiVideoMetadata"}},"required":["contents"],"type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the text-returning Gemini models: Vertex AI's `generateContent` response, forwarded unchanged, with the generated text at `candidates[0].content.parts[0].text`.\n`candidates` is where a completed generation lands and is the field to branch on. `promptFeedback` and `usageMetadata` are BOTH present on a safety-blocked response that carries no candidate at all, so neither is evidence that anything was generated.","example":{"candidates":[{"content":{"parts":[{"text":"A lighthouse stands at the edge of the harbour, its lamp still turning as the sun comes up."}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash","responseId":"0d1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b","usageMetadata":{"candidatesTokenCount":21,"promptTokenCount":12,"totalTokenCount":33}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiFunctionDeclaration":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{"description":"JSON schema for the function parameters","type":"object"}},"required":["name","parameters"],"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiGenerationConfig":{"properties":{"imageConfig":{"description":"Configuration for image generation","properties":{"aspectRatio":{"description":"Aspect ratio for generated images","type":"string"},"imageOutputOptions":{"description":"Optional. The image output format for generated images.","properties":{"compressionQuality":{"description":"Optional. The compression quality of the output image.","type":"integer"},"mimeType":{"description":"Optional. The image format that the output should be saved as.","type":"string"}},"type":"object"},"imageSize":{"description":"Optional. Specifies the size of generated images. Supported values are 1K, 2K, 4K. If not specified, the model will use default value 1K.","type":"string"}},"type":"object"},"maxOutputTokens":{"description":"Maximum number of tokens that can be generated in the response. A token is approximately 4 characters. 100 tokens correspond to roughly 60-80 words.\n","example":2048,"maximum":8192,"minimum":16,"type":"integer"},"responseModalities":{"items":{"enum":["TEXT","IMAGE"],"type":"string"},"type":"array"},"seed":{"description":"When seed is fixed to a specific value, the model makes a best effort to provide the same response for repeated requests. Deterministic output isn't guaranteed. Also, changing the model or parameter settings, such as the temperature, can cause variations in the response even when you use the same seed value. By default, a random seed value is used. Available for the following models:, gemini-2.5-flash, gemini-2.5-pro, gemini-2.5-flash-preview-04-1, gemini-2.5-pro-preview-05-0, gemini-2.0-flash-lite-00, gemini-2.0-flash-001\n","example":343940597,"type":"integer"},"stopSequences":{"items":{"type":"string"},"type":"array"},"temperature":{"default":1,"description":"The temperature is used for sampling during response generation, which occurs when topP and topK are applied. Temperature controls the degree of randomness in token selection. Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible. If the model returns a response that's too generic, too short, or the model gives a fallback response, try increasing the temperature\n","format":"float","maximum":2,"minimum":0,"type":"number"},"thinkingConfig":{"description":"Optional. Configuration for thinking features. Thinking is a process where the model breaks down a complex task into smaller steps to generate a higher-quality response.","properties":{"includeThoughts":{"description":"Optional. If true, the model will include its thoughts in the response.","type":"boolean"},"thinkingBudget":{"description":"Optional. The token budget for the model's thinking process. The model will make a best effort to stay within this budget.","type":"integer"},"thinkingLevel":{"description":"Optional. The thinking level for the model.","enum":["THINKING_LEVEL_UNSPECIFIED","LOW","MEDIUM","HIGH","MINIMAL"],"type":"string"}},"type":"object"},"topK":{"default":40,"description":"Top-K changes how the model selects tokens for output. A top-K of 1 means the next selected token is the most probable among all tokens in the model's vocabulary. A top-K of 3 means that the next token is selected from among the 3 most probable tokens by using temperature.\n","example":40,"minimum":1,"type":"integer"},"topP":{"default":0.95,"description":"If specified, nucleus sampling is used.\nTop-P changes how the model selects tokens for output. Tokens are selected from the most (see top-K) to least probable until the sum of their probabilities equals the top-P value. For example, if tokens A, B, and C have a probability of 0.3, 0.2, and 0.1 and the top-P value is 0.5, then the model will select either A or B as the next token by using temperature and excludes C as a candidate.\nSpecify a lower value for less random responses and a higher value for more random responses.\n","format":"float","maximum":1,"minimum":0,"type":"number"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiOffset":{"description":"Represents a duration offset for video timeline positions.\n","properties":{"nanos":{"description":"Signed fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values.\n","example":0,"maximum":999999999,"minimum":0,"type":"integer"},"seconds":{"description":"Signed seconds of the span of time. Must be from -315,576,000,000 to +315,576,000,000 inclusive.\n","example":60,"maximum":315576000000,"minimum":-315576000000,"type":"integer"}},"type":"object"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiSafetySetting":{"description":"Per request settings for blocking unsafe content. Enforced on GenerateContentResponse.candidates.\n","properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"threshold":{"$ref":"#/components/schemas/GeminiSafetyThreshold"}},"required":["category","threshold"],"type":"object"},"GeminiSafetyThreshold":{"enum":["OFF","BLOCK_NONE","BLOCK_LOW_AND_ABOVE","BLOCK_MEDIUM_AND_ABOVE","BLOCK_ONLY_HIGH"],"type":"string"},"GeminiSystemInstructionContent":{"description":"Available for gemini-2.0-flash and gemini-2.0-flash-lite. Instructions for the model to steer it toward better performance. For example, \"Answer as concisely as possible\" or \"Don't use technical terms in your response\". The text strings count toward the token limit. The role field of systemInstruction is ignored and doesn't affect the performance of the model. Note: Only text should be used in parts and content in each part should be in a separate paragraph.\n","properties":{"parts":{"description":"A list of ordered parts that make up a single message. Different parts may have different IANA MIME types. For limits on the inputs, such as the maximum number of tokens or the number of images, see the model specifications on the Google models page.\n","items":{"$ref":"#/components/schemas/GeminiTextPart"},"type":"array"},"role":{"description":"The identity of the entity that creates the message. The following values are supported: user: This indicates that the message is sent by a real person, typically a user-generated message. model: This indicates that the message is generated by the model. The model value is used to insert messages from the model into the conversation during multi-turn conversations. For non-multi-turn conversations, this field can be left blank or unset.\n","enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiTextPart":{"properties":{"text":{"description":"A text prompt or code snippet.","example":"Answer as concisely as possible","type":"string"}},"type":"object"},"GeminiTool":{"description":"A piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. See Function calling.\n","properties":{"functionDeclarations":{"items":{"$ref":"#/components/schemas/GeminiFunctionDeclaration"},"type":"array"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"GeminiVideoMetadata":{"description":"For video input, the start and end offset of the video in Duration format. For example, to specify a 10 second clip starting at 1:00, set \"startOffset\": { \"seconds\": 60 } and \"endOffset\": { \"seconds\": 70 }. The metadata should only be specified while the video data is presented in inlineData or fileData.\n","properties":{"endOffset":{"$ref":"#/components/schemas/GeminiOffset"},"startOffset":{"$ref":"#/components/schemas/GeminiOffset"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3.5-flash","x-comfy-input-schema-authored":true,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/vertexai/gemini-3.7-flash.json b/router-schemas/vertexai/gemini-3.7-flash.json new file mode 100644 index 000000000..4d93cce8b --- /dev/null +++ b/router-schemas/vertexai/gemini-3.7-flash.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"vertexai/gemini-3.7-flash","description":"The request body Comfy Router accepts for the model \"vertexai/gemini-3.7-flash\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"63d290559279"},"paths":{"/v2/models/vertexai/gemini-3.7-flash":{"post":{"operationId":"runRouterModel","summary":"Run vertexai/gemini-3.7-flash synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/GeminiGenerateContentResponse"}],"description":"Comfy Router output schema for the text-returning Gemini models: Vertex AI's `generateContent` response, forwarded unchanged, with the generated text at `candidates[0].content.parts[0].text`.\n`candidates` is where a completed generation lands and is the field to branch on. `promptFeedback` and `usageMetadata` are BOTH present on a safety-blocked response that carries no candidate at all, so neither is evidence that anything was generated.","example":{"candidates":[{"content":{"parts":[{"text":"A lighthouse stands at the edge of the harbour, its lamp still turning as the sun comes up."}],"role":"model"},"finishReason":"STOP"}],"modelVersion":"gemini-2.5-flash","responseId":"0d1f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b","usageMetadata":{"candidatesTokenCount":21,"promptTokenCount":12,"totalTokenCount":33}}}}}}}}}},"components":{"schemas":{"GeminiCandidate":{"properties":{"citationMetadata":{"$ref":"#/components/schemas/GeminiCitationMetadata"},"content":{"$ref":"#/components/schemas/GeminiContent"},"finishReason":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiCitation":{"properties":{"authors":{"items":{"type":"string"},"type":"array"},"endIndex":{"type":"integer"},"license":{"type":"string"},"publicationDate":{"format":"date","type":"string"},"startIndex":{"type":"integer"},"title":{"type":"string"},"uri":{"type":"string"}},"type":"object"},"GeminiCitationMetadata":{"properties":{"citations":{"items":{"$ref":"#/components/schemas/GeminiCitation"},"type":"array"}},"type":"object"},"GeminiContent":{"description":"The content of the current conversation with the model. For single-turn queries, this is a single instance. For multi-turn queries, this is a repeated field that contains conversation history and the latest request.\n","properties":{"parts":{"items":{"$ref":"#/components/schemas/GeminiPart"},"type":"array"},"role":{"enum":["user","model"],"example":"user","type":"string"}},"required":["role","parts"],"type":"object"},"GeminiFileData":{"description":"URI based data.","properties":{"fileUri":{"description":"URI","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiGenerateContentResponse":{"properties":{"candidates":{"items":{"$ref":"#/components/schemas/GeminiCandidate"},"type":"array"},"createTime":{"description":"Timestamp when the response was created.","type":"string"},"modelVersion":{"description":"The model version used to generate the response.","type":"string"},"promptFeedback":{"$ref":"#/components/schemas/GeminiPromptFeedback"},"responseId":{"description":"Unique identifier for the response.","type":"string"},"usageMetadata":{"$ref":"#/components/schemas/GeminiUsageMetadata"}},"type":"object"},"GeminiInlineData":{"description":"Inline data in raw bytes. For gemini-2.0-flash-lite and gemini-2.0-flash, you can specify up to 3000 images by using inlineData.\n","properties":{"data":{"description":"The base64 encoding of the image, PDF, or video to include inline in the prompt. When including media inline, you must also specify the media type (mimeType) of the data. Size limit: 20MB\n","format":"byte","type":"string"},"mimeType":{"$ref":"#/components/schemas/GeminiMimeType"}},"type":"object"},"GeminiMimeType":{"description":"The media type of the file specified in the data or fileUri fields. Acceptable values include the following. For gemini-2.0-flash-lite and gemini-2.0-flash, the maximum length of an audio file is 8.4 hours and the maximum length of a video file (without audio) is one hour. For more information, see Gemini audio and video requirements. Text files must be UTF-8 encoded. The contents of the text file count toward the token limit. There is no limit on image resolution.","enum":["application/pdf","audio/mpeg","audio/mp3","audio/wav","image/png","image/jpeg","image/webp","text/plain","video/mov","video/mpeg","video/mp4","video/mpg","video/avi","video/wmv","video/mpegps","video/flv"],"type":"string"},"GeminiPart":{"properties":{"fileData":{"$ref":"#/components/schemas/GeminiFileData"},"inlineData":{"$ref":"#/components/schemas/GeminiInlineData"},"text":{"description":"A text prompt or code snippet.","example":"Write a story about a robot learning to paint","type":"string"},"thought":{"description":"Indicates this part is a thinking/reasoning step from the model.","type":"boolean"}},"type":"object"},"GeminiPromptFeedback":{"properties":{"blockReason":{"type":"string"},"blockReasonMessage":{"type":"string"},"safetyRatings":{"items":{"$ref":"#/components/schemas/GeminiSafetyRating"},"type":"array"}},"type":"object"},"GeminiSafetyCategory":{"enum":["HARM_CATEGORY_SEXUALLY_EXPLICIT","HARM_CATEGORY_HATE_SPEECH","HARM_CATEGORY_HARASSMENT","HARM_CATEGORY_DANGEROUS_CONTENT"],"type":"string"},"GeminiSafetyRating":{"properties":{"category":{"$ref":"#/components/schemas/GeminiSafetyCategory"},"probability":{"description":"The probability that the content violates the specified safety category","enum":["NEGLIGIBLE","LOW","MEDIUM","HIGH","UNKNOWN"],"type":"string"}},"type":"object"},"GeminiUsageMetadata":{"properties":{"cachedContentTokenCount":{"description":"Output only. Number of tokens in the cached part in the input (the cached content).","type":"integer"},"candidatesTokenCount":{"description":"Number of tokens in the response(s).","type":"integer"},"candidatesTokensDetails":{"description":"Breakdown of candidate tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"promptTokenCount":{"description":"Number of tokens in the request. When cachedContent is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.","type":"integer"},"promptTokensDetails":{"description":"Breakdown of prompt tokens by modality.","items":{"$ref":"#/components/schemas/ModalityTokenCount"},"type":"array"},"thoughtsTokenCount":{"description":"Number of tokens present in thoughts output.","type":"integer"},"toolUsePromptTokenCount":{"description":"Number of tokens present in tool-use prompt(s).","type":"integer"},"totalTokenCount":{"description":"Total number of tokens (prompt + candidates).","type":"integer"},"trafficType":{"description":"Traffic type used for the request (e.g., PROVISIONED_THROUGHPUT).","type":"string"}},"type":"object"},"Modality":{"description":"Type of input or output content modality.","enum":["MODALITY_UNSPECIFIED","TEXT","IMAGE","VIDEO","AUDIO","DOCUMENT"],"type":"string"},"ModalityTokenCount":{"properties":{"modality":{"$ref":"#/components/schemas/Modality"},"tokenCount":{"description":"Number of tokens for the given modality.","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"vertexai/gemini-3.7-flash","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.0-i2v.json b/router-schemas/wan/happyhorse-1.0-i2v.json new file mode 100644 index 000000000..45585b6a4 --- /dev/null +++ b/router-schemas/wan/happyhorse-1.0-i2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.0-i2v","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.0-i2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/happyhorse-1.0-i2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.0-i2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.0-i2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.0-r2v.json b/router-schemas/wan/happyhorse-1.0-r2v.json new file mode 100644 index 000000000..84e6254ca --- /dev/null +++ b/router-schemas/wan/happyhorse-1.0-r2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.0-r2v","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.0-r2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/happyhorse-1.0-r2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.0-r2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.0-r2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.0-t2v.json b/router-schemas/wan/happyhorse-1.0-t2v.json new file mode 100644 index 000000000..903cad5d2 --- /dev/null +++ b/router-schemas/wan/happyhorse-1.0-t2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.0-t2v","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.0-t2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"fa98a609ca89"},"paths":{"/v2/models/wan/happyhorse-1.0-t2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.0-t2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a TEXT-to-video id, so its example is the T2V shape: the asset at `output.video_url` and the `video_duration`/`video_ratio`/`video_count` usage triple. `duration`/`SR` are the conditioned-video spelling and are not what a T2V task reports.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan2.6-t2v/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"video_count":1,"video_duration":5,"video_ratio":"1920*1080"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.0-t2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.0-video-edit.json b/router-schemas/wan/happyhorse-1.0-video-edit.json new file mode 100644 index 000000000..fd488e881 --- /dev/null +++ b/router-schemas/wan/happyhorse-1.0-video-edit.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.0-video-edit","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.0-video-edit\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/happyhorse-1.0-video-edit":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.0-video-edit synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.0-video-edit","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.1-i2v.json b/router-schemas/wan/happyhorse-1.1-i2v.json new file mode 100644 index 000000000..c01c781df --- /dev/null +++ b/router-schemas/wan/happyhorse-1.1-i2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.1-i2v","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.1-i2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/happyhorse-1.1-i2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.1-i2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.1-i2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.1-r2v.json b/router-schemas/wan/happyhorse-1.1-r2v.json new file mode 100644 index 000000000..91002bc5f --- /dev/null +++ b/router-schemas/wan/happyhorse-1.1-r2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.1-r2v","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.1-r2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/happyhorse-1.1-r2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.1-r2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.1-r2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/happyhorse-1.1-t2v.json b/router-schemas/wan/happyhorse-1.1-t2v.json new file mode 100644 index 000000000..ced2ad405 --- /dev/null +++ b/router-schemas/wan/happyhorse-1.1-t2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/happyhorse-1.1-t2v","description":"The request body Comfy Router accepts for the model \"wan/happyhorse-1.1-t2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"fa98a609ca89"},"paths":{"/v2/models/wan/happyhorse-1.1-t2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/happyhorse-1.1-t2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a TEXT-to-video id, so its example is the T2V shape: the asset at `output.video_url` and the `video_duration`/`video_ratio`/`video_count` usage triple. `duration`/`SR` are the conditioned-video spelling and are not what a T2V task reports.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan2.6-t2v/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"video_count":1,"video_duration":5,"video_ratio":"1920*1080"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/happyhorse-1.1-t2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.5-i2i-preview.json b/router-schemas/wan/wan2.5-i2i-preview.json new file mode 100644 index 000000000..a4d6cc111 --- /dev/null +++ b/router-schemas/wan/wan2.5-i2i-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.5-i2i-preview","description":"The request body Comfy Router accepts for the model \"wan/wan2.5-i2i-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"8de2d069c345"},"paths":{"/v2/models/wan/wan2.5-i2i-preview":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.5-i2i-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes an IMAGE id, so its example is the image shape: the assets at `output.results[].url` — a LIST, one element per generated image — with `output.task_metrics` reporting the TOTAL/SUCCEEDED/FAILED split and the `size`/`image_count` usage pair. The example deliberately shows a partially failed task, the case in which an element carries `code`/`message` INSTEAD of `url`, because that is the shape a caller most easily gets wrong. `output.video_url` is the video spelling and is never populated here.","example":{"output":{"end_time":"2027-01-01T00:00:12.000Z","results":[{"actual_prompt":"a single red maple leaf resting on still water, shallow depth of field, soft morning light","orig_prompt":"a single red maple leaf resting on still water","url":"https://example.invalid/wan/wan2.5-t2i-preview/generated-1.png"},{"code":"DataInspectionFailed","message":"This candidate was rejected; the task as a whole succeeded.","orig_prompt":"a single red maple leaf resting on still water"}],"scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_metrics":{"FAILED":1,"SUCCEEDED":1,"TOTAL":2},"task_status":"SUCCEEDED"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"image_count":1,"size":"1024*1024"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.5-i2i-preview","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.5-i2v-preview.json b/router-schemas/wan/wan2.5-i2v-preview.json new file mode 100644 index 000000000..bcd458ebc --- /dev/null +++ b/router-schemas/wan/wan2.5-i2v-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.5-i2v-preview","description":"The request body Comfy Router accepts for the model \"wan/wan2.5-i2v-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan2.5-i2v-preview":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.5-i2v-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.5-i2v-preview","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.5-t2i-preview.json b/router-schemas/wan/wan2.5-t2i-preview.json new file mode 100644 index 000000000..db51190e9 --- /dev/null +++ b/router-schemas/wan/wan2.5-t2i-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.5-t2i-preview","description":"The request body Comfy Router accepts for the model \"wan/wan2.5-t2i-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"8de2d069c345"},"paths":{"/v2/models/wan/wan2.5-t2i-preview":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.5-t2i-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes an IMAGE id, so its example is the image shape: the assets at `output.results[].url` — a LIST, one element per generated image — with `output.task_metrics` reporting the TOTAL/SUCCEEDED/FAILED split and the `size`/`image_count` usage pair. The example deliberately shows a partially failed task, the case in which an element carries `code`/`message` INSTEAD of `url`, because that is the shape a caller most easily gets wrong. `output.video_url` is the video spelling and is never populated here.","example":{"output":{"end_time":"2027-01-01T00:00:12.000Z","results":[{"actual_prompt":"a single red maple leaf resting on still water, shallow depth of field, soft morning light","orig_prompt":"a single red maple leaf resting on still water","url":"https://example.invalid/wan/wan2.5-t2i-preview/generated-1.png"},{"code":"DataInspectionFailed","message":"This candidate was rejected; the task as a whole succeeded.","orig_prompt":"a single red maple leaf resting on still water"}],"scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_metrics":{"FAILED":1,"SUCCEEDED":1,"TOTAL":2},"task_status":"SUCCEEDED"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"image_count":1,"size":"1024*1024"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.5-t2i-preview","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.5-t2v-preview.json b/router-schemas/wan/wan2.5-t2v-preview.json new file mode 100644 index 000000000..5ece5227e --- /dev/null +++ b/router-schemas/wan/wan2.5-t2v-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.5-t2v-preview","description":"The request body Comfy Router accepts for the model \"wan/wan2.5-t2v-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"fa98a609ca89"},"paths":{"/v2/models/wan/wan2.5-t2v-preview":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.5-t2v-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a TEXT-to-video id, so its example is the T2V shape: the asset at `output.video_url` and the `video_duration`/`video_ratio`/`video_count` usage triple. `duration`/`SR` are the conditioned-video spelling and are not what a T2V task reports.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan2.6-t2v/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"video_count":1,"video_duration":5,"video_ratio":"1920*1080"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.5-t2v-preview","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.6-i2v.json b/router-schemas/wan/wan2.6-i2v.json new file mode 100644 index 000000000..fc8249de5 --- /dev/null +++ b/router-schemas/wan/wan2.6-i2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.6-i2v","description":"The request body Comfy Router accepts for the model \"wan/wan2.6-i2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan2.6-i2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.6-i2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.6-i2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.6-r2v.json b/router-schemas/wan/wan2.6-r2v.json new file mode 100644 index 000000000..19ef80165 --- /dev/null +++ b/router-schemas/wan/wan2.6-r2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.6-r2v","description":"The request body Comfy Router accepts for the model \"wan/wan2.6-r2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan2.6-r2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.6-r2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.6-r2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.6-t2v.json b/router-schemas/wan/wan2.6-t2v.json new file mode 100644 index 000000000..c407a23a4 --- /dev/null +++ b/router-schemas/wan/wan2.6-t2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.6-t2v","description":"The request body Comfy Router accepts for the model \"wan/wan2.6-t2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"fa98a609ca89"},"paths":{"/v2/models/wan/wan2.6-t2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.6-t2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a TEXT-to-video id, so its example is the T2V shape: the asset at `output.video_url` and the `video_duration`/`video_ratio`/`video_count` usage triple. `duration`/`SR` are the conditioned-video spelling and are not what a T2V task reports.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan2.6-t2v/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"video_count":1,"video_duration":5,"video_ratio":"1920*1080"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.6-t2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.7-i2v.json b/router-schemas/wan/wan2.7-i2v.json new file mode 100644 index 000000000..b711bbfd0 --- /dev/null +++ b/router-schemas/wan/wan2.7-i2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.7-i2v","description":"The request body Comfy Router accepts for the model \"wan/wan2.7-i2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan2.7-i2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.7-i2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.7-i2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.7-r2v.json b/router-schemas/wan/wan2.7-r2v.json new file mode 100644 index 000000000..66bd98e57 --- /dev/null +++ b/router-schemas/wan/wan2.7-r2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.7-r2v","description":"The request body Comfy Router accepts for the model \"wan/wan2.7-r2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan2.7-r2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.7-r2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.7-r2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.7-t2v.json b/router-schemas/wan/wan2.7-t2v.json new file mode 100644 index 000000000..5a24b2ac3 --- /dev/null +++ b/router-schemas/wan/wan2.7-t2v.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.7-t2v","description":"The request body Comfy Router accepts for the model \"wan/wan2.7-t2v\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"fa98a609ca89"},"paths":{"/v2/models/wan/wan2.7-t2v":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.7-t2v synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a TEXT-to-video id, so its example is the T2V shape: the asset at `output.video_url` and the `video_duration`/`video_ratio`/`video_count` usage triple. `duration`/`SR` are the conditioned-video spelling and are not what a T2V task reports.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan2.6-t2v/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"video_count":1,"video_duration":5,"video_ratio":"1920*1080"}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.7-t2v","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan2.7-videoedit.json b/router-schemas/wan/wan2.7-videoedit.json new file mode 100644 index 000000000..7a54a6ecd --- /dev/null +++ b/router-schemas/wan/wan2.7-videoedit.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan2.7-videoedit","description":"The request body Comfy Router accepts for the model \"wan/wan2.7-videoedit\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan2.7-videoedit":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan2.7-videoedit synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan2.7-videoedit","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan3.0-video-prime.json b/router-schemas/wan/wan3.0-video-prime.json new file mode 100644 index 000000000..4ec2dbb49 --- /dev/null +++ b/router-schemas/wan/wan3.0-video-prime.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan3.0-video-prime","description":"The request body Comfy Router accepts for the model \"wan/wan3.0-video-prime\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan3.0-video-prime":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan3.0-video-prime synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan3.0-video-prime","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/wan/wan3.0-video.json b/router-schemas/wan/wan3.0-video.json new file mode 100644 index 000000000..159c3d6a4 --- /dev/null +++ b/router-schemas/wan/wan3.0-video.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"wan/wan3.0-video","description":"The request body Comfy Router accepts for the model \"wan/wan3.0-video\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"58bc710d47de"},"paths":{"/v2/models/wan/wan3.0-video":{"post":{"operationId":"runRouterModel","summary":"Run wan/wan3.0-video synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WanTaskQueryResponse"},{"properties":{"code":{"description":"Error code for a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded).","type":"string"},"message":{"description":"Detailed information about a failed request, reported at the ROOT of the envelope rather than under `output` (not returned if the request succeeded). Read this before falling back to `output.message`.","type":"string"}},"type":"object"}],"description":"Comfy Router output schema for the Wan and HappyHorse models: the terminal `GET /proxy/wan/api/v1/tasks/{task_id}` document, forwarded unchanged. All three DashScope submit operations — `/api/v1/services/aigc/video-generation/video-synthesis` (video), `/api/v1/services/aigc/text2image/image-synthesis` (image) and `/api/v1/services/aigc/image2image/image-synthesis` (image) — are SUBMIT-AND-POLL (`routerresult/classification.go` records all three as `ReturnModeSubmitPoll` on the shared `wanTaskStatus` poll route), so the body a caller receives is the finished task rather than the `{task_id, task_status}` handle the underlying submit answers with. That is the reading a modality-shaped guess gets backwards: unlike xAI and BytePlus, DashScope puts the IMAGE operations behind the same task queue as the video one, and all twenty ids answer this one document.\n`output.task_status` is DashScope's own UPPERCASE vocabulary — `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELED`, `UNKNOWN` — forwarded unchanged; `routerpollstate/families.go` `FamilyWan` treats `SUCCEEDED` as the one terminal success and the rest of the closed set as terminal failures.\nWHICH LEAF CARRIES THE ASSET DEPENDS ON THE OPERATION, and a caller must select on the operation it submitted rather than on `output` being non-empty:\n* VIDEO tasks (the eighteen video ids, submitted through\n `video-generation/video-synthesis`) put the generated video at\n `output.video_url`. An I2V task that also generated audio carries it separately at\n `output.check_audio`; that is not a substitute for `output.video_url`.\n* IMAGE tasks (`wan/wan2.5-t2i-preview` through `text2image/image-synthesis` and\n `wan/wan2.5-i2i-preview` through `image2image/image-synthesis`) put each generated\n image at `output.results[].url`. `results` is a LIST — DashScope defaults to four\n images and honours `parameters.n` — and an individual element may carry `code` and\n `message` INSTEAD of `url` when that one image failed while the task as a whole\n succeeded, so a caller must read `url` per element rather than assume the list is\n uniform. `output.task_metrics` reports the TOTAL/SUCCEEDED/FAILED split for\n exactly that case.\n\nA `SUCCEEDED` whose `output` carries neither leaf is a success that produced nothing, not a finished generation; through Router that distinction is already settled, because `routerpollstate` answers it as a Comfy Router error rather than with the provider document. A FAILED task reports its reason at `output.code`/`output.message`, and DashScope may additionally report a top-level `code`/`message` on the envelope itself; read the ROOT pair first and fall back to the nested one, which is the order the first-party Wan poller uses.\nThe URLs are DashScope-hosted and valid for 24 HOURS from completion, so download them promptly rather than storing them. `usage` reports DashScope's own generation accounting — its numbers, not the Comfy charge — and its fields are per-operation (`video_duration`/`video_ratio`/`video_count` for T2V, `duration`/`SR` for I2V and wan3.0-video, `size`/`image_count` for T2I).\nSCHEMA VS EXAMPLE: the schema below is the ONE shared `WanTaskQueryResponse` all twenty ids answer with — the wan/* output components are split by modality only so that each published document carries an example of its OWN operation, never a video example printed under an image model. The split is presentational; the contract is not.\nTHIS DOCUMENT describes a conditioned-video or wan3.0-video id, so its example is the video shape: the asset at `output.video_url` and the `duration`/`SR` usage pair.","example":{"output":{"actual_prompt":"a single red maple leaf falling onto still water, slow motion, shallow depth of field","end_time":"2027-01-01T00:01:04.000Z","orig_prompt":"a single red maple leaf falling onto still water","scheduled_time":"2027-01-01T00:00:01.000Z","submit_time":"2027-01-01T00:00:00.000Z","task_id":"0385dc79-5ff8-4d82-bcb6-7c1a9f2e4d60","task_status":"SUCCEEDED","video_url":"https://example.invalid/wan/wan3.0-video/generated.mp4"},"request_id":"7574ee8f-38a3-4b1e-9280-11c33ab46e51","usage":{"SR":720,"duration":5}}}}}}}}}},"components":{"schemas":{"WanTaskQueryResponse":{"properties":{"output":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (for video tasks)","type":"string"},"check_audio":{"description":"Audio URL for I2V tasks with audio generation","type":"string"},"code":{"description":"The error code for the failed request (not returned if request is successful)","type":"string"},"end_time":{"description":"Task completion time","type":"string"},"message":{"description":"Detailed information about the failed request (not returned if request is successful)","type":"string"},"orig_prompt":{"description":"Original input prompt (for video tasks)","type":"string"},"results":{"description":"List of task results for image generation tasks","items":{"properties":{"actual_prompt":{"description":"Actual prompt after intelligent rewriting (if enabled)","type":"string"},"code":{"description":"Image error code (returned when some tasks fail)","type":"string"},"message":{"description":"Image error information (returned when some tasks fail)","type":"string"},"orig_prompt":{"description":"Original input prompt","type":"string"},"url":{"description":"Generated image URL address","type":"string"}},"type":"object"},"type":"array"},"scheduled_time":{"description":"Task execution time","type":"string"},"submit_time":{"description":"Task submission time","type":"string"},"task_id":{"description":"Task ID","type":"string"},"task_metrics":{"description":"Task result statistics for image generation tasks","properties":{"FAILED":{"description":"Number of failed tasks","type":"integer"},"SUCCEEDED":{"description":"Number of successful tasks","type":"integer"},"TOTAL":{"description":"Total number of tasks","type":"integer"}},"type":"object"},"task_status":{"description":"Task status","enum":["PENDING","RUNNING","SUCCEEDED","FAILED","CANCELED","UNKNOWN"],"type":"string"},"video_url":{"description":"Video URL for completed video generation tasks. Link validity period 24 hours","type":"string"}},"required":["task_id","task_status"],"type":"object"},"request_id":{"description":"Unique request identifier","type":"string"},"usage":{"description":"Output information statistics. Only successful results are counted","properties":{"SR":{"description":"Video resolution level (I2V and wan3.0-video tasks)","type":"integer"},"duration":{"description":"Duration of generated video in seconds (I2V and wan3.0-video tasks)","type":"number"},"fps":{"description":"Frame rate of the generated video (wan3.0-video tasks)","type":"integer"},"image_count":{"description":"Number of generated images (T2I tasks)","type":"integer"},"input_video_duration":{"description":"Duration of the input video in seconds, 0.0 when no video input (wan3.0-video tasks)","type":"number"},"output_video_duration":{"description":"Duration of the output video in seconds (wan3.0-video tasks)","type":"number"},"ratio":{"description":"Aspect ratio of the generated video, e.g. 16:9 (wan3.0-video tasks)","type":"string"},"size":{"description":"Image resolution (T2I tasks)","type":"string"},"video_count":{"description":"Number of generated videos (T2V tasks)","type":"integer"},"video_duration":{"description":"Duration of generated video in seconds (T2V tasks)","type":"number"},"video_ratio":{"description":"Video resolution ratio (T2V tasks)","type":"string"}},"type":"object"}},"required":["request_id","output"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"wan/wan3.0-video","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-image-2.0.json b/router-schemas/xai/grok-imagine-image-2.0.json new file mode 100644 index 000000000..77ef5b80b --- /dev/null +++ b/router-schemas/xai/grok-imagine-image-2.0.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-image-2.0","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-image-2.0\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0a88e171355d"},"paths":{"/v2/models/xai/grok-imagine-image-2.0":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-image-2.0 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIImageGenerationResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine image models: the `POST /v1/images/generations` document xAI returns, forwarded unchanged EXCEPT for the re-hosted `data[]` entries described below - every other field is xAI's own. This family is DIRECT-RETURN — `routerresult/classification.go` records `{provider: xai, endpoint: /v1/images/generations}` as `ReturnModeDirect` with no poll route — so the body a caller receives is the finished generation on the original call rather than a task handle.\nThe generated images are in `data`, and on THIS surface an entry is addressed by its `url`. Router coerces the outbound `response_format` to `url` for this family and re-hosts every image it can fetch onto Comfy storage, replacing xAI's URL with a signed Comfy one and clearing `b64_json`, so a request that asked for `b64_json` is answered exactly as a `url` request is. `mime_type` names the encoding. The `/proxy/` route reached directly is the one that still answers in the format the caller asked for.\nDurability is per entry, not per response: an image whose re-host fails keeps xAI's own answer — under the coerced `response_format` that is xAI's short-lived URL — rather than a Comfy one, and the rest of the response is unaffected. So one `data` array can mix durable Comfy URLs with expiring partner ones, and a caller that stores or replays this document should not assume every URL in it outlives the call. Such a response IS the answer to the call that produced it, returned and charged exactly as a fully re-hosted one is — but it is replayable only BRIEFLY. A prompt retry carrying the same `Idempotency-Key` — within a few minutes of the original, which is where a connection dropped mid-call puts an SDK's automatic re-send — is answered from the record exactly as any other replay is, because the partner's own link is still alive that soon. A retry after that is answered `409 invalid_input` rather than handed a document whose links may already have expired. The key is consumed either way, so no retry ever re-runs or re-charges; once the replay window has passed, use a new key to run the generation again. A response whose every entry re-hosted cleanly replays normally for the full 24 hours, because a Comfy signed URL is minted with the same lifetime the record is kept for.\n`block_reason` and `usage` are populated WITHOUT any `data` when xAI's input moderation refuses the request, so a caller must key completion off `data` rather than off a `200` alone — which is exactly why the nightly SDK case for this family asserts `data` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"data":[{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated.jpg"},{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated-2.jpg"}],"usage":{"cost_in_usd_ticks":200000000}}}}}}}}}},"components":{"schemas":{"XAIGeneratedImage":{"description":"A generated image from xAI","properties":{"b64_json":{"description":"A base64-encoded string representation of the generated image in jpeg encoding (if response_format is b64_json)","type":"string"},"mime_type":{"description":"The MIME type of the generated image (e.g. image/png, image/jpeg, image/webp).","type":"string"},"url":{"description":"A url to the generated image (if response_format is url)","type":"string"}},"type":"object"},"XAIImageGenerationResponse":{"description":"Response from xAI image generation or editing","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","type":"string"},"data":{"description":"A list of generated image objects","items":{"$ref":"#/components/schemas/XAIGeneratedImage"},"type":"array"},"usage":{"$ref":"#/components/schemas/XAIImageUsage"}},"type":"object"},"XAIImageUsage":{"description":"Usage information for the image generation request","properties":{"cost_in_usd_ticks":{"description":"Accurate cost of this request in USD ticks (10,000,000,000 ticks = 1 USD)","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-image-2.0","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-image-pro.json b/router-schemas/xai/grok-imagine-image-pro.json new file mode 100644 index 000000000..3f78be247 --- /dev/null +++ b/router-schemas/xai/grok-imagine-image-pro.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-image-pro","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-image-pro\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0a88e171355d"},"paths":{"/v2/models/xai/grok-imagine-image-pro":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-image-pro synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIImageGenerationResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine image models: the `POST /v1/images/generations` document xAI returns, forwarded unchanged EXCEPT for the re-hosted `data[]` entries described below - every other field is xAI's own. This family is DIRECT-RETURN — `routerresult/classification.go` records `{provider: xai, endpoint: /v1/images/generations}` as `ReturnModeDirect` with no poll route — so the body a caller receives is the finished generation on the original call rather than a task handle.\nThe generated images are in `data`, and on THIS surface an entry is addressed by its `url`. Router coerces the outbound `response_format` to `url` for this family and re-hosts every image it can fetch onto Comfy storage, replacing xAI's URL with a signed Comfy one and clearing `b64_json`, so a request that asked for `b64_json` is answered exactly as a `url` request is. `mime_type` names the encoding. The `/proxy/` route reached directly is the one that still answers in the format the caller asked for.\nDurability is per entry, not per response: an image whose re-host fails keeps xAI's own answer — under the coerced `response_format` that is xAI's short-lived URL — rather than a Comfy one, and the rest of the response is unaffected. So one `data` array can mix durable Comfy URLs with expiring partner ones, and a caller that stores or replays this document should not assume every URL in it outlives the call. Such a response IS the answer to the call that produced it, returned and charged exactly as a fully re-hosted one is — but it is replayable only BRIEFLY. A prompt retry carrying the same `Idempotency-Key` — within a few minutes of the original, which is where a connection dropped mid-call puts an SDK's automatic re-send — is answered from the record exactly as any other replay is, because the partner's own link is still alive that soon. A retry after that is answered `409 invalid_input` rather than handed a document whose links may already have expired. The key is consumed either way, so no retry ever re-runs or re-charges; once the replay window has passed, use a new key to run the generation again. A response whose every entry re-hosted cleanly replays normally for the full 24 hours, because a Comfy signed URL is minted with the same lifetime the record is kept for.\n`block_reason` and `usage` are populated WITHOUT any `data` when xAI's input moderation refuses the request, so a caller must key completion off `data` rather than off a `200` alone — which is exactly why the nightly SDK case for this family asserts `data` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"data":[{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated.jpg"},{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated-2.jpg"}],"usage":{"cost_in_usd_ticks":200000000}}}}}}}}}},"components":{"schemas":{"XAIGeneratedImage":{"description":"A generated image from xAI","properties":{"b64_json":{"description":"A base64-encoded string representation of the generated image in jpeg encoding (if response_format is b64_json)","type":"string"},"mime_type":{"description":"The MIME type of the generated image (e.g. image/png, image/jpeg, image/webp).","type":"string"},"url":{"description":"A url to the generated image (if response_format is url)","type":"string"}},"type":"object"},"XAIImageGenerationResponse":{"description":"Response from xAI image generation or editing","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","type":"string"},"data":{"description":"A list of generated image objects","items":{"$ref":"#/components/schemas/XAIGeneratedImage"},"type":"array"},"usage":{"$ref":"#/components/schemas/XAIImageUsage"}},"type":"object"},"XAIImageUsage":{"description":"Usage information for the image generation request","properties":{"cost_in_usd_ticks":{"description":"Accurate cost of this request in USD ticks (10,000,000,000 ticks = 1 USD)","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-image-pro","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-image-quality.json b/router-schemas/xai/grok-imagine-image-quality.json new file mode 100644 index 000000000..afd2a40c5 --- /dev/null +++ b/router-schemas/xai/grok-imagine-image-quality.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-image-quality","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-image-quality\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0a88e171355d"},"paths":{"/v2/models/xai/grok-imagine-image-quality":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-image-quality synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIImageGenerationResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine image models: the `POST /v1/images/generations` document xAI returns, forwarded unchanged EXCEPT for the re-hosted `data[]` entries described below - every other field is xAI's own. This family is DIRECT-RETURN — `routerresult/classification.go` records `{provider: xai, endpoint: /v1/images/generations}` as `ReturnModeDirect` with no poll route — so the body a caller receives is the finished generation on the original call rather than a task handle.\nThe generated images are in `data`, and on THIS surface an entry is addressed by its `url`. Router coerces the outbound `response_format` to `url` for this family and re-hosts every image it can fetch onto Comfy storage, replacing xAI's URL with a signed Comfy one and clearing `b64_json`, so a request that asked for `b64_json` is answered exactly as a `url` request is. `mime_type` names the encoding. The `/proxy/` route reached directly is the one that still answers in the format the caller asked for.\nDurability is per entry, not per response: an image whose re-host fails keeps xAI's own answer — under the coerced `response_format` that is xAI's short-lived URL — rather than a Comfy one, and the rest of the response is unaffected. So one `data` array can mix durable Comfy URLs with expiring partner ones, and a caller that stores or replays this document should not assume every URL in it outlives the call. Such a response IS the answer to the call that produced it, returned and charged exactly as a fully re-hosted one is — but it is replayable only BRIEFLY. A prompt retry carrying the same `Idempotency-Key` — within a few minutes of the original, which is where a connection dropped mid-call puts an SDK's automatic re-send — is answered from the record exactly as any other replay is, because the partner's own link is still alive that soon. A retry after that is answered `409 invalid_input` rather than handed a document whose links may already have expired. The key is consumed either way, so no retry ever re-runs or re-charges; once the replay window has passed, use a new key to run the generation again. A response whose every entry re-hosted cleanly replays normally for the full 24 hours, because a Comfy signed URL is minted with the same lifetime the record is kept for.\n`block_reason` and `usage` are populated WITHOUT any `data` when xAI's input moderation refuses the request, so a caller must key completion off `data` rather than off a `200` alone — which is exactly why the nightly SDK case for this family asserts `data` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"data":[{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated.jpg"},{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated-2.jpg"}],"usage":{"cost_in_usd_ticks":200000000}}}}}}}}}},"components":{"schemas":{"XAIGeneratedImage":{"description":"A generated image from xAI","properties":{"b64_json":{"description":"A base64-encoded string representation of the generated image in jpeg encoding (if response_format is b64_json)","type":"string"},"mime_type":{"description":"The MIME type of the generated image (e.g. image/png, image/jpeg, image/webp).","type":"string"},"url":{"description":"A url to the generated image (if response_format is url)","type":"string"}},"type":"object"},"XAIImageGenerationResponse":{"description":"Response from xAI image generation or editing","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","type":"string"},"data":{"description":"A list of generated image objects","items":{"$ref":"#/components/schemas/XAIGeneratedImage"},"type":"array"},"usage":{"$ref":"#/components/schemas/XAIImageUsage"}},"type":"object"},"XAIImageUsage":{"description":"Usage information for the image generation request","properties":{"cost_in_usd_ticks":{"description":"Accurate cost of this request in USD ticks (10,000,000,000 ticks = 1 USD)","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-image-quality","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-image.json b/router-schemas/xai/grok-imagine-image.json new file mode 100644 index 000000000..9f32433d6 --- /dev/null +++ b/router-schemas/xai/grok-imagine-image.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-image","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-image\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"0a88e171355d"},"paths":{"/v2/models/xai/grok-imagine-image":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-image synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIImageGenerationResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine image models: the `POST /v1/images/generations` document xAI returns, forwarded unchanged EXCEPT for the re-hosted `data[]` entries described below - every other field is xAI's own. This family is DIRECT-RETURN — `routerresult/classification.go` records `{provider: xai, endpoint: /v1/images/generations}` as `ReturnModeDirect` with no poll route — so the body a caller receives is the finished generation on the original call rather than a task handle.\nThe generated images are in `data`, and on THIS surface an entry is addressed by its `url`. Router coerces the outbound `response_format` to `url` for this family and re-hosts every image it can fetch onto Comfy storage, replacing xAI's URL with a signed Comfy one and clearing `b64_json`, so a request that asked for `b64_json` is answered exactly as a `url` request is. `mime_type` names the encoding. The `/proxy/` route reached directly is the one that still answers in the format the caller asked for.\nDurability is per entry, not per response: an image whose re-host fails keeps xAI's own answer — under the coerced `response_format` that is xAI's short-lived URL — rather than a Comfy one, and the rest of the response is unaffected. So one `data` array can mix durable Comfy URLs with expiring partner ones, and a caller that stores or replays this document should not assume every URL in it outlives the call. Such a response IS the answer to the call that produced it, returned and charged exactly as a fully re-hosted one is — but it is replayable only BRIEFLY. A prompt retry carrying the same `Idempotency-Key` — within a few minutes of the original, which is where a connection dropped mid-call puts an SDK's automatic re-send — is answered from the record exactly as any other replay is, because the partner's own link is still alive that soon. A retry after that is answered `409 invalid_input` rather than handed a document whose links may already have expired. The key is consumed either way, so no retry ever re-runs or re-charges; once the replay window has passed, use a new key to run the generation again. A response whose every entry re-hosted cleanly replays normally for the full 24 hours, because a Comfy signed URL is minted with the same lifetime the record is kept for.\n`block_reason` and `usage` are populated WITHOUT any `data` when xAI's input moderation refuses the request, so a caller must key completion off `data` rather than off a `200` alone — which is exactly why the nightly SDK case for this family asserts `data` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"data":[{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated.jpg"},{"mime_type":"image/jpeg","url":"https://example.invalid/xai/grok-imagine-image/generated-2.jpg"}],"usage":{"cost_in_usd_ticks":200000000}}}}}}}}}},"components":{"schemas":{"XAIGeneratedImage":{"description":"A generated image from xAI","properties":{"b64_json":{"description":"A base64-encoded string representation of the generated image in jpeg encoding (if response_format is b64_json)","type":"string"},"mime_type":{"description":"The MIME type of the generated image (e.g. image/png, image/jpeg, image/webp).","type":"string"},"url":{"description":"A url to the generated image (if response_format is url)","type":"string"}},"type":"object"},"XAIImageGenerationResponse":{"description":"Response from xAI image generation or editing","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","type":"string"},"data":{"description":"A list of generated image objects","items":{"$ref":"#/components/schemas/XAIGeneratedImage"},"type":"array"},"usage":{"$ref":"#/components/schemas/XAIImageUsage"}},"type":"object"},"XAIImageUsage":{"description":"Usage information for the image generation request","properties":{"cost_in_usd_ticks":{"description":"Accurate cost of this request in USD ticks (10,000,000,000 ticks = 1 USD)","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-image","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-video-1.5-preview.json b/router-schemas/xai/grok-imagine-video-1.5-preview.json new file mode 100644 index 000000000..7b6cfc1b9 --- /dev/null +++ b/router-schemas/xai/grok-imagine-video-1.5-preview.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-video-1.5-preview","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-video-1.5-preview\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"d015539bc19e"},"paths":{"/v2/models/xai/grok-imagine-video-1.5-preview":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-video-1.5-preview synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIVideoResultResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine video models: the terminal `GET /v1/videos/{request_id}` document, forwarded unchanged EXCEPT for the re-hosted `video.url` described below - every other field is xAI's own. This family is SUBMIT-AND-POLL — the submit answers with an `XAIVideoAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyXAIVideo`), so the body a caller receives is the finished result rather than that handle.\nThe generated video is in `video`. Its `url` is NULLABLE, and `routerpollstate` treats a success whose `video.url` is empty as `success_without_output` rather than as a completed generation.\nThat `url` is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of xAI's own, so it is a Comfy link and not the partner's. Durability is per entry, not per response, exactly as it is on the image family: a video whose re-host could not be performed keeps xAI's own short-lived URL rather than a Comfy one, so a caller that stores or replays this document should not assume the URL outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the video rather than storing it.\n`status` is xAI's own two-value vocabulary — `pending` or `done`, NOT `completed` — and `status`, `model` and `usage` are all populated on a PENDING poll and on an input-moderation refusal alike. A caller must therefore key completion off `video`, which is why the nightly SDK case for this family asserts `video` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"model":"grok-imagine-video","status":"done","usage":{"cost_in_usd_ticks":3500000000},"video":{"duration":4,"respect_moderation":true,"url":"https://example.invalid/xai/grok-imagine-video/generated.mp4"}}}}}}}}}},"components":{"schemas":{"XAIGeneratedVideo":{"description":"A generated video from xAI","properties":{"duration":{"description":"Duration of the generated video in seconds","type":"integer"},"respect_moderation":{"description":"Whether the video generated by the model respects moderation rules","type":"boolean"},"url":{"description":"Download URL for the generated video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps xAI's own short-lived URL instead. NULLABLE: a success whose `url` is empty is not a completed generation. Either way the link expires, so download the video rather than storing the URL.","nullable":true,"type":"string"}},"type":"object"},"XAIVideoResultResponse":{"description":"Response from getting video generation result","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","nullable":true,"type":"string"},"model":{"description":"The model used to generate the video","type":"string"},"status":{"description":"Status of the deferred request: \"pending\" or \"done\"","enum":["pending","done"],"type":"string"},"usage":{"$ref":"#/components/schemas/XAIVideoUsage"},"video":{"$ref":"#/components/schemas/XAIGeneratedVideo"}},"type":"object"},"XAIVideoUsage":{"description":"Usage information for the video generation request","properties":{"cost_in_usd_ticks":{"description":"The cost of this request expressed in USD ticks. One USD cent equals 100,000,000 ticks, so one US dollar equals 10,000,000,000 ticks.\n","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-video-1.5-preview","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-video-1.5.json b/router-schemas/xai/grok-imagine-video-1.5.json new file mode 100644 index 000000000..9fb159896 --- /dev/null +++ b/router-schemas/xai/grok-imagine-video-1.5.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-video-1.5","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-video-1.5\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"d015539bc19e"},"paths":{"/v2/models/xai/grok-imagine-video-1.5":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-video-1.5 synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIVideoResultResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine video models: the terminal `GET /v1/videos/{request_id}` document, forwarded unchanged EXCEPT for the re-hosted `video.url` described below - every other field is xAI's own. This family is SUBMIT-AND-POLL — the submit answers with an `XAIVideoAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyXAIVideo`), so the body a caller receives is the finished result rather than that handle.\nThe generated video is in `video`. Its `url` is NULLABLE, and `routerpollstate` treats a success whose `video.url` is empty as `success_without_output` rather than as a completed generation.\nThat `url` is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of xAI's own, so it is a Comfy link and not the partner's. Durability is per entry, not per response, exactly as it is on the image family: a video whose re-host could not be performed keeps xAI's own short-lived URL rather than a Comfy one, so a caller that stores or replays this document should not assume the URL outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the video rather than storing it.\n`status` is xAI's own two-value vocabulary — `pending` or `done`, NOT `completed` — and `status`, `model` and `usage` are all populated on a PENDING poll and on an input-moderation refusal alike. A caller must therefore key completion off `video`, which is why the nightly SDK case for this family asserts `video` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"model":"grok-imagine-video","status":"done","usage":{"cost_in_usd_ticks":3500000000},"video":{"duration":4,"respect_moderation":true,"url":"https://example.invalid/xai/grok-imagine-video/generated.mp4"}}}}}}}}}},"components":{"schemas":{"XAIGeneratedVideo":{"description":"A generated video from xAI","properties":{"duration":{"description":"Duration of the generated video in seconds","type":"integer"},"respect_moderation":{"description":"Whether the video generated by the model respects moderation rules","type":"boolean"},"url":{"description":"Download URL for the generated video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps xAI's own short-lived URL instead. NULLABLE: a success whose `url` is empty is not a completed generation. Either way the link expires, so download the video rather than storing the URL.","nullable":true,"type":"string"}},"type":"object"},"XAIVideoResultResponse":{"description":"Response from getting video generation result","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","nullable":true,"type":"string"},"model":{"description":"The model used to generate the video","type":"string"},"status":{"description":"Status of the deferred request: \"pending\" or \"done\"","enum":["pending","done"],"type":"string"},"usage":{"$ref":"#/components/schemas/XAIVideoUsage"},"video":{"$ref":"#/components/schemas/XAIGeneratedVideo"}},"type":"object"},"XAIVideoUsage":{"description":"Usage information for the video generation request","properties":{"cost_in_usd_ticks":{"description":"The cost of this request expressed in USD ticks. One USD cent equals 100,000,000 ticks, so one US dollar equals 10,000,000,000 ticks.\n","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-video-1.5","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true} diff --git a/router-schemas/xai/grok-imagine-video.json b/router-schemas/xai/grok-imagine-video.json new file mode 100644 index 000000000..3afd22cdd --- /dev/null +++ b/router-schemas/xai/grok-imagine-video.json @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"xai/grok-imagine-video","description":"The request body Comfy Router accepts for the model \"xai/grok-imagine-video\", and the response body it returns. The INPUT schema is the same schema the server validates a call against before it reaches the provider, so what is published and what is enforced cannot differ. The OUTPUT schema describes the provider's native result document exactly as Router returns it: Router does not validate, narrow or re-envelope the response, so the output schema is descriptive rather than enforced, and Comfy owns no output shape of its own.","version":"d015539bc19e"},"paths":{"/v2/models/xai/grok-imagine-video":{"post":{"operationId":"runRouterModel","summary":"Run xai/grok-imagine-video synchronously.","security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"additionalProperties":true,"description":"This model's input has not been narrowed by Comfy yet. Router forwards the body to the partner unchanged, so the partner's own documentation is authoritative until a schema is authored for this model. Any JSON object is accepted here and by the server's pre-provider validation alike.","type":"object"}}}},"responses":{"200":{"description":"OK - the model's native JSON output, returned unchanged; the schema describes the provider's terminal result document as Router returns it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/XAIVideoResultResponse"}],"description":"Comfy Router output schema for the xAI Grok Imagine video models: the terminal `GET /v1/videos/{request_id}` document, forwarded unchanged EXCEPT for the re-hosted `video.url` described below - every other field is xAI's own. This family is SUBMIT-AND-POLL — the submit answers with an `XAIVideoAsyncResponse` handle carrying `request_id`, and Router polls on the caller's behalf (`routerpollstate/families.go` `FamilyXAIVideo`), so the body a caller receives is the finished result rather than that handle.\nThe generated video is in `video`. Its `url` is NULLABLE, and `routerpollstate` treats a success whose `video.url` is empty as `success_without_output` rather than as a completed generation.\nThat `url` is RE-HOSTED: Router copies the finished video onto Comfy storage and answers a Comfy-signed URL valid for up to 24 hours in place of xAI's own, so it is a Comfy link and not the partner's. Durability is per entry, not per response, exactly as it is on the image family: a video whose re-host could not be performed keeps xAI's own short-lived URL rather than a Comfy one, so a caller that stores or replays this document should not assume the URL outlives the call. 24 hours is the CEILING, not a guarantee: the Comfy link is signed for 24 hours from the moment it is minted, and Router memoises it for 23 hours, so a later poll or an `Idempotency-Key` replay can hand back a link with as little as an hour left. Either way the link expires, so download the video rather than storing it.\n`status` is xAI's own two-value vocabulary — `pending` or `done`, NOT `completed` — and `status`, `model` and `usage` are all populated on a PENDING poll and on an input-moderation refusal alike. A caller must therefore key completion off `video`, which is why the nightly SDK case for this family asserts `video` and nothing else (`testing/e2e/router_sdk/cases.json`).","example":{"model":"grok-imagine-video","status":"done","usage":{"cost_in_usd_ticks":3500000000},"video":{"duration":4,"respect_moderation":true,"url":"https://example.invalid/xai/grok-imagine-video/generated.mp4"}}}}}}}}}},"components":{"schemas":{"XAIGeneratedVideo":{"description":"A generated video from xAI","properties":{"duration":{"description":"Duration of the generated video in seconds","type":"integer"},"respect_moderation":{"description":"Whether the video generated by the model respects moderation rules","type":"boolean"},"url":{"description":"Download URL for the generated video. Router re-hosts the video onto Comfy storage and rewrites this field, so it is normally a Comfy-signed URL valid for up to 24 hours - signed for 24 hours when minted and replayed from a 23-hour memo, so a later poll can hand back one with as little as an hour left. When the re-host could not be performed the field keeps xAI's own short-lived URL instead. NULLABLE: a success whose `url` is empty is not a completed generation. Either way the link expires, so download the video rather than storing the URL.","nullable":true,"type":"string"}},"type":"object"},"XAIVideoResultResponse":{"description":"Response from getting video generation result","properties":{"block_reason":{"description":"If the request was blocked by input moderation, contains the block reason","nullable":true,"type":"string"},"model":{"description":"The model used to generate the video","type":"string"},"status":{"description":"Status of the deferred request: \"pending\" or \"done\"","enum":["pending","done"],"type":"string"},"usage":{"$ref":"#/components/schemas/XAIVideoUsage"},"video":{"$ref":"#/components/schemas/XAIGeneratedVideo"}},"type":"object"},"XAIVideoUsage":{"description":"Usage information for the video generation request","properties":{"cost_in_usd_ticks":{"description":"The cost of this request expressed in USD ticks. One USD cent equals 100,000,000 ticks, so one US dollar equals 10,000,000,000 ticks.\n","type":"integer"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"x-comfy-router-model-id":"xai/grok-imagine-video","x-comfy-input-schema-authored":false,"x-comfy-output-schema-authored":true}