{
  "description": "LibLib AI ControlNet 图生图工具：基于 Canny 边缘检测，将参考图转换为指定风格的图片",
  "task_spec": "请创建一个「LibLib ControlNet 图生图工具」，使用本地 uv 环境。\n\n## 功能需求\n基于 LibLib AI 开放 API，实现 ControlNet Canny 图生图功能。包含三个步骤：图片上传、任务提交、状态轮询。\n\n## 输入参数（POST /generate，JSON Body）\n- image: str — 图片来源，支持两种格式：\n  - Base64 编码的图片数据\n  - 已有的 HTTP 图片 URL\n- prompt: str — 正向提示词\n- negative_prompt: str — 反向提示词，默认 \"lowres, bad anatomy, text, error\"\n- width: int — 输出宽度，默认 512\n- height: int — 输出高度，默认 512\n- steps: int — 采样步数，默认 20\n- cfg_scale: float — CFG Scale，默认 7\n- img_count: int — 生成图片数量，默认 1\n- control_weight: float — ControlNet 权重，默认 1.0\n- preprocessor: int — 预处理器枚举，默认 1（Canny）\n- canny_low: int — Canny 低阈值，默认 100\n- canny_high: int — Canny 高阈值，默认 200\n\n## 输出（JSON）\n- images: list[str] — 生成的图片 URL 列表\n- task_id: str — 任务 ID\n- status: str — 任务状态\n\n## API 认证机制（严格按照以下实现）\n\n### HMAC-SHA1 签名\n```python\nimport hmac, hashlib, base64, uuid, time\n\ndef generate_auth_url(domain, uri, ak, sk):\n    ts = str(int(time.time() * 1000))\n    nonce = uuid.uuid4().hex\n    sign_str = f\"{uri}&{ts}&{nonce}\"\n    dig = hmac.new(sk.encode(), sign_str.encode(), hashlib.sha1).digest()\n    signature = base64.urlsafe_b64encode(dig).rstrip(b\"=\").decode()\n    return f\"{domain}{uri}?AccessKey={ak}&Timestamp={ts}&SignatureNonce={nonce}&Signature={signature}\"\n```\n\n### 环境变量\n- LIBLIBAI_ACCESS_KEY — 从 .env 文件读取\n- LIBLIBAI_SECRET_KEY — 从 .env 文件读取\n- LIBLIBAI_DOMAIN — 默认 https://openapi.liblibai.cloud\n\n## 模型配置（关键：baseType 必须匹配）\n```python\nTEMPLATE_UUID = \"e10adc3949ba59abbe56e057f20f883e\"  # 1.5和XL文生图模板\nCHECKPOINT_ID = \"6320087784014d5f850259e8f49890a1\"  # SD 1.5 官方底模\nCANNY_MODEL_ID = \"7d917ec7e55c5805db737d3b493c91ce\"  # SD 1.5 Canny ControlNet 模型\n```\n注意：ControlNet 模型和 Checkpoint 底模必须是同一基础算法类型（都是 SD 1.5），否则 API 会报错 \"Cn模型baseType不匹配\"。\n\n## 图片上传流程（当输入为 Base64 时执行）\n\n### 第一步：获取 OSS 上传签名\n- 接口：POST /api/generate/upload/signature\n- 请求体：{\"name\": \"文件名\", \"extension\": \"png\"}\n- 返回字段：key, policy, postUrl, xOssDate, xOssExpires, xOssSignature, xOssCredential, xOssSignatureVersion\n\n### 第二步：上传文件到 OSS\n```python\ndata = {\n    'key': sig_data['key'],\n    'policy': sig_data['policy'],\n    'x-oss-date': sig_data['xOssDate'],\n    'x-oss-expires': sig_data['xOssExpires'],\n    'x-oss-signature': sig_data['xOssSignature'],\n    'x-oss-credential': sig_data['xOssCredential'],\n    'x-oss-signature-version': sig_data['xOssSignatureVersion']\n}\nfiles = {'file': ('image.png', file_bytes, 'image/png')}\nresp = requests.post(sig_data['postUrl'], data=data, files=files)\n# 成功返回 204\n```\n\n### 第三步：拼接图片 URL\n- 格式：{postUrl}/{key}\n- 注意：该 URL 浏览器直接访问会返回 403，但可用于 LibLib API 调用\n\n## ControlNet 生图任务提交\n- 接口：POST /api/generate/webui/text2img\n- 请求体：\n```json\n{\n    \"templateUuid\": \"e10adc3949ba59abbe56e057f20f883e\",\n    \"generateParams\": {\n        \"checkPointId\": \"6320087784014d5f850259e8f49890a1\",\n        \"prompt\": \"...\",\n        \"negativePrompt\": \"...\",\n        \"sampler\": 15,\n        \"steps\": 20,\n        \"cfgScale\": 7,\n        \"width\": 512,\n        \"height\": 512,\n        \"imgCount\": 1,\n        \"seed\": -1,\n        \"controlNet\": [{\n            \"unitOrder\": 1,\n            \"sourceImage\": \"图片URL\",\n            \"width\": 512,\n            \"height\": 512,\n            \"preprocessor\": 1,\n            \"annotationParameters\": {\n                \"canny\": {\n                    \"preprocessorResolution\": 512,\n                    \"lowThreshold\": 100,\n                    \"highThreshold\": 200\n                }\n            },\n            \"model\": \"7d917ec7e55c5805db737d3b493c91ce\",\n            \"controlWeight\": 1,\n            \"startingControlStep\": 0,\n            \"endingControlStep\": 1,\n            \"pixelPerfect\": 1,\n            \"controlMode\": 0,\n            \"resizeMode\": 1\n        }]\n    }\n}\n```\n- 返回：{\"code\": 0, \"data\": {\"generateUuid\": \"任务ID\"}}\n\n## 任务状态轮询\n- 接口：POST /api/generate/record/query\n- 请求体：{\"generateUuid\": \"任务ID\"}\n- 返回 data.generateStatus：\n  - 1 = 排队中\n  - 2 = 生成中\n  - 5 = 成功（从 data.images[].imageUrl 获取结果图片）\n  - 6/7 = 失败\n- 轮询间隔：5 秒，超时：5 分钟\n\n## 技术要求\n1. 使用 uv 环境，项目名 liblibai_controlnet\n2. 核心依赖：requests, python-dotenv\n3. HTTP 接口：FastAPI + uvicorn，端口通过 --port 参数指定\n4. 路由：\n   - POST /generate（生图）\n   - GET /health（健康检查，返回 {\"status\":\"ok\"}）\n5. 编写自测脚本 tests/test_generate.py：使用一个公开图片 URL 调用生图接口，验证返回格式正确\n6. 自测通过后注册，tool_id = \"liblibai_controlnet\"，runtime_type = \"local\"，host_dir 填项目目录绝对路径\n\n## 注意\n- 这是 uv 本地项目，不需要 Docker\n- 测试阶段只验证核心逻辑（单元测试），不要启动 HTTP 服务器\n- 先跑通自测脚本再注册，确保核心逻辑正确\n- pixelPerfect、controlMode、resizeMode 必须是整数类型（1/0），不能是布尔值\n- 图片上传时 file 字段必须放在表单最后"
}
