ComfyPilotComfyPilot
文档 / 开发 / 通过 Comfy Router 使用 FLUX.1 Kontext

通过 Comfy Router 使用 FLUX.1 Kontext

通过 Comfy Router 调用 FLUX.1 Kontext Pro 和 Kontext Max 的 Python、TypeScript 和 cURL 代码片段,以及请求字段和结果形状

开发 code 更新于 2026-10-11

查看官方原文

通过 Comfy Router 使用 FLUX.1 Kontext

通过 Comfy Router 调用 FLUX.1 Kontext Pro 和 Kontext Max 的 Python、TypeScript 和 cURL 代码片段,以及请求字段和结果形状

FLUX.1 Kontext 的 API 参考。FLUX.1 Kontext 是 Black Forest Labs 的指令驱动图像编辑模型:发送一张图像和一条文本指令,即可取回编辑后的图像,同时场景的其余部分保持不变。

输入: 图像, 文本 · 输出: 图像

快速开始

在你的 Comfy 工作区中创建密钥,并将其导出为 COMFY_API_KEY。Python 和 TypeScript 代码片段使用 Comfy SDK(pip install comfy-sdk 和 npm install @comfyorg/sdk);cURL 代码片段是通过原始 HTTP 发出的同一调用。

选择你要调用的模型。下面的全部内容,从代码片段到 schema 和示例,都随你的选择而变化。

[tab] Kontext Pro 模型 ID: bfl/flux-kontext-pro 端点: POST https://api.comfy.org/v2/models/bfl/flux-kontext-pro > [tab] 等待结果 > > ``python Python theme={null} > import asyncio > import base64 > from comfy_sdk import AsyncComfy > > with open("input.jpg", "rb") as f: > input_image = base64.b64encode(f.read()).decode() > > # 从环境中读取 COMFY_API_KEY。 > # SDK 会自动创建幂等键,并在自动重试时复用该键。 > async def main(): > async with AsyncComfy() as client: > result = await client.models.run( > "bfl/flux-kontext-pro", > { > "prompt": "replace the background with a sunlit beach, keep the subject unchanged", > "input_image": input_image, > "aspect_ratio": "1:1", > }, > ) > > print("image:", result["result"]["sample"]) > > asyncio.run(main()) > ` > > `typescript TypeScript theme={null} > import { comfy } from "@comfyorg/sdk"; > import { readFile } from "node:fs/promises"; > > const inputImage = (await readFile("input.jpg")).toString("base64"); > > // 从环境中读取 COMFY_API_KEY。 > // SDK 会自动创建幂等键,并在自动重试时复用该键。 > type Result = { result: { sample: string } }; > const result = await comfy.models.run<Result>("bfl/flux-kontext-pro", { > prompt: "replace the background with a sunlit beach, keep the subject unchanged", > input_image: inputImage, > aspect_ratio: "1:1", > }); > if (result.kind !== "json") throw new Error("expected a JSON result"); > > console.log("image:", result.data.result.sample); > ` > > `bash cURL theme={null} > INPUT_IMAGE=$(base64 < input.jpg | tr -d '\n') > > curl https://api.comfy.org/v2/models/bfl/flux-kontext-pro \ > -H "X-API-Key: $COMFY_API_KEY" \ > -H "Idempotency-Key: $(uuidgen)" \ > -H "Content-Type: application/json" \ > -d "{\"prompt\": \"replace the background with a sunlit beach, keep the subject unchanged\", \"input_image\": \"$INPUT_IMAGE\", \"aspect_ratio\": \"1:1\"}" > ` > [tab] 排队并稍后收集 > > 将相同的请求体发送到 POST https://api.comfy.org/v2/models/bfl/flux-kontext-pro/requests。运行被受理后,Router 会立即返回 201 和 request_id;结果就绪后,可以从本进程或其他进程收集。状态、取消与收集的细节见 [队列投递](https://docs.comfy.org/zh/development/comfy-router/queue.md)。 > > `python Python theme={null} > import asyncio > import base64 > from comfy_sdk import AsyncComfy > > with open("input.jpg", "rb") as f: > input_image = base64.b64encode(f.read()).decode() > > # 从环境中读取 COMFY_API_KEY。 > # 每次 submit() 调用都会生成自己的 Idempotency-Key,并在自动重试时复用该键。 > async def main(): > async with AsyncComfy() as client: > handle = await client.models.submit( > "bfl/flux-kontext-pro", > { > "prompt": "replace the background with a sunlit beach, keep the subject unchanged", > "input_image": input_image, > "aspect_ratio": "1:1", > }, > ) > print("request_id:", handle.request_id) # 配合模型 ID,就是另一个进程所需的全部信息 > > # 轮询直到请求完成,每次等待服务器指定的 Retry-After 时长。 > async for update in handle.iter_events(): > print(update.status, update.queue_position) > > # 提供商自己的负载,与 models.run() 返回的值相同。 > # 失败或已取消的请求会在此处抛出类型化的 Router 错误。 > result = await handle.get() > > print("image:", result["result"]["sample"]) > > asyncio.run(main()) > ` > > `typescript TypeScript theme={null} > import { comfy } from "@comfyorg/sdk"; > import { readFile } from "node:fs/promises"; > > const inputImage = (await readFile("input.jpg")).toString("base64"); > > // 从环境中读取 COMFY_API_KEY。 > // 每次 submit() 调用都会生成自己的 Idempotency-Key,并在自动重试时复用该键。 > type Result = { result: { sample: string } }; > const handle = await comfy.models.submit<Result>("bfl/flux-kontext-pro", { > prompt: "replace the background with a sunlit beach, keep the subject unchanged", > input_image: inputImage, > aspect_ratio: "1:1", > }); > console.log("requestId:", handle.requestId); // 配合模型 ID,就是另一个进程所需的全部信息 > > // 轮询直到请求完成,每次等待服务器指定的 Retry-After 时长。 > for await (const update of handle.events()) { > console.log(update.status, update.queuePosition); > } > > // 与 models.run() 返回的结果相同。失败或已取消的请求会在此处被拒绝。 > const result = await handle.get(); > if (result.kind !== "json") throw new Error("expected a JSON result"); > > console.log("image:", result.data.result.sample); > ` > > `bash cURL theme={null} > INPUT_IMAGE=$(base64 < input.jpg | tr -d '\n') > > # 1. 提交。Router 返回 201,并附带 request_id、status_url、response_url 和 cancel_url。 > curl https://api.comfy.org/v2/models/bfl/flux-kontext-pro/requests \ > -H "X-API-Key: $COMFY_API_KEY" \ > -H "Idempotency-Key: $(uuidgen)" \ > -H "Content-Type: application/json" \ > -d "{\"prompt\": \"replace the background with a sunlit beach, keep the subject unchanged\", \"input_image\": \"$INPUT_IMAGE\", \"aspect_ratio\": \"1:1\"}" > > # 2. 轮询直到 status 为 COMPLETED,每次等待响应指定的 Retry-After 秒数。 > REQUEST_ID="<request_id from the 201 body>" > curl -i https://api.comfy.org/v2/models/bfl/flux-kontext-pro/requests/$REQUEST_ID/status \ > -H "X-API-Key: $COMFY_API_KEY" > > # 3. 收集。返回 200 时携带模型的原生输出;仍在运行时返回 202 和状态正文。 > curl https://api.comfy.org/v2/models/bfl/flux-kontext-pro/requests/$REQUEST_ID \ > -H "X-API-Key: $COMFY_API_KEY" > ` ## Schema ### 输入 **aspect\_ratio** string 输出的宽高比,介于 21:9 和 9:21 之间,例如 16:9。当提供了输入图像时,默认为输入图像的宽高比,否则为 1:1。 **input\_image** string 要编辑的图像,为 base64 编码的图像或 http(s) URL。可选;不提供时,模型仅根据提示词生成。 **input\_image\_2** string 额外的参考图像,base64 编码或 http(s) URL(实验性多参考)。 **input\_image\_3** string 额外的参考图像,base64 编码或 http(s) URL(实验性多参考)。 **input\_image\_4** string 额外的参考图像,base64 编码或 http(s) URL(实验性多参考)。 **output\_format** string (default: "png") 输出图像格式。 可能的值:jpeg、png、webp **prompt** string *required* 描述要应用于 input\_image 的编辑操作,或在未提供 input\_image 时要生成的图像的文本提示词。 **prompt\_upsampling** boolean (default: false) 是否对提示词进行上采样。如果启用,提示词会被自动修改,以便进行更具创造性的生成。 **safety\_tolerance** integer (default: 2) 输入和输出审核的阈值级别,介于 0(最严格)和 6(最宽松)之间。 范围:0 到 6 **seed** integer 用于可复现性的可选种子。省略时使用随机种子。 **webhook\_secret** string 用于 webhook 签名验证的可选密钥。 **webhook\_url** string (uri) 接收 webhook 通知的 URL。 格式:uri 由 Router 在 GET /v2/models/bfl/flux-kontext-pro/openapi.json 提供的 schema 生成,这也是请求到达提供商之前 Router 用于验证调用的同一份文档。 ### 输出 **cost** number 提供商报告的以积分计的费用,任务变为 Ready 后填充。 格式:float **id** string *required* BFL 任务标识符。 **progress** number BFL 报告的可选生成进度。 范围:0 到 1 格式:float **result** object *required* 已完成的生成。此处不可为空:该组件的 required 条目承诺 200 会携带结果,而可为空的 result 会将其降格为仅检查键是否存在。 **result.cost** number 提供商报告的生成费用。这是 BFL 的数值,而非 Comfy 的收费。 格式:double **result.duration** number 提供商报告的生成时长,以秒为单位。 格式:double **result.end\_time** number 提供商报告的生成完成时间,为自 Unix 纪元起的秒数。原因与 start_time 相同,使用 double。 格式:double **result.prompt** string 经过任何提示词上采样后,生成实际运行的提示词。 **result.sample** string (uri) 已生成资产的签名 URL。Router 会将资产重新托管到 Comfy 存储上并重写此字段,因此它通常是有效的 Comfy 托管 URL,有效期最长 24 小时:签发时签名为 24 小时,并从 23 小时的缓存中重放,因此稍后的轮询可能返回仅剩一小时有效期的 URL;如果某个叶子节点无法执行重新托管,则会保留 BFL 自己的短期交付 URL,视频约为两小时,图像约为十分钟。无论哪种情况,链接都会过期,因此请下载资产,而不要存储 URL。 格式:uri **result.seed** integer 生成所使用的种子,无论是由用户提供还是由提供商选择。声明为 int64 是因为 BFL 会返回大于 2^31 的种子(例如 2784347701),而未格式化的 integer 在许多 SDK 生成器中会生成 32 位字段。 格式:int64 **result.start\_time** number 提供商报告的生成开始时间,为自 Unix 纪元起的秒数。使用 double 而非 float:在当前的纪元值附近,float32 的间隔约为 128 秒,这会把整次生成的时间跨度压缩为单个解码值。 格式:double **status** string *required* 任务状态:Pending、Reasoning、Generating、Ready、Request Moderated、Content Moderated、Error 或 Task not found。 ## 示例 ### 输入 `json theme={null} { "prompt": "replace the background with a sunlit beach, keep the subject unchanged", "input_image": "<base64 of input.jpg>", "aspect_ratio": "1:1" } ` ### 输出 `json theme={null} { "id": "0a1b2c3d-...", "status": "Ready", "result": { "sample": "https://.../out.png", "prompt": "replace the background with a sunlit beach, keep the subject unchanged", "seed": 1234567890 } } `` 该 URL 是临时的。如果需要保留图像,请及时下载。
[tab] Kontext Max 模型 ID: bfl/flux-kontext-max 端点: POST https://api.comfy.org/v2/models/bfl/flux-kontext-max > [tab] 等待结果 > > ``python Python theme={null} > import asyncio > import base64 > from comfy_sdk import AsyncComfy > > with open("input.jpg", "rb") as f: > input_image = base64.b64encode(f.read()).decode() > > # 从环境中读取 COMFY_API_KEY。 > # SDK 会自动创建幂等键,并在自动重试时复用该键。 > async def main(): > async with AsyncComfy() as client: > result = await client.models.run( > "bfl/flux-kontext-max", > { > "prompt": "replace the background with a sunlit beach, keep the subject unchanged", > "input_image": input_image, > "aspect_ratio": "1:1", > }, > ) > > print("image:", result["result"]["sample"]) > > asyncio.run(main()) > ` > > `typescript TypeScript theme={null} > import { comfy } from "@comfyorg/sdk"; > import { readFile } from "node:fs/promises"; > > const inputImage = (await readFile("input.jpg")).toString("base64"); > > // 从环境中读取 COMFY_API_KEY。 > // SDK 会自动创建幂等键,并在自动重试时复用该键。 > type Result = { result: { sample: string } }; > const result = await comfy.models.run<Result>("bfl/flux-kontext-max", { > prompt: "replace the background with a sunlit beach, keep the subject unchanged", > input_image: inputImage, > aspect_ratio: "1:1", > }); > if (result.kind !== "json") throw new Error("expected a JSON result"); > > console.log("image:", result.data.result.sample); > ` > > `bash cURL theme={null} > INPUT_IMAGE=$(base64 < input.jpg | tr -d '\n') > > curl https://api.comfy.org/v2/models/bfl/flux-kontext-max \ > -H "X-API-Key: $COMFY_API_KEY" \ > -H "Idempotency-Key: $(uuidgen)" \ > -H "Content-Type: application/json" \ > -d "{\"prompt\": \"replace the background with a sunlit beach, keep the subject unchanged\", \"input_image\": \"$INPUT_IMAGE\", \"aspect_ratio\": \"1:1\"}" > ` > [tab] 排队并稍后收集 > > 将相同的请求体发送到 POST https://api.comfy.org/v2/models/bfl/flux-kontext-max/requests。运行被受理后,Router 会立即返回 201 和 request_id;结果就绪后,可以从本进程或其他进程收集。状态、取消与收集的细节见 [队列投递](https://docs.comfy.org/zh/development/comfy-router/queue.md)。 > > `python Python theme={null} > import asyncio > import base64 > from comfy_sdk import AsyncComfy > > with open("input.jpg", "rb") as f: > input_image = base64.b64encode(f.read()).decode() > > # 从环境中读取 COMFY_API_KEY。 > # 每次 submit() 调用都会生成自己的 Idempotency-Key,并在自动重试时复用该键。 > async def main(): > async with AsyncComfy() as client: > handle = await client.models.submit( > "bfl/flux-kontext-max", > { > "prompt": "replace the background with a sunlit beach, keep the subject unchanged", > "input_image": input_image, > "aspect_ratio": "1:1", > }, > ) > print("request_id:", handle.request_id) # 配合模型 ID,就是另一个进程所需的全部信息 > > # 轮询直到请求完成,每次等待服务器指定的 Retry-After 时长。 > async for update in handle.iter_events(): > print(update.status, update.queue_position) > > # 提供商自己的负载,与 models.run() 返回的值相同。 > # 失败或已取消的请求会在此处抛出类型化的 Router 错误。 > result = await handle.get() > > print("image:", result["result"]["sample"]) > > asyncio.run(main()) > ` > > `typescript TypeScript theme={null} > import { comfy } from "@comfyorg/sdk"; > import { readFile } from "node:fs/promises"; > > const inputImage = (await readFile("input.jpg")).toString("base64"); > > // 从环境中读取 COMFY_API_KEY。 > // 每次 submit() 调用都会生成自己的 Idempotency-Key,并在自动重试时复用该键。 > type Result = { result: { sample: string } }; > const handle = await comfy.models.submit<Result>("bfl/flux-kontext-max", { > prompt: "replace the background with a sunlit beach, keep the subject unchanged", > input_image: inputImage, > aspect_ratio: "1:1", > }); > console.log("requestId:", handle.requestId); // 配合模型 ID,就是另一个进程所需的全部信息 > > // 轮询直到请求完成,每次等待服务器指定的 Retry-After 时长。 > for await (const update of handle.events()) { > console.log(update.status, update.queuePosition); > } > > // 与 models.run() 返回的结果相同。失败或已取消的请求会在此处被拒绝。 > const result = await handle.get(); > if (result.kind !== "json") throw new Error("expected a JSON result"); > > console.log("image:", result.data.result.sample); > ` > > `bash cURL theme={null} > INPUT_IMAGE=$(base64 < input.jpg | tr -d '\n') > > # 1. 提交。Router 返回 201,并附带 request_id、status_url、response_url 和 cancel_url。 > curl https://api.comfy.org/v2/models/bfl/flux-kontext-max/requests \ > -H "X-API-Key: $COMFY_API_KEY" \ > -H "Idempotency-Key: $(uuidgen)" \ > -H "Content-Type: application/json" \ > -d "{\"prompt\": \"replace the background with a sunlit beach, keep the subject unchanged\", \"input_image\": \"$INPUT_IMAGE\", \"aspect_ratio\": \"1:1\"}" > > # 2. 轮询直到 status 为 COMPLETED,每次等待响应指定的 Retry-After 秒数。 > REQUEST_ID="<request_id from the 201 body>" > curl -i https://api.comfy.org/v2/models/bfl/flux-kontext-max/requests/$REQUEST_ID/status \ > -H "X-API-Key: $COMFY_API_KEY" > > # 3. 收集。返回 200 时携带模型的原生输出;仍在运行时返回 202 和状态正文。 > curl https://api.comfy.org/v2/models/bfl/flux-kontext-max/requests/$REQUEST_ID \ > -H "X-API-Key: $COMFY_API_KEY" > ` ## Schema ### 输入 **aspect\_ratio** string 输出的宽高比,介于 21:9 和 9:21 之间,例如 16:9。当提供了输入图像时,默认为输入图像的宽高比,否则为 1:1。 **input\_image** string 要编辑的图像,为 base64 编码的图像或 http(s) URL。可选;不提供时,模型仅根据提示词生成。 **input\_image\_2** string 额外的参考图像,base64 编码或 http(s) URL(实验性多参考)。 **input\_image\_3** string 额外的参考图像,base64 编码或 http(s) URL(实验性多参考)。 **input\_image\_4** string 额外的参考图像,base64 编码或 http(s) URL(实验性多参考)。 **output\_format** string (default: "png") 输出图像格式。 可能的值:jpeg、png、webp **prompt** string *required* 描述要应用于 input\_image 的编辑操作,或在未提供 input\_image 时要生成的图像的文本提示词。 **prompt\_upsampling** boolean (default: false) 是否对提示词进行上采样。如果启用,提示词会被自动修改,以便进行更具创造性的生成。 **safety\_tolerance** integer (default: 2) 输入和输出审核的阈值级别,介于 0(最严格)和 6(最宽松)之间。 范围:0 到 6 **seed** integer 用于可复现性的可选种子。省略时使用随机种子。 **webhook\_secret** string 用于 webhook 签名验证的可选密钥。 **webhook\_url** string (uri) 接收 webhook 通知的 URL。 格式:uri 由 Router 在 GET /v2/models/bfl/flux-kontext-max/openapi.json 提供的 schema 生成,这也是请求到达提供商之前 Router 用于验证调用的同一份文档。 ### 输出 **cost** number 提供商报告的以积分计的费用,任务变为 Ready 后填充。 格式:float **id** string *required* BFL 任务标识符。 **progress** number BFL 报告的可选生成进度。 范围:0 到 1 格式:float **result** object *required* 已完成的生成。此处不可为空:该组件的 required 条目承诺 200 会携带结果,而可为空的 result 会将其降格为仅检查键是否存在。 **result.cost** number 提供商报告的生成费用。这是 BFL 的数值,而非 Comfy 的收费。 格式:double **result.duration** number 提供商报告的生成时长,以秒为单位。 格式:double **result.end\_time** number 提供商报告的生成完成时间,为自 Unix 纪元起的秒数。原因与 start_time 相同,使用 double。 格式:double **result.prompt** string 经过任何提示词上采样后,生成实际运行的提示词。 **result.sample** string (uri) 已生成资产的签名 URL。Router 会将资产重新托管到 Comfy 存储上并重写此字段,因此它通常是有效的 Comfy 托管 URL,有效期最长 24 小时:签发时签名为 24 小时,并从 23 小时的缓存中重放,因此稍后的轮询可能返回仅剩一小时有效期的 URL;如果某个叶子节点无法执行重新托管,则会保留 BFL 自己的短期交付 URL,视频约为两小时,图像约为十分钟。无论哪种情况,链接都会过期,因此请下载资产,而不要存储 URL。 格式:uri **result.seed** integer 生成所使用的种子,无论是由用户提供还是由提供商选择。声明为 int64 是因为 BFL 会返回大于 2^31 的种子(例如 2784347701),而未格式化的 integer 在许多 SDK 生成器中会生成 32 位字段。 格式:int64 **result.start\_time** number 提供商报告的生成开始时间,为自 Unix 纪元起的秒数。使用 double 而非 float:在当前的纪元值附近,float32 的间隔约为 128 秒,这会把整次生成的时间跨度压缩为单个解码值。 格式:double **status** string *required* 任务状态:Pending、Reasoning、Generating、Ready、Request Moderated、Content Moderated、Error 或 Task not found。 ## 示例 ### 输入 `json theme={null} { "prompt": "replace the background with a sunlit beach, keep the subject unchanged", "input_image": "<base64 of input.jpg>", "aspect_ratio": "1:1" } ` ### 输出 `json theme={null} { "id": "0a1b2c3d-...", "status": "Ready", "result": { "sample": "https://.../out.png", "prompt": "replace the background with a sunlit beach, keep the subject unchanged", "seed": 1234567890 } } `` 该 URL 是临时的。如果需要保留图像,请及时下载。

发布前须知

SDK 会生成 Idempotency-Key 并在自动重试中复用它。手动重试时,请复用原始 key。Router 最长可保持连接 10 分钟。

请求失败时,Router 会发送 X-Comfy-Error-Type 响应头说明原因。422 表示 Router 在调用提供商之前就拒绝了输入,413 表示请求体超出了 Router 可接受的大小。已生成的资源请及时下载,因为结果 URL 会过期。

上文任何字段描述中提到的尺寸限制,都是提供商对该字段自身的限定,引自提供商的规范。Router 会对整个请求体另行设置上限,base64 编码的媒体内容也计入其中:参见请求体大小。

本页记录的是通过 Comfy Router 调用的某一个合作伙伴模型。同一个 comfy-sdk / @comfyorg/sdk 包还提供第二个客户端,用于在 Comfy Cloud 上运行完整的 ComfyUI 工作流图:Comfy(api_key=...) / new Comfy({ apiKey }),并带有 client.workflows、client.assets 和 client.jobs。请参阅 Comfy SDKs。

[card] 请求头 身份验证、幂等性、请求 ID、错误分类、重试节奏、消费限额。
[card] 使用 Router API 模型发现、验证错误、重试与计费。
[card] 限制 Router 目前不支持的功能,以及替代方案。