Przeglądaj źródła

add tools: flux,seedream,liblibai,nanobanana

guantao 16 godzin temu
rodzic
commit
652ee2a99d
100 zmienionych plików z 5994 dodań i 320 usunięć
  1. 8 2
      .env
  2. 720 0
      data/liblibai_controlnet_models.json
  3. 0 0
      data/liblibai_controlnet_models.md
  4. 645 0
      data/parse_models.py
  5. 399 157
      data/registry.json
  6. 781 0
      data/registry_old.json
  7. 79 20
      data/sources.json
  8. 1 0
      pyproject.toml
  9. 1 1
      src/tool_agent/__main__.py
  10. BIN
      src/tool_agent/__pycache__/__init__.cpython-312.pyc
  11. BIN
      src/tool_agent/__pycache__/__main__.cpython-312.pyc
  12. BIN
      src/tool_agent/__pycache__/config.cpython-312.pyc
  13. BIN
      src/tool_agent/__pycache__/models.cpython-312.pyc
  14. BIN
      src/tool_agent/registry/__pycache__/__init__.cpython-312.pyc
  15. BIN
      src/tool_agent/registry/__pycache__/registry.cpython-312.pyc
  16. BIN
      src/tool_agent/router/__pycache__/__init__.cpython-312.pyc
  17. BIN
      src/tool_agent/router/__pycache__/agent.cpython-312.pyc
  18. BIN
      src/tool_agent/router/__pycache__/dispatcher.cpython-312.pyc
  19. BIN
      src/tool_agent/router/__pycache__/server.cpython-312.pyc
  20. BIN
      src/tool_agent/router/__pycache__/status.cpython-312.pyc
  21. 14 0
      src/tool_agent/router/dispatcher.py
  22. 57 0
      src/tool_agent/router/server.py
  23. 32 0
      summarize.py
  24. 17 0
      summarize2.py
  25. 16 0
      summarize3.py
  26. 1 0
      t2.jpg
  27. 1 0
      test.jpg
  28. 135 0
      tests/call_nano_banana.py
  29. BIN
      tests/features/background_asset/background_bokeh_img2.png
  30. BIN
      tests/features/background_asset/background_green_img1.png
  31. BIN
      tests/features/background_asset/background_green_img4.png
  32. 62 0
      tests/features/background_asset/mapping.json
  33. BIN
      tests/features/character_asset/character_ref_back.png
  34. BIN
      tests/features/character_asset/character_ref_img1.png
  35. BIN
      tests/features/character_asset/character_ref_kneel.png
  36. BIN
      tests/features/character_asset/character_ref_main.png
  37. BIN
      tests/features/character_asset/character_ref_side.png
  38. 65 0
      tests/features/character_asset/mapping.json
  39. 2 0
      tests/features/color_scheme/color_scheme.json
  40. 150 0
      tests/features/color_scheme/color_scheme_complete.json
  41. BIN
      tests/features/color_scheme/color_scheme_visual.png
  42. BIN
      tests/features/color_scheme/color_swatch.png
  43. 132 0
      tests/features/color_scheme/img_1_colors.json
  44. BIN
      tests/features/color_scheme/img_1_palette.png
  45. 132 0
      tests/features/color_scheme/img_2_colors.json
  46. BIN
      tests/features/color_scheme/img_2_palette.png
  47. 132 0
      tests/features/color_scheme/img_3_colors.json
  48. BIN
      tests/features/color_scheme/img_3_palette.png
  49. 132 0
      tests/features/color_scheme/img_4_colors.json
  50. BIN
      tests/features/color_scheme/img_4_palette.png
  51. 132 0
      tests/features/color_scheme/img_5_colors.json
  52. BIN
      tests/features/color_scheme/img_5_palette.png
  53. 56 0
      tests/features/color_scheme/mapping.json
  54. BIN
      tests/features/depth_map/depth_img_1.png
  55. BIN
      tests/features/depth_map/depth_img_2.png
  56. BIN
      tests/features/depth_map/depth_img_3.png
  57. BIN
      tests/features/depth_map/depth_img_4.png
  58. BIN
      tests/features/depth_map/depth_img_5.png
  59. 79 0
      tests/features/depth_map/mapping.json
  60. BIN
      tests/features/easel_asset/easel_blank_canvas_img4.png
  61. 57 0
      tests/features/easel_asset/mapping.json
  62. BIN
      tests/features/edge_map/img_1_canny.png
  63. BIN
      tests/features/edge_map/img_2_canny.png
  64. BIN
      tests/features/edge_map/img_3_canny.png
  65. BIN
      tests/features/edge_map/img_4_canny.png
  66. BIN
      tests/features/edge_map/img_5_canny.png
  67. 45 0
      tests/features/edge_map/mapping.json
  68. 155 0
      tests/features/lighting_bokeh/lighting_analysis.json
  69. 28 0
      tests/features/lighting_bokeh/lighting_img_2.json
  70. 26 0
      tests/features/lighting_bokeh/lighting_img_3.json
  71. 27 0
      tests/features/lighting_bokeh/lighting_img_5.json
  72. BIN
      tests/features/lighting_bokeh/lighting_visual.png
  73. 49 0
      tests/features/lighting_bokeh/mapping.json
  74. 79 0
      tests/features/palette_asset/mapping.json
  75. BIN
      tests/features/palette_asset/palette_impasto_img1_v2.png
  76. BIN
      tests/features/pose_skeleton/img_1_openpose.png
  77. BIN
      tests/features/pose_skeleton/img_2_openpose.png
  78. BIN
      tests/features/pose_skeleton/img_3_openpose.png
  79. BIN
      tests/features/pose_skeleton/img_4_openpose.png
  80. 99 0
      tests/features/pose_skeleton/mapping.json
  81. 231 0
      tests/features/validation_report.md
  82. 209 0
      tests/features/validation_summary.md
  83. 194 0
      tests/features/τ┤áµ¥ÉΘ¬îΦ»üµèÑσæè.md
  84. 57 0
      tests/find_keys.py
  85. BIN
      tests/output/flux_output_1775647098_0.png
  86. BIN
      tests/output/openpose_result_1775635386_0.png
  87. BIN
      tests/output/seedream_output_1775647134_0.png
  88. BIN
      tests/output/workflow_result_1775635574_0.png
  89. BIN
      tests/output/workflow_result_1775635614_0.png
  90. BIN
      tests/output/workflow_result_1775635883_0.png
  91. BIN
      tests/output/workflow_result_1775635913_0.png
  92. BIN
      tests/output/workflow_result_1775638093_0.png
  93. BIN
      tests/output/workflow_result_1775638649_0.png
  94. BIN
      tests/output/workflow_result_1775638711_0.png
  95. 6 5
      tests/test_liblibai_tool.py
  96. 466 0
      tests/test_liblibai_workflows.py
  97. 126 0
      tests/test_local_openpose.py
  98. 100 0
      tests/test_router_api.py
  99. 59 135
      tests/upload.py
  100. 0 0
      tmp_api1.json

+ 8 - 2
.env

@@ -1,6 +1,8 @@
 QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
 QWEN_API_KEY=sk-9453c827b9e14108b53d2b30ef7c75fe
-GEMINI_API_KEY=AIzaSyBcERQpAUpTaibOI7VYZ6tq6NYI05vLPco
+GEMINI_API_KEY=AIzaSyBHh6WDjMfWtZiJgISVLvrlHeS1iTGjkio
+GEMINI_API_BASE=https://generativelanguage.googleapis.com/v1beta
+
 ANTHROPIC_API_KEY=sk-90286f71a5aac6abbf1736d3f981d05d7dbf012a0e6df9d156df1627dd4eb38e
 ANTHROPIC_BASE_URL=https://imds.ai/
 
@@ -11,4 +13,8 @@ LIBLIBAI_SECRET_KEY=7guKoYyckYNMMswTocV9QIf2Ks7iUn52
 
 # RunComfy 配置
 RUNCOMFY_USER_ID=93325160-df5c-4ab8-9b2d-3f1018e7ced8
-API_TOKEN=fc0f2bdc-6ecf-4abe-8902-9be475894283
+API_TOKEN=fc0f2bdc-6ecf-4abe-8902-9be475894283
+
+
+APIYI_BASE_URL=https://api.apiyi.com/v1
+APIYI_KEY=sk-6igJkYBdNGZln2wxB8525235C1F4412dB4449eB7B8F17a2d

+ 720 - 0
data/liblibai_controlnet_models.json

@@ -0,0 +1,720 @@
+{
+    "线稿类": {
+        "Canny(硬边缘)": [
+            {
+                "model_name": "control_v11p_sd15_canny",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "7d917ec7e55c5805db737d3b493c91ce"
+            },
+            {
+                "model_name": "t2iadapter_canny_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "a2c41c4e97944f3aa71f913bdc45b1ca"
+            },
+            {
+                "model_name": "t2iadapter_canny_sd15v2",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "c04144bcf017232483181cd8607097c2"
+            },
+            {
+                "model_name": "diffusers_xl_canny_full",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "56de5edadb6f2891aff05ff078dc0470"
+            },
+            {
+                "model_name": "diffusers_xl_canny_mid",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "efb97e9d8c237573298c3a5a7869b89c"
+            },
+            {
+                "model_name": "diffusers_xl_canny_small",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "dccde738064e9748f93b48ec5868968e"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_canny",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "5242e3d18cc18689bd8af11dd2d675c1"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_canny_anime",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "4f3e1cfe79f87496ec69a37826c3afeb"
+            },
+            {
+                "model_name": "sai_xl_canny_128lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "63c7f2c6c354336513831aa522d7e0f4"
+            },
+            {
+                "model_name": "sai_xl_canny_256lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "5bf551f53651764cad56363e17900d87"
+            },
+            {
+                "model_name": "t2i-adapter_diffusers_xl_canny",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "618390ab2957a422612cb2ba92a2788f"
+            },
+            {
+                "model_name": "t2i-adapter_xl_canny",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "7cd56501c336c1edba78430355c9d081"
+            },
+            {
+                "model_name": "xinsir_controlnet-canny-sdxl_V2",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "b6806516962f4e1599a93ac4483c3d23"
+            },
+            {
+                "model_name": "XLabs-flux-canny-controlnet_v3",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "017997cd6ba44c4dbe8f60e0a26cd0df"
+            },
+            {
+                "model_name": "InstantX-FLUX.1-dev-Controlnet-Union-Pro",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "13c1e1b96ba64f9cbb2b54f89b5fe873"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-Controlnet-Union",
+                "base_algorithm": "Qwen Image",
+                "uuid": "5b5f21d2b80445598db19e924bd3a409"
+            }
+        ],
+        "SoftEdge(软边缘)": [
+            {
+                "model_name": "control_v11p_sd15_softedge",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "0929722d9047ec6498a50ff5d1081629"
+            },
+            {
+                "model_name": "sargezt_xl_softedge",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "dda1a0c480bfab9833d9d9a1e4a71fff"
+            },
+            {
+                "model_name": "controlnet-sd-xl-1.0-softedge-dexined",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "37bddde3d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "mistoLine_softedge_sdxl_fp16",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "4f6726be104a432f8039b018c92ed4bf"
+            },
+            {
+                "model_name": "mistoLine_rank256",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "83286d0e66a845c58f7d23442f9dedf9"
+            },
+            {
+                "model_name": "XLabs-flux-hed-controlnet_v3",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "6c4d620df3644514903b8189735c6ae9"
+            },
+            {
+                "model_name": "F.1_mistoline_dev_v1",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "3e6860a3b9444f25ae07d9c1b5d1ba9e"
+            },
+            {
+                "model_name": "InstantX-FLUX.1-dev-Controlnet-Union-Pro",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "13c1e1b96ba64f9cbb2b54f89b5fe873"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-Controlnet-Union",
+                "base_algorithm": "Qwen Image",
+                "uuid": "5b5f21d2b80445598db19e924bd3a409"
+            }
+        ],
+        "MLSD(直线)": [
+            {
+                "model_name": "control_v11p_sd15_mlsd",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "7168cece6a0d491375aa1753ff3bdc21"
+            }
+        ],
+        "Scribble/Sketch(涂鸦/草图)": [
+            {
+                "model_name": "control_v11p_sd15_scribble",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "fe57911f7ba1b84eb27f1e1ecead3367"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_scribble_anime",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "4a399a87f1ffbc26d065a38765d30d24"
+            },
+            {
+                "model_name": "xinsir_controlnet-scribble-sdxl-1.0",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "888cf8985bd6442cba1f2d975b6eb022"
+            },
+            {
+                "model_name": "xinsir_anime_painter",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "f936bf22cb8e4dcfa6b0f3b96cdd8eb7"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-Controlnet-Union",
+                "base_algorithm": "Qwen Image",
+                "uuid": "5b5f21d2b80445598db19e924bd3a409"
+            }
+        ],
+        "Lineart(线稿)": [
+            {
+                "model_name": "control_v11p_sd15_lineart",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "b06dfbd1a61c35e933d9f8caa8a0e031"
+            },
+            {
+                "model_name": "control_v11p_sd15s2_lineart_anime",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "c263e039c57b8a958ee0a936039af654"
+            },
+            {
+                "model_name": "t2i-adapter_diffusers_xl_lineart",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "a0f01da42bf48b0ba02c86b6c26b5699"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-Controlnet-Union",
+                "base_algorithm": "Qwen Image",
+                "uuid": "5b5f21d2b80445598db19e924bd3a409"
+            }
+        ]
+    },
+    "空间关系类": {
+        "Depth(深度图)": [
+            {
+                "model_name": "control_v11f1p_sd15_depth",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "cf63d214734760dcdc108b1bd094921b"
+            },
+            {
+                "model_name": "t2iadapter_depth_sd15v2",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "f08a4a889b56d4099e8a947503cabc14"
+            },
+            {
+                "model_name": "t2iadapter_depth_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "8b74bf9ea84f592c069b523d9bef9dab"
+            },
+            {
+                "model_name": "t2iadapter_zoedepth_sd15v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "fc8b79f97eeceda388b43df12509c311"
+            },
+            {
+                "model_name": "control_sd15_inpaint_depth_hand_fp16",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "3497061cd45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "t2i-adapter_diffusers_xl_depth_zoe",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "a35993a2d1cde4a6c800364a68731c67"
+            },
+            {
+                "model_name": "sai_xl_depth_128lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "3156f3428afc7122c66b2b950f09d4cd"
+            },
+            {
+                "model_name": "t2i-adapter_diffusers_xl_depth_midas",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "c22ec6a7a24eed6b91889ae1a1e94b2e"
+            },
+            {
+                "model_name": "diffusers_xl_depth_mid",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "740d6d428e70d4b40888efa4d9eb642a"
+            },
+            {
+                "model_name": "xinsir_controlnet_depth_sdxl_1.0",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "6349e9dae8814084bd9c1585d335c24c"
+            },
+            {
+                "model_name": "sai_xl_depth_256lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "08d0fbb72d7fab601218df26978a46e0"
+            },
+            {
+                "model_name": "sargezt_xl_depth",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "feb9ee5779bf2eb3fdd669f2e3e6b1aa"
+            },
+            {
+                "model_name": "sargezt_xl_depth_zeed",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "4216d4b49a6b559d76d181908f866eb8"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_depth_anime",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "dea707d52e3a8f243da5579579cb3a3d"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_depth",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "693d7182db5293c0087524580111fd96"
+            },
+            {
+                "model_name": "sargezt_xl_depth_faid_vidit",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "1c6d32d0fb004cf1becc2b526fd83690"
+            },
+            {
+                "model_name": "diffusers_xl_depth_small",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "6a786af31a13776100e9c6a90f99aebf"
+            },
+            {
+                "model_name": "diffusers_xl_depth_full",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "04dcab4b18c7b821e96660d6c19de50b"
+            },
+            {
+                "model_name": "XLabs-flux-depth-controlnet_v3",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "0cc4e6b8206b44cdab51e30fb8b9c328"
+            },
+            {
+                "model_name": "InstantX-FLUX.1-dev-Controlnet-Union-Pro",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "13c1e1b96ba64f9cbb2b54f89b5fe873"
+            },
+            {
+                "model_name": "Flux.1-dev-Controlnet-Depth",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "64dd7a6c714f4512a4500f6a01b016b7"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-Controlnet-Union",
+                "base_algorithm": "Qwen Image",
+                "uuid": "5b5f21d2b80445598db19e924bd3a409"
+            }
+        ],
+        "Segment(语义分割)": [
+            {
+                "model_name": "control_v11p_sd15_seg",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "94571f4bb5136464afc1540a92ae3ee8"
+            }
+        ],
+        "Normal(正态)": [
+            {
+                "model_name": "control_v11p_sd15_normalbae",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "9a85fdca18a8b58b2fb2ff13ab339be4"
+            },
+            {
+                "model_name": "Flux.1-dev-Controlnet-Surface-Normal",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "e51fdccdf3b8417aab246bde40b5f360"
+            }
+        ]
+    },
+    "姿态类": {
+        "OpenPose(姿态)": [
+            {
+                "model_name": "control_v11p_sd15_openpose",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "b46dd34ef9c2fe189446599d62516cbf"
+            },
+            {
+                "model_name": "t2iadapter_openpose_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "5a8b19a8809e00be4e17517e8ab174ad"
+            },
+            {
+                "model_name": "control_v11p_sd15_densepose_fp16",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "3b4e0830d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "control_sd15_animal_openpose_fp16",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "329f0073d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "control_v2p_sd15_mediapipe_face",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "73de0752a7a8431ba21637cda6723c95"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_openpose_anime_v2",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "4cbbd2483088ef5f0d41bfef0d7141fb"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_openpose_anime",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "abb5d55cf94c504f6f8c64abc0b1483f"
+            },
+            {
+                "model_name": "thibaud_xl_openpose_256lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "4dd1f4df2a9d3a9db8aeaa9480196d02"
+            },
+            {
+                "model_name": "t2i-adapter_xl_openpose",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "9deac5a5c60abfd03261bd174ddba47d"
+            },
+            {
+                "model_name": "t2i-adapter_diffusers_xl_openpose",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "9cd43e1856040c2436f00802d5b54ee5"
+            },
+            {
+                "model_name": "thibaud_xl_openpose",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "2fe4f992a81c5ccbdf8e9851c8c96ff2"
+            },
+            {
+                "model_name": "controlnet-densepose-sdxl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "3ae77dfdd45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "xinsir_controlnet-openpose-sdxl-1.0",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "23ef8ab803d64288afdb7106b8967a55"
+            },
+            {
+                "model_name": "F.1-ControlNet-Pose-V1",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "7c6d889cb9c04b78858d8fece80f9f85"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-Controlnet-Union",
+                "base_algorithm": "Qwen Image",
+                "uuid": "5b5f21d2b80445598db19e924bd3a409"
+            }
+        ]
+    },
+    "画面参考": {
+        "Tile/Blur(分块/模糊)": [
+            {
+                "model_name": "control_v11f1e_sd15_tile",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "37e42c6bdb6fab4c24a662100f20f722"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_blur_anime",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "46a34a643f6855e9b3861515712df5d9"
+            },
+            {
+                "model_name": "xinsir_controlnet_tile_sdxl_1.0",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "0f47ef6d4f4b40afab8b290c98baac0e"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_blur_anime_beta",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "44199bb6dcf4f65e09a4e5e57ebdf9b4"
+            },
+            {
+                "model_name": "kohya_controllllite_xl_blur",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "aac5fe593565f0673673731d54ecfab8"
+            },
+            {
+                "model_name": "TTPLanet_SDXL_Controlnet_Tile_Realistic_v1",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "13bfaf39f9214c658507a92cd15fd02d"
+            },
+            {
+                "model_name": "TTPLanet_SDXL_Controlnet_Tile_Realistic_v2",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "163d505651a64d6bac9a907b213dc8b0"
+            },
+            {
+                "model_name": "Flux.1-dev-Controlnet-Upscaler",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "a696b5bdadc740119fd76505b33d6898"
+            }
+        ],
+        "Reference(参考)": [
+            {
+                "model_name": "None",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "/"
+            }
+        ]
+    },
+    "风格迁移": {
+        "IP-Adapter": [
+            {
+                "model_name": "ip-adapter_sd15",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "18801062fe4289dd0a984e69de9f9e7c"
+            },
+            {
+                "model_name": "ip-adapter_sd15_plus",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "ad4bd9b4b05c4ac8faf7f81d9fdcadc8"
+            },
+            {
+                "model_name": "ip-adapter_sd15_light",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "3a1ddfd0d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter_sd15_vit-G",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "36f3d2a0d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter_xl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "8ea2538fdd7dcdea52b2da6b5151f875"
+            },
+            {
+                "model_name": "ip-adapter-plus_sdxl_vit-h",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "38ee73f1d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter_sdxl_vit-h",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "375866e3d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "InstantX-F.1-dev-IP-Adapter",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "c6ed70879cf011ef96d600163e37ec70"
+            },
+            {
+                "model_name": "F.1-redux-dev",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "8ddf6f3ba8a111efbb1700163e031cf1"
+            }
+        ],
+        "T2I-Adapter": [
+            {
+                "model_name": "t2iadapter_canny_sd15v2",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "c04144bcf017232483181cd8607097c2"
+            },
+            {
+                "model_name": "t2iadapter_depth_sd15v2",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "f08a4a889b56d4099e8a947503cabc14"
+            },
+            {
+                "model_name": "t2iadapter_canny_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "a2c41c4e97944f3aa71f913bdc45b1ca"
+            },
+            {
+                "model_name": "t2iadapter_color_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "8e581a4e7c986950d71f1102accad5d0"
+            },
+            {
+                "model_name": "t2iadapter_depth_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "8b74bf9ea84f592c069b523d9bef9dab"
+            },
+            {
+                "model_name": "t2iadapter_keypose_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "181d8d213381458cb6e326760637d4b4"
+            },
+            {
+                "model_name": "t2iadapter_openpose_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "5a8b19a8809e00be4e17517e8ab174ad"
+            },
+            {
+                "model_name": "t2iadapter_seg_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "3c680cc8edfbc4479423549e01f21897"
+            },
+            {
+                "model_name": "t2iadapter_sketch_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "0d19dd02091ec2d01f3cdd99a4f4b442"
+            },
+            {
+                "model_name": "t2iadapter_sketch_sd15v2",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "bd6c5dbb73c2c2e538850c23ab2dcbf5"
+            },
+            {
+                "model_name": "t2iadapter_style_sd14v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "e33777a1f374eccd9464623c56a82c91"
+            },
+            {
+                "model_name": "t2iadapter_zoedepth_sd15v1",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "fc8b79f97eeceda388b43df12509c311"
+            }
+        ],
+        "Shuffle (随机洗牌)": [
+            {
+                "model_name": "control_v11e_sd15_shuffle",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "9efba1cc2d469bf4be8fc135689bc8a0"
+            }
+        ]
+    },
+    "上色": {
+        "Recolor(重上色)": [
+            {
+                "model_name": "ioclab_sd15_recolor",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "e0db5b9e227eac932c71498cf7e03a78"
+            },
+            {
+                "model_name": "sai_xl_recolor_128lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "af92235f1de682ceac136c06450c9a51"
+            },
+            {
+                "model_name": "sai_xl_recolor_256lora",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "03051a3606b4974ec02fc55b079757e7"
+            }
+        ]
+    },
+    "局部重绘": {
+        "Inpaint(局部重绘)": [
+            {
+                "model_name": "control_v11p_sd15_inpaint",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "ebeada0aa92959b4e905ab6980d5d203"
+            },
+            {
+                "model_name": "segmentation_mask_brushnet_ckpt",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "14aa553bf6534a419a9a465eba900f3a"
+            },
+            {
+                "model_name": "random_mask_brushnet_cpkt",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "de44488f84a74e02a1fac604d790698c"
+            },
+            {
+                "model_name": "segmentation_mask_brushnet_ckpt_sdxl_v1",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "a311363995dd4f2fa42ee3fc9582d920"
+            },
+            {
+                "model_name": "random_mask_brushnet_ckpt_sdxl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "3161fc68c59847b0ad826a9fb18c857f"
+            },
+            {
+                "model_name": "F.1-dev-Controlnet-Inpainting-Alpha",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "012d2f780c0b44dba829bb223207e608"
+            },
+            {
+                "model_name": "F.1-dev-Controlnet-Inpainting-Beta",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "31df01fc271d484ca4d496179d69a665"
+            },
+            {
+                "model_name": "InstantX-Qwen-Image-ControlNet-Inpainting",
+                "base_algorithm": "Qwen Image",
+                "uuid": "2228ab9234a34aa5abf77caa907c0de1"
+            }
+        ]
+    },
+    "换脸": {
+        "IP-Adapter": [
+            {
+                "model_name": "ip-adapter_face_id",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "368e6a37d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-faceid-portrait_sd15",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "330504bcd45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-faceid-plusv2_sd15",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "34fb8ef6d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-faceid-plus_sd15",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "362a215ad45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-faceid-portrait-v11_sd15",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "35c50016d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-faceid_sdxl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "38879e1ad45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-faceid-plusv2_sdxl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "3953f672d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "ip-adapter-plus-face_sdxl_vit-h",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "336955e4d45c11ee9b5e00163e365853"
+            }
+        ],
+        "Instant ID": [
+            {
+                "model_name": "ip-adapter_instant_id_sdxl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "3a8267c7d45c11ee9b5e00163e365853"
+            },
+            {
+                "model_name": "control_instant_id_sdxl",
+                "base_algorithm": "基础算法 XL",
+                "uuid": "3560664ad45c11ee9b5e00163e365853"
+            }
+        ],
+        "puLID": [
+            {
+                "model_name": "pulid_flux_v0.9.1",
+                "base_algorithm": "基础算法 F.1",
+                "uuid": "405836d1ae2646b4ba2716ed6bd5453a"
+            }
+        ]
+    },
+    "其他": {},
+    "光影": {
+        "puLID": [
+            {
+                "model_name": "control_v1u_sd15_illumination",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "3109072a5cf6403faba6162003b8f483"
+            },
+            {
+                "model_name": "control_v1p_sd15_brightness",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "39b8eac0d45c11ee9b5e00163e365853"
+            }
+        ]
+    },
+    "二维码": {
+        "puLID": [
+            {
+                "model_name": "control_v1p_sd15_qrcode_monster",
+                "base_algorithm": "基础算法 1.5",
+                "uuid": "1fa6070c35626e760b1473926852cbbc"
+            }
+        ]
+    }
+}

Plik diff jest za duży
+ 0 - 0
data/liblibai_controlnet_models.md


+ 645 - 0
data/parse_models.py

@@ -0,0 +1,645 @@
+import json
+import os
+
+raw_data = """
+适用方向
+Controlnet 类型
+模型名称
+基础算法类型
+模型版本UUID
+线稿类
+
+Canny(硬边缘)
+control_v11p_sd15_canny
+基础算法 1.5
+7d917ec7e55c5805db737d3b493c91ce
+
+t2iadapter_canny_sd14v1
+基础算法 1.5
+a2c41c4e97944f3aa71f913bdc45b1ca
+
+t2iadapter_canny_sd15v2
+基础算法 1.5
+c04144bcf017232483181cd8607097c2
+
+diffusers_xl_canny_full
+基础算法 XL
+56de5edadb6f2891aff05ff078dc0470
+
+diffusers_xl_canny_mid
+基础算法 XL
+efb97e9d8c237573298c3a5a7869b89c
+
+diffusers_xl_canny_small
+基础算法 XL
+dccde738064e9748f93b48ec5868968e
+
+kohya_controllllite_xl_canny
+基础算法 XL
+5242e3d18cc18689bd8af11dd2d675c1
+
+kohya_controllllite_xl_canny_anime
+基础算法 XL
+4f3e1cfe79f87496ec69a37826c3afeb
+
+sai_xl_canny_128lora
+基础算法 XL
+63c7f2c6c354336513831aa522d7e0f4
+
+sai_xl_canny_256lora
+基础算法 XL
+5bf551f53651764cad56363e17900d87
+
+t2i-adapter_diffusers_xl_canny
+基础算法 XL
+618390ab2957a422612cb2ba92a2788f
+
+t2i-adapter_xl_canny
+基础算法 XL
+7cd56501c336c1edba78430355c9d081
+
+xinsir_controlnet-canny-sdxl_V2
+基础算法 XL
+b6806516962f4e1599a93ac4483c3d23
+
+XLabs-flux-canny-controlnet_v3
+基础算法 F.1
+017997cd6ba44c4dbe8f60e0a26cd0df
+
+InstantX-FLUX.1-dev-Controlnet-Union-Pro
+基础算法 F.1
+13c1e1b96ba64f9cbb2b54f89b5fe873
+
+InstantX-Qwen-Image-Controlnet-Union
+Qwen Image
+5b5f21d2b80445598db19e924bd3a409
+
+SoftEdge(软边缘)
+
+control_v11p_sd15_softedge
+基础算法 1.5
+0929722d9047ec6498a50ff5d1081629
+
+sargezt_xl_softedge
+基础算法 XL
+dda1a0c480bfab9833d9d9a1e4a71fff
+
+controlnet-sd-xl-1.0-softedge-dexined
+基础算法 XL
+37bddde3d45c11ee9b5e00163e365853
+
+mistoLine_softedge_sdxl_fp16
+基础算法 XL
+4f6726be104a432f8039b018c92ed4bf
+
+mistoLine_rank256
+基础算法 XL
+83286d0e66a845c58f7d23442f9dedf9
+
+XLabs-flux-hed-controlnet_v3
+基础算法 F.1
+6c4d620df3644514903b8189735c6ae9
+
+F.1_mistoline_dev_v1
+基础算法 F.1
+3e6860a3b9444f25ae07d9c1b5d1ba9e
+
+InstantX-FLUX.1-dev-Controlnet-Union-Pro
+基础算法 F.1
+13c1e1b96ba64f9cbb2b54f89b5fe873
+
+InstantX-Qwen-Image-Controlnet-Union
+Qwen Image
+5b5f21d2b80445598db19e924bd3a409
+
+MLSD(直线)
+control_v11p_sd15_mlsd
+基础算法 1.5
+7168cece6a0d491375aa1753ff3bdc21
+
+Scribble/Sketch(涂鸦/草图)
+control_v11p_sd15_scribble
+基础算法 1.5
+fe57911f7ba1b84eb27f1e1ecead3367
+
+kohya_controllllite_xl_scribble_anime
+基础算法 XL
+4a399a87f1ffbc26d065a38765d30d24
+
+xinsir_controlnet-scribble-sdxl-1.0
+基础算法 XL
+888cf8985bd6442cba1f2d975b6eb022
+
+xinsir_anime_painter
+基础算法 XL
+f936bf22cb8e4dcfa6b0f3b96cdd8eb7
+
+InstantX-Qwen-Image-Controlnet-Union
+Qwen Image
+5b5f21d2b80445598db19e924bd3a409
+
+Lineart(线稿)
+
+control_v11p_sd15_lineart
+基础算法 1.5
+b06dfbd1a61c35e933d9f8caa8a0e031
+
+control_v11p_sd15s2_lineart_anime
+基础算法 1.5
+c263e039c57b8a958ee0a936039af654
+
+t2i-adapter_diffusers_xl_lineart
+基础算法 XL
+a0f01da42bf48b0ba02c86b6c26b5699
+
+InstantX-Qwen-Image-Controlnet-Union
+Qwen Image
+5b5f21d2b80445598db19e924bd3a409
+空间关系类
+
+Depth(深度图)
+
+control_v11f1p_sd15_depth
+基础算法 1.5
+cf63d214734760dcdc108b1bd094921b
+
+t2iadapter_depth_sd15v2
+基础算法 1.5
+f08a4a889b56d4099e8a947503cabc14
+
+t2iadapter_depth_sd14v1
+基础算法 1.5
+8b74bf9ea84f592c069b523d9bef9dab
+
+t2iadapter_zoedepth_sd15v1
+基础算法 1.5
+fc8b79f97eeceda388b43df12509c311
+
+control_sd15_inpaint_depth_hand_fp16
+基础算法 1.5
+3497061cd45c11ee9b5e00163e365853
+
+t2i-adapter_diffusers_xl_depth_zoe
+基础算法 XL
+a35993a2d1cde4a6c800364a68731c67
+
+sai_xl_depth_128lora
+基础算法 XL
+3156f3428afc7122c66b2b950f09d4cd
+
+t2i-adapter_diffusers_xl_depth_midas
+基础算法 XL
+c22ec6a7a24eed6b91889ae1a1e94b2e
+
+diffusers_xl_depth_mid
+基础算法 XL
+740d6d428e70d4b40888efa4d9eb642a
+
+xinsir_controlnet_depth_sdxl_1.0
+基础算法 XL
+6349e9dae8814084bd9c1585d335c24c
+
+sai_xl_depth_256lora
+基础算法 XL
+08d0fbb72d7fab601218df26978a46e0
+
+sargezt_xl_depth
+基础算法 XL
+feb9ee5779bf2eb3fdd669f2e3e6b1aa
+
+sargezt_xl_depth_zeed
+基础算法 XL
+4216d4b49a6b559d76d181908f866eb8
+
+kohya_controllllite_xl_depth_anime
+基础算法 XL
+dea707d52e3a8f243da5579579cb3a3d
+
+kohya_controllllite_xl_depth
+基础算法 XL
+693d7182db5293c0087524580111fd96
+
+sargezt_xl_depth_faid_vidit
+基础算法 XL
+1c6d32d0fb004cf1becc2b526fd83690
+
+diffusers_xl_depth_small
+基础算法 XL
+6a786af31a13776100e9c6a90f99aebf
+
+diffusers_xl_depth_full
+基础算法 XL
+04dcab4b18c7b821e96660d6c19de50b
+
+XLabs-flux-depth-controlnet_v3
+基础算法 F.1
+0cc4e6b8206b44cdab51e30fb8b9c328
+
+InstantX-FLUX.1-dev-Controlnet-Union-Pro
+基础算法 F.1
+13c1e1b96ba64f9cbb2b54f89b5fe873
+
+Flux.1-dev-Controlnet-Depth
+基础算法 F.1
+64dd7a6c714f4512a4500f6a01b016b7
+
+InstantX-Qwen-Image-Controlnet-Union
+Qwen Image
+5b5f21d2b80445598db19e924bd3a409
+
+Segment(语义分割)
+control_v11p_sd15_seg
+基础算法 1.5
+94571f4bb5136464afc1540a92ae3ee8
+
+Normal(正态)
+control_v11p_sd15_normalbae
+基础算法 1.5
+9a85fdca18a8b58b2fb2ff13ab339be4
+
+Flux.1-dev-Controlnet-Surface-Normal
+基础算法 F.1
+e51fdccdf3b8417aab246bde40b5f360
+姿态类
+
+OpenPose(姿态)
+
+control_v11p_sd15_openpose
+基础算法 1.5
+b46dd34ef9c2fe189446599d62516cbf
+
+t2iadapter_openpose_sd14v1
+基础算法 1.5
+5a8b19a8809e00be4e17517e8ab174ad
+
+control_v11p_sd15_densepose_fp16
+基础算法 1.5
+3b4e0830d45c11ee9b5e00163e365853
+
+control_sd15_animal_openpose_fp16
+基础算法 1.5
+329f0073d45c11ee9b5e00163e365853
+
+control_v2p_sd15_mediapipe_face
+基础算法 1.5
+73de0752a7a8431ba21637cda6723c95
+
+kohya_controllllite_xl_openpose_anime_v2
+基础算法 XL
+4cbbd2483088ef5f0d41bfef0d7141fb
+
+kohya_controllllite_xl_openpose_anime
+基础算法 XL
+abb5d55cf94c504f6f8c64abc0b1483f
+
+thibaud_xl_openpose_256lora
+基础算法 XL
+4dd1f4df2a9d3a9db8aeaa9480196d02
+
+t2i-adapter_xl_openpose
+基础算法 XL
+9deac5a5c60abfd03261bd174ddba47d
+
+t2i-adapter_diffusers_xl_openpose
+基础算法 XL
+9cd43e1856040c2436f00802d5b54ee5
+
+thibaud_xl_openpose
+基础算法 XL
+2fe4f992a81c5ccbdf8e9851c8c96ff2
+
+controlnet-densepose-sdxl
+基础算法 XL
+3ae77dfdd45c11ee9b5e00163e365853
+
+xinsir_controlnet-openpose-sdxl-1.0
+基础算法 XL
+23ef8ab803d64288afdb7106b8967a55
+
+F.1-ControlNet-Pose-V1
+基础算法 F.1
+7c6d889cb9c04b78858d8fece80f9f85
+
+InstantX-Qwen-Image-Controlnet-Union
+Qwen Image
+5b5f21d2b80445598db19e924bd3a409
+画面参考
+Tile/Blur(分块/模糊)
+control_v11f1e_sd15_tile
+基础算法 1.5
+37e42c6bdb6fab4c24a662100f20f722
+
+kohya_controllllite_xl_blur_anime
+基础算法 XL
+46a34a643f6855e9b3861515712df5d9
+
+xinsir_controlnet_tile_sdxl_1.0
+基础算法 XL
+0f47ef6d4f4b40afab8b290c98baac0e
+
+kohya_controllllite_xl_blur_anime_beta
+基础算法 XL
+44199bb6dcf4f65e09a4e5e57ebdf9b4
+
+kohya_controllllite_xl_blur
+基础算法 XL
+aac5fe593565f0673673731d54ecfab8
+
+TTPLanet_SDXL_Controlnet_Tile_Realistic_v1
+基础算法 XL
+13bfaf39f9214c658507a92cd15fd02d
+
+TTPLanet_SDXL_Controlnet_Tile_Realistic_v2
+基础算法 XL
+163d505651a64d6bac9a907b213dc8b0
+
+Flux.1-dev-Controlnet-Upscaler
+基础算法 F.1
+a696b5bdadc740119fd76505b33d6898
+
+Reference(参考)
+None
+基础算法 1.5
+/
+风格迁移
+IP-Adapter
+
+ip-adapter_sd15
+基础算法 1.5
+18801062fe4289dd0a984e69de9f9e7c
+
+ip-adapter_sd15_plus
+基础算法 1.5
+ad4bd9b4b05c4ac8faf7f81d9fdcadc8
+
+ip-adapter_sd15_light
+基础算法 1.5
+3a1ddfd0d45c11ee9b5e00163e365853
+
+ip-adapter_sd15_vit-G
+基础算法 1.5
+36f3d2a0d45c11ee9b5e00163e365853
+
+ip-adapter_xl
+基础算法 XL
+8ea2538fdd7dcdea52b2da6b5151f875
+
+ip-adapter-plus_sdxl_vit-h
+基础算法 XL
+38ee73f1d45c11ee9b5e00163e365853
+
+ip-adapter_sdxl_vit-h
+基础算法 XL
+375866e3d45c11ee9b5e00163e365853
+
+InstantX-F.1-dev-IP-Adapter
+基础算法 F.1
+c6ed70879cf011ef96d600163e37ec70
+
+F.1-redux-dev
+基础算法 F.1
+8ddf6f3ba8a111efbb1700163e031cf1
+
+T2I-Adapter
+t2iadapter_canny_sd15v2
+基础算法 1.5
+c04144bcf017232483181cd8607097c2
+
+t2iadapter_depth_sd15v2
+基础算法 1.5
+f08a4a889b56d4099e8a947503cabc14
+
+t2iadapter_canny_sd14v1
+基础算法 1.5
+a2c41c4e97944f3aa71f913bdc45b1ca
+
+t2iadapter_color_sd14v1
+基础算法 1.5
+8e581a4e7c986950d71f1102accad5d0
+
+t2iadapter_depth_sd14v1
+基础算法 1.5
+8b74bf9ea84f592c069b523d9bef9dab
+
+t2iadapter_keypose_sd14v1
+基础算法 1.5
+181d8d213381458cb6e326760637d4b4
+
+t2iadapter_openpose_sd14v1
+基础算法 1.5
+5a8b19a8809e00be4e17517e8ab174ad
+
+t2iadapter_seg_sd14v1
+基础算法 1.5
+3c680cc8edfbc4479423549e01f21897
+
+t2iadapter_sketch_sd14v1
+基础算法 1.5
+0d19dd02091ec2d01f3cdd99a4f4b442
+
+t2iadapter_sketch_sd15v2
+基础算法 1.5
+bd6c5dbb73c2c2e538850c23ab2dcbf5
+
+t2iadapter_style_sd14v1
+基础算法 1.5
+e33777a1f374eccd9464623c56a82c91
+
+t2iadapter_zoedepth_sd15v1
+基础算法 1.5
+fc8b79f97eeceda388b43df12509c311
+
+Shuffle (随机洗牌)
+control_v11e_sd15_shuffle
+基础算法 1.5
+9efba1cc2d469bf4be8fc135689bc8a0
+上色
+Recolor(重上色)
+ioclab_sd15_recolor
+基础算法 1.5
+e0db5b9e227eac932c71498cf7e03a78
+
+sai_xl_recolor_128lora
+基础算法 XL
+af92235f1de682ceac136c06450c9a51
+
+sai_xl_recolor_256lora
+基础算法 XL
+03051a3606b4974ec02fc55b079757e7
+局部重绘
+
+Inpaint(局部重绘)
+
+control_v11p_sd15_inpaint
+基础算法 1.5
+ebeada0aa92959b4e905ab6980d5d203
+
+segmentation_mask_brushnet_ckpt
+基础算法 1.5
+14aa553bf6534a419a9a465eba900f3a
+
+random_mask_brushnet_cpkt
+基础算法 1.5
+de44488f84a74e02a1fac604d790698c
+
+segmentation_mask_brushnet_ckpt_sdxl_v1
+基础算法 XL
+a311363995dd4f2fa42ee3fc9582d920
+
+random_mask_brushnet_ckpt_sdxl
+基础算法 XL
+3161fc68c59847b0ad826a9fb18c857f
+
+F.1-dev-Controlnet-Inpainting-Alpha
+基础算法 F.1
+012d2f780c0b44dba829bb223207e608
+
+F.1-dev-Controlnet-Inpainting-Beta
+基础算法 F.1
+31df01fc271d484ca4d496179d69a665
+
+InstantX-Qwen-Image-ControlNet-Inpainting
+Qwen Image
+2228ab9234a34aa5abf77caa907c0de1
+换脸
+IP-Adapter
+
+ip-adapter_face_id
+基础算法 1.5
+368e6a37d45c11ee9b5e00163e365853
+
+ip-adapter-faceid-portrait_sd15
+基础算法 1.5
+330504bcd45c11ee9b5e00163e365853
+
+ip-adapter-faceid-plusv2_sd15
+基础算法 1.5
+34fb8ef6d45c11ee9b5e00163e365853
+
+ip-adapter-faceid-plus_sd15
+基础算法 1.5
+362a215ad45c11ee9b5e00163e365853
+
+ip-adapter-faceid-portrait-v11_sd15
+基础算法 1.5
+35c50016d45c11ee9b5e00163e365853
+
+ip-adapter-faceid_sdxl
+基础算法 XL
+38879e1ad45c11ee9b5e00163e365853
+
+ip-adapter-faceid-plusv2_sdxl
+基础算法 XL
+3953f672d45c11ee9b5e00163e365853
+
+ip-adapter-plus-face_sdxl_vit-h
+基础算法 XL
+336955e4d45c11ee9b5e00163e365853
+
+Instant ID
+ip-adapter_instant_id_sdxl
+基础算法 XL
+3a8267c7d45c11ee9b5e00163e365853
+
+control_instant_id_sdxl
+基础算法 XL
+3560664ad45c11ee9b5e00163e365853
+
+puLID
+pulid_flux_v0.9.1
+基础算法 F.1
+405836d1ae2646b4ba2716ed6bd5453a
+其他
+光影
+control_v1u_sd15_illumination
+基础算法 1.5
+3109072a5cf6403faba6162003b8f483
+
+control_v1p_sd15_brightness
+基础算法 1.5
+39b8eac0d45c11ee9b5e00163e365853
+
+二维码
+control_v1p_sd15_qrcode_monster
+基础算法 1.5
+1fa6070c35626e760b1473926852cbbc
+"""
+
+lines = [l.strip() for l in raw_data.split('\n') if l.strip()]
+
+models_by_category = {}
+current_category = "General"
+current_type = "Misc"
+
+i = 0
+while i < len(lines):
+    line = lines[i]
+    if line in ["适用方向", "Controlnet 类型", "模型名称", "基础算法类型", "模型版本UUID"]:
+        i += 1
+        continue
+    
+    # Check if this is a top level category (usually ends with 类 or specific words)
+    if line.endswith("类") or line in ["风格迁移", "上色", "局部重绘", "换脸", "其他", "光影", "二维码", "画面参考"]:
+        current_category = line
+        if current_category not in models_by_category:
+            models_by_category[current_category] = {}
+        i += 1
+        continue
+        
+    # Check if this is a sub-type (usually Chinese translation in parens or standard names like IP-Adapter)
+    if "(" in line or line in ["IP-Adapter", "Instant ID", "puLID", "T2I-Adapter", "Shuffle (随机洗牌)"]:
+        current_type = line
+        if current_category not in models_by_category:
+             models_by_category[current_category] = {}
+        if current_type not in models_by_category[current_category]:
+            models_by_category[current_category][current_type] = []
+        i += 1
+        continue
+        
+    # Model block: 3 lines
+    if i + 2 < len(lines):
+        model_name = lines[i]
+        algo = lines[i+1]
+        uuid = lines[i+2]
+        
+        # Verify it's actually an algo block
+        if algo.startswith("基础算法") or algo == "Qwen Image" or uuid == "/":
+            if current_category not in models_by_category:
+                models_by_category[current_category] = {}
+            if current_type not in models_by_category[current_category]:
+                models_by_category[current_category][current_type] = []
+                
+            models_by_category[current_category][current_type].append({
+                "model_name": model_name,
+                "base_algorithm": algo,
+                "uuid": uuid
+            })
+            i += 3
+            continue
+            
+    # Default case
+    i += 1
+    
+# Generate Markdown
+md_lines = ["# LiblibAI ControlNet Models Mapping\\n"]
+
+for category, types in models_by_category.items():
+    md_lines.append(f"## {category}\\n")
+    for t, models in types.items():
+        md_lines.append(f"### {t}")
+        md_lines.append("| Model Name | Base Algorithm | UUID |")
+        md_lines.append("| --- | --- | --- |")
+        for m in models:
+            md_lines.append(f"| {m['model_name']} | {m['base_algorithm']} | `{m['uuid']}` |")
+        md_lines.append("\\n")
+
+md_content = "\\n".join(md_lines)
+
+# Write outputs
+os.makedirs("/root/Tool_Agent/data", exist_ok=True)
+
+with open("/root/Tool_Agent/data/liblibai_controlnet_models.md", "w", encoding="utf-8") as f:
+    f.write(md_content)
+    
+with open("/root/Tool_Agent/data/liblibai_controlnet_models.json", "w", encoding="utf-8") as f:
+    json.dump(models_by_category, f, indent=4, ensure_ascii=False)

+ 399 - 157
data/registry.json

@@ -4,7 +4,7 @@
       "tool_id": "image_stitcher",
       "name": "图片拼接工具",
       "category": "cv",
-      "description": "将多张图片按指定方向(水平/垂直/网格)拼接成一张大图。支持间距设置、背景色填充和统一缩放模式。输入输出均为 Base64 编码的 PNG 图片。",
+      "description": "将多张图片按指定方向(水平/垂直/网格)拼接成一张大图。支持间距设置、背景色填充和统一缩放模式。输入支持网络 URL 链接和 Base64,输出直接返回一张拼接成功的干净云端长效外链(URL)。",
       "input_schema": {
         "properties": {
           "background_color": {
@@ -29,7 +29,7 @@
             "type": "string"
           },
           "images": {
-            "description": "Base64 编码的图片列表,至少 2 张",
+            "description": "图片数据源列表,强烈推荐传递 http:// 或 https:// 网络链接!兼容 Base64 编码,至少 2 张",
             "items": {
               "type": "string"
             },
@@ -61,17 +61,17 @@
       },
       "output_schema": {
         "properties": {
-          "height": {
-            "description": "结果图高度(像素)",
-            "type": "integer"
-          },
-          "image": {
-            "description": "拼接结果,Base64 编码的 PNG 图片",
+          "url": {
+            "description": "拼接成功的持久化外链",
             "type": "string"
           },
           "width": {
             "description": "结果图宽度(像素)",
             "type": "integer"
+          },
+          "height": {
+            "description": "结果图高度(像素)",
+            "type": "integer"
           }
         },
         "reason": "图片拼接工具输出结果定义",
@@ -90,20 +90,58 @@
     },
     {
       "tool_id": "liblibai_controlnet",
-      "name": "LibLib ControlNet 图生图",
+      "name": "LibLib AI 综合生成工具",
       "category": "cv",
-      "description": "基于 LibLib AI 开放 API 的 ControlNet Canny 图生图工具,支持通过边缘检测控制图像生成",
+      "description": "基于 LibLib AI 开放 API 的万能生成引擎。支持【多路并发控制】:允许在一次请求中同时投喂多个 ControlNet(如同时传入 OpenPose + Canny + Depth),AI 会在同一张图上严格遵守所有控制条件!支持模块:canny(边缘), softedge(软边缘), lineart(线稿), openpose(姿态), depth(深度图), inpaint(蒙版重绘), instantid(换脸)。",
       "input_schema": {
         "type": "object",
         "properties": {
-          "image": {
+          "mode": {
             "type": "string",
-            "description": "图片来源,支持 Base64 编码的图片数据或 HTTP 图片 URL"
+            "description": "单路模式(可选值:text2img, img2img, canny, softedge, lineart, openpose, depth, inpaint, instantid)。如果你想跑多路并发,这个字段可以无视或默认。"
           },
           "prompt": {
             "type": "string",
             "description": "正向提示词"
           },
+          "image": {
+            "type": "string",
+            "description": "单路模式的主参考图(支持 URL/Base64)。"
+          },
+          "control_nets": {
+            "type": "array",
+            "description": "【核心魔法】多路控制模型组!在此可同时叠加传入多个控制网参数字典(覆盖外层 mode/image)",
+            "items": {
+              "type": "object",
+              "properties": {
+                "mode": {
+                  "type": "string",
+                  "description": "控制类型: canny, softedge, lineart, openpose, depth"
+                },
+                "image": {
+                  "type": "string",
+                  "description": "对应的参考图 URL/Base64"
+                },
+                "weight": {
+                  "type": "number",
+                  "description": "控制权重,默认 1.0",
+                  "default": 1.0
+                }
+              },
+              "required": [
+                "mode",
+                "image"
+              ]
+            }
+          },
+          "mask_image": {
+            "type": "string",
+            "description": "蒙版图片来源 (黑白蒙版),仅用于 mode 为 inpaint 时传入"
+          },
+          "pose_image": {
+            "type": "string",
+            "description": "人物姿态参考图来源,仅用于 mode 为 instantid 换脸时传入,指定目标面部朝向或动作"
+          },
           "negative_prompt": {
             "type": "string",
             "description": "反向提示词",
@@ -126,37 +164,17 @@
           },
           "cfg_scale": {
             "type": "number",
-            "description": "CFG Scale",
-            "default": 7
+            "description": "CFG Scale 提示词引导系数",
+            "default": 7.0
           },
           "img_count": {
             "type": "integer",
             "description": "生成图片数量",
             "default": 1
-          },
-          "control_weight": {
-            "type": "number",
-            "description": "ControlNet 权重",
-            "default": 1
-          },
-          "preprocessor": {
-            "type": "integer",
-            "description": "预处理器枚举",
-            "default": 1
-          },
-          "canny_low": {
-            "type": "integer",
-            "description": "Canny 低阈值",
-            "default": 100
-          },
-          "canny_high": {
-            "type": "integer",
-            "description": "Canny 高阈值",
-            "default": 200
           }
         },
         "required": [
-          "image",
+          "mode",
           "prompt"
         ]
       },
@@ -183,14 +201,58 @@
       "stream_support": false,
       "status": "active",
       "backend_runtime": "local",
-      "group_ids": [],
+      "group_ids": [
+        "liblib_ai_group"
+      ],
+      "tool_slug_ids": []
+    },
+    {
+      "tool_id": "liblib_image_uploader",
+      "name": "LibLib Image Uploader",
+      "category": "utility",
+      "description": "专为将本地图片数据转为持久化云端 URL 构建的老实工具。无论你是给它纯 Base64、带有 data:image 头部的 Base64 都会原封不动传给 Liblib 对象存储,并返回生成的临时网络跨端直连图片 URL(http...),完美解决大模型生图过程中因为超大 Base64 字符撑爆思考上下文的异常限制!只接收 Base64 字符串参数,不要传图片路径。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "image_base64": {
+            "type": "string",
+            "description": "Base64 encoded image data (e.g., data:image/png;base64,iVBORw0...)"
+          },
+          "filename": {
+            "type": "string",
+            "description": "Optional filename for extension detection",
+            "default": "image.png"
+          }
+        },
+        "required": [
+          "image_base64"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "status": {
+            "type": "string"
+          },
+          "url": {
+            "type": "string",
+            "description": "生成的公共网络临时或永久直连 URL,可原样喂给其它支持 HTTP 图生图组件应用!"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "liblib_ai_group"
+      ],
       "tool_slug_ids": []
     },
     {
       "tool_id": "launch_comfy_env",
       "name": "Launch ComfyUI Environment",
       "category": "ai",
-      "description": "启动 RunComfy 云端机器并等待就绪。返回 server_id 用于后续 ComfyUI workflow 执行。需要环境变量 RUNCOMFY_USER_ID 和 API_TOKEN。",
+      "description": "[🔴 暂时外服不可用] 启动 RunComfy 云端机器并等待就绪。返回 server_id 用于后续 ComfyUI workflow 执行。需要环境变量 RUNCOMFY_USER_ID 和 API_TOKEN。",
       "input_schema": {
         "type": "object",
         "properties": {
@@ -246,7 +308,7 @@
         ]
       },
       "stream_support": false,
-      "status": "active",
+      "status": "inactive",
       "backend_runtime": "local",
       "group_ids": [
         "runcomfy_lifecycle"
@@ -259,7 +321,7 @@
       "tool_id": "runcomfy_workflow_executor",
       "name": "RunComfy Workflow Executor",
       "category": "image_generation",
-      "description": "在已就绪的 RunComfy 机器上提交 ComfyUI 工作流,上传输入文件,监听执行状态,下载结果图片(不启动/关闭机器)",
+      "description": "[🔴 暂时外服不可用] 在已就绪的 RunComfy 机器上提交 ComfyUI 工作流,上传输入文件,监听执行状态,下载结果图片(不启动/关闭机器)",
       "input_schema": {
         "type": "object",
         "required": [
@@ -328,7 +390,7 @@
         }
       },
       "stream_support": false,
-      "status": "active",
+      "status": "inactive",
       "backend_runtime": "local",
       "group_ids": [
         "runcomfy_lifecycle"
@@ -341,7 +403,7 @@
       "tool_id": "runcomfy_stop_env",
       "name": "RunComfy Stop Service",
       "category": "cloud",
-      "description": "Stop and delete RunComfy server instances to release resources. Works with launch_comfy_env for complete lifecycle management.",
+      "description": "[🔴 暂时外服不可用] Stop and delete RunComfy server instances to release resources. Works with launch_comfy_env for complete lifecycle management.",
       "input_schema": {
         "type": "object",
         "properties": {
@@ -372,7 +434,7 @@
         }
       },
       "stream_support": false,
-      "status": "active",
+      "status": "inactive",
       "backend_runtime": "local",
       "group_ids": [
         "runcomfy_lifecycle"
@@ -386,7 +448,7 @@
       "name": "即梦-创建任务",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "提交异步任务到上游(本地服务转发 JI_MENG_API_BASE,POST /add_task)。需配置 tools/local/ji_meng/.env。",
+      "description": "[🔴 暂时不可用/停机维护] 提交异步任务到上游(本地服务转发 JI_MENG_API_BASE,POST /add_task)。需配置 tools/local/ji_meng/.env。",
       "input_schema": {
         "type": "object",
         "properties": {
@@ -436,7 +498,7 @@
       "name": "即梦-查询任务",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "按 task_id 查询任务状态与结果(本地服务转发上游,POST /query_task)。",
+      "description": "[🔴 暂时不可用/停机维护] 按 task_id 查询任务状态与结果(本地服务转发上游,POST /query_task)。",
       "input_schema": {
         "type": "object",
         "properties": {
@@ -478,52 +540,41 @@
     },
     {
       "tool_id": "nano_banana",
-      "name": "Nano Banana(Gemini 图模)",
+      "name": "Nano Banana(Gemini 多模态神级图模)",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "通过 Google Gemini 原生图模 REST generateContent 文生图/图生图。需 GEMINI_API_KEY;可选 gemini-2.5-flash-image、gemini-3.1-flash-image-preview 等。详见 https://ai.google.dev/gemini-api/docs/image-generation?hl=zh-cn#rest",
+      "description": "通过 Google Gemini 原生大模型生图。它是系统内唯一原生支持【跨模态/多图融合意象生图】的模型!支持在 images 数组里丢进 N 张完全不同的图片(例如:一张人像、一把宝剑、一栋大楼),并在 prompt 中基于这几张图进行超强逻辑融合。强力推荐用来做抽象图生图、无边界的多图概念重组。",
       "input_schema": {
         "type": "object",
         "properties": {
           "prompt": {
             "type": "string",
-            "description": "提示词(文生图或与参考图配合做编辑)"
+            "description": "提示词(可以明确指代传入的多张图片,进行复杂长本推理和跨模态生图)"
           },
           "model": {
             "type": "string",
-            "description": "模型 ID;省略则使用环境变量 GEMINI_IMAGE_MODEL,默认 gemini-2.5-flash-image。示例:gemini-3.1-flash-image-preview"
+            "description": "模型 ID;省略则使用环境变量 GEMINI_IMAGE_MODEL,默认 gemini-3.1-flash-image-preview"
           },
           "aspect_ratio": {
             "type": "string",
-            "description": "输出宽高比,如 1:1、16:9(对应 generationConfig.imageConfig.aspectRatio)"
+            "description": "输出宽高比,如 1:1、16:9"
           },
           "image_size": {
             "type": "string",
-            "description": "Gemini 3.x 输出规格:512、1K、2K、4K(须大写 K,见官方文档)"
+            "description": "输出规格:512、1K、2K、4K"
           },
           "response_modalities": {
             "type": "array",
             "items": {
               "type": "string"
             },
-            "description": "如 [\"TEXT\",\"IMAGE\"] 或 [\"IMAGE\"];省略则由 API 默认"
+            "description": "如 [\"TEXT\",\"IMAGE\"]"
           },
-          "images": {
+          "image_urls": {
             "type": "array",
-            "description": "可选参考图,每项为 {mime_type, data},data 为 Base64 或 data URL",
+            "description": "【杀手锏】参考图外链数组!允许同时传入多张关联图片的网络 URL。底层支持自动下载网图整合喂给大模型进行跨特征意象重组。",
             "items": {
-              "type": "object",
-              "properties": {
-                "mime_type": {
-                  "type": "string"
-                },
-                "data": {
-                  "type": "string"
-                }
-              },
-              "required": [
-                "data"
-              ]
+              "type": "string"
             }
           }
         },
@@ -557,93 +608,117 @@
       "group_ids": []
     },
     {
-      "tool_id": "flux_submit",
-      "name": "FLUX-提交生图任务",
+      "tool_id": "midjourney_submit_job",
+      "name": "Midjourney-提交生图任务",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "向 BFL 提交异步生图任务(POST /v1/{model})。需 BFL_API_KEY;model 为端点名如 flux-2-pro-preview。返回 id、polling_url 供 flux_query 轮询。文档 https://docs.bfl.ai/quick_start/generating_images",
+      "description": "[🔴 暂时不可用/本地重构中] 提交 Midjourney 生图任务(转发至 MIDJOURNEY_API_BASE/submit_job)。需配置 tools/local/midjourney/.env。mode 为 relaxed 或 fast。",
       "input_schema": {
         "type": "object",
         "properties": {
-          "model": {
+          "cookie": {
             "type": "string",
-            "description": "端点路径段,如 flux-2-pro-preview、flux-2-max、flux-dev、flux-kontext-pro 等"
+            "description": "Midjourney 会话 cookie"
           },
           "prompt": {
             "type": "string",
             "description": "提示词"
           },
-          "width": {
-            "type": "integer",
-            "description": "输出宽度(像素),可选"
-          },
-          "height": {
-            "type": "integer",
-            "description": "输出高度(像素),可选"
+          "user_id": {
+            "type": "string",
+            "description": "用户 ID"
           },
-          "parameters": {
-            "type": "object",
-            "description": "合并进 BFL 请求体的额外字段(模型专有参数)"
+          "mode": {
+            "type": "string",
+            "enum": [
+              "relaxed",
+              "fast"
+            ],
+            "description": "队列模式:relaxed 或 fast"
           }
         },
         "required": [
-          "model",
-          "prompt"
+          "cookie",
+          "prompt",
+          "user_id",
+          "mode"
         ]
       },
       "output_schema": {
+        "type": "object",
+        "description": "上游返回 JSON,通常含 job_id 或 id",
+        "properties": {}
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "midjourney_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "midjourney_query_job_status",
+      "name": "Midjourney-查询任务状态",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "[🔴 暂时不可用/本地重构中] 查询指定任务状态(转发 MIDJOURNEY_API_BASE/query_job_status)。",
+      "input_schema": {
         "type": "object",
         "properties": {
-          "id": {
+          "cookie": {
             "type": "string",
-            "description": "请求 ID,flux_query 的 request_id"
+            "description": "Midjourney 会话 cookie"
           },
-          "polling_url": {
+          "job_id": {
             "type": "string",
-            "description": "轮询地址,须原样传给 flux_query(全球端点必须用返回的 URL)"
+            "description": "submit_job 返回的任务 ID"
           }
-        }
+        },
+        "required": [
+          "cookie",
+          "job_id"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "description": "上游状态 JSON,如 status / job_status 等",
+        "properties": {}
       },
       "stream_support": false,
       "status": "active",
       "backend_runtime": "local",
       "group_ids": [
-        "flux_bfl_lifecycle"
+        "midjourney_lifecycle"
       ]
     },
     {
-      "tool_id": "flux_query",
-      "name": "FLUX-查询任务结果",
+      "tool_id": "midjourney_get_image_urls",
+      "name": "Midjourney-获取结果图链接",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "按 flux_submit 返回的 polling_url 与 id 轮询状态;Ready 时 result.sample 为签名图 URL(约 10 分钟有效)。",
+      "description": "[🔴 暂时不可用/本地重构中] 根据 job_id 获取 4 张图 URL(转发 MIDJOURNEY_API_BASE/get_image_urls)。",
       "input_schema": {
         "type": "object",
         "properties": {
-          "polling_url": {
-            "type": "string",
-            "description": "提交响应中的 polling_url"
-          },
-          "request_id": {
+          "job_id": {
             "type": "string",
-            "description": "提交响应中的 id"
+            "description": "任务 ID"
           }
         },
         "required": [
-          "polling_url",
-          "request_id"
+          "job_id"
         ]
       },
       "output_schema": {
         "type": "object",
+        "description": "上游返回 JSON 或 URL 数组,常见字段 image_urls / urls",
         "properties": {
-          "status": {
-            "type": "string",
-            "description": "如 Ready、Pending、Error、Failed 等"
-          },
-          "result": {
-            "type": "object",
-            "description": "成功时常含 sample(图片签名 URL)"
+          "image_urls": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "四张图片链接(字段名以实际服务为准)"
           }
         }
       },
@@ -651,130 +726,297 @@
       "status": "active",
       "backend_runtime": "local",
       "group_ids": [
-        "flux_bfl_lifecycle"
+        "midjourney_lifecycle"
       ]
     },
     {
-      "tool_id": "midjourney_submit_job",
-      "name": "Midjourney-提交生图任务",
+      "tool_id": "flux_generate",
+      "name": "Flux-多模态生图编辑API",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "提交 Midjourney 生图任务(转发至 MIDJOURNEY_API_BASE/submit_job)。需配置 tools/local/midjourney/.env。mode 为 relaxed 或 fast。",
+      "description": "基于 APIyi 中转站实现的高级 Flux 图像生成与编辑(图生图/局部重绘)调用。如果只传 prompt 则触发纯文生图;如果传入 image_url (最好配合 flux-kontext-max 模型) 则触发高级图像编辑。一次性返回生成好的图片URL(临时URL,10分钟有效)。",
       "input_schema": {
         "type": "object",
         "properties": {
-          "cookie": {
+          "prompt": {
             "type": "string",
-            "description": "Midjourney 会话 cookie"
+            "description": "正向提示词。描述图像的视觉内容,越丰富效果越好。"
           },
-          "prompt": {
+          "aspect_ratio": {
             "type": "string",
-            "description": "提示词"
+            "description": "图像宽高比。支持从 3:7 (竖版) 到 7:3 (横版) 的连续宽高比范围,如'1:1'(默认), '16:9', '9:16', '3:2', '2:3'。",
+            "default": "16:9"
           },
-          "user_id": {
+          "model": {
             "type": "string",
-            "description": "用户 ID"
+            "description": "模型选型。支持 flux-2-flex, flux-2-klein-4b, flux-2-klein-9b, flux-2-max, flux-2-pro, flux-dev, flux-kontext-max, flux-kontext-pro",
+            "default": "flux-kontext-pro",
+            "enum": [
+              "flux-2-flex",
+              "flux-2-klein-4b",
+              "flux-2-klein-9b",
+              "flux-2-max",
+              "flux-2-pro",
+              "flux-dev",
+              "flux-kontext-max",
+              "flux-kontext-pro"
+            ]
+          },
+          "seed": {
+            "type": "integer",
+            "description": "随机种子。固定种子可以保证在相同提示词下重现相同构图结果。"
           },
-          "mode": {
+          "safety_tolerance": {
+            "type": "integer",
+            "description": "内容安全级别控制,0=最严格,6=最宽松,默认位2",
+            "default": 2
+          },
+          "output_format": {
             "type": "string",
+            "description": "输出格式,如'jpeg'或'png'",
+            "default": "png",
             "enum": [
-              "relaxed",
-              "fast"
-            ],
-            "description": "队列模式:relaxed 或 fast"
+              "jpeg",
+              "png"
+            ]
+          },
+          "prompt_upsampling": {
+            "type": "boolean",
+            "description": "是否开启提示词增强。设为true时,AI会自动丰富简短的提示词,改善生图细节。",
+            "default": false
+          },
+          "image_url": {
+            "type": "string",
+            "description": "参考图网络外链。若传入此项,则引擎切换至图生图(编辑)模式。强烈建议模型选 flux-kontext-max。"
+          },
+          "mask_url": {
+            "type": "string",
+            "description": "(选填)蒙版原图外链。必须配合 image_url 使用,白色为需要编辑区域,黑色为保护区域。"
           }
         },
         "required": [
-          "cookie",
-          "prompt",
-          "user_id",
-          "mode"
+          "prompt"
         ]
       },
       "output_schema": {
         "type": "object",
-        "description": "上游返回 JSON,通常含 job_id 或 id",
-        "properties": {}
+        "properties": {
+          "images": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "成功生成的图片URL数组(链接10分钟有效,需使用外部工具抓取下载)"
+          },
+          "status": {
+            "type": "string",
+            "description": "结果态"
+          }
+        }
       },
       "stream_support": false,
       "status": "active",
       "backend_runtime": "local",
       "group_ids": [
-        "midjourney_lifecycle"
+        "flux_apiyi_group"
       ]
     },
     {
-      "tool_id": "midjourney_query_job_status",
-      "name": "Midjourney-查询任务状态",
+      "tool_id": "seedream_generate",
+      "name": "SeeDream-4.0图片生成",
       "tool_slug_ids": [],
       "category": "cv",
-      "description": "查询指定任务状态(转发 MIDJOURNEY_API_BASE/query_job_status)。",
+      "description": "基于 APIyi 中转站实现的 SeeDream 图片生成与编辑接口。若传入 image_url 将触发高级图生图(图像编辑)。",
       "input_schema": {
         "type": "object",
         "properties": {
-          "cookie": {
+          "prompt": {
             "type": "string",
-            "description": "Midjourney 会话 cookie"
+            "description": "正向提示词。描述图像的视觉内容。"
           },
-          "job_id": {
+          "model": {
             "type": "string",
-            "description": "submit_job 返回的任务 ID"
+            "description": "使用的模型名。支持4.0, 4.5, 5.0版本",
+            "default": "seedream-5-0-260128",
+            "enum": [
+              "seedream-4-0-250828",
+              "seedream-4-5-251128",
+              "seedream-5-0-260128"
+            ]
+          },
+          "size": {
+            "type": "string",
+            "description": "尺寸分辨率。支持精度如 '1024x1024' 或者直传标准如 '1K', '2K', '4K'。",
+            "default": "2048x2048"
+          },
+          "quality": {
+            "type": "string",
+            "description": "图片质量。'standard' 或 'hd'",
+            "default": "hd",
+            "enum": [
+              "standard",
+              "hd"
+            ]
+          },
+          "n": {
+            "type": "integer",
+            "description": "生成的图片数量",
+            "default": 1
+          },
+          "image_url": {
+            "type": "string",
+            "description": "参考图网络外链。若传入此项,则引擎切换至图生图(编辑)模式。"
           }
         },
         "required": [
-          "cookie",
-          "job_id"
+          "prompt"
         ]
       },
       "output_schema": {
         "type": "object",
-        "description": "上游状态 JSON,如 status / job_status 等",
-        "properties": {}
+        "properties": {
+          "images": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "成功生成的图片URL数组。"
+          },
+          "status": {
+            "type": "string",
+            "description": "结果态"
+          }
+        }
       },
       "stream_support": false,
       "status": "active",
       "backend_runtime": "local",
       "group_ids": [
-        "midjourney_lifecycle"
+        "seedream_apiyi_group"
       ]
     },
     {
-      "tool_id": "midjourney_get_image_urls",
-      "name": "Midjourney-获取结果图链接",
-      "tool_slug_ids": [],
+      "tool_id": "runcomfy_check_status",
+      "name": "RunComfy Check Status",
+      "description": "[🔴 暂时外服不可用] 获取当前用户的 RunComfy 机器列表和状态。可以查看是否已有机房资源,防止重复拉起新机器。",
+      "category": "cloud",
+      "backend_runtime": "local",
+      "group_ids": [
+        "runcomfy_lifecycle"
+      ],
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "server_id": {
+            "type": "string",
+            "description": "可选。指定具体机器 ID 查询。为空时查询名下所有机器列表。"
+          }
+        }
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "total": {
+            "type": "integer",
+            "description": "存在的机器总量"
+          },
+          "servers": {
+            "type": "array",
+            "items": {
+              "type": "object"
+            },
+            "description": "机器状态列表,包括 server_id, current_status, main_service_url 等详细信息"
+          }
+        },
+        "required": [
+          "total",
+          "servers"
+        ]
+      },
+      "stream_support": false,
+      "status": "inactive"
+    },
+    {
+      "name": "LibLib AI 模型搜索",
       "category": "cv",
-      "description": "根据 job_id 获取 4 张图 URL(转发 MIDJOURNEY_API_BASE/get_image_urls)。",
+      "description": "通过关键词搜索 LibLib AI 上的最新或特定风格的大模型(比如国风、二次元、写实等)。成功返回会包括一列推荐模型实体,必须从中提取关键的 versionUuid 以获取后续生图能力。",
       "input_schema": {
         "type": "object",
         "properties": {
-          "job_id": {
+          "keyword": {
             "type": "string",
-            "description": "任务 ID"
+            "description": "搜索关键词(如 二次元, XL, 写实)"
           }
         },
         "required": [
-          "job_id"
+          "keyword"
         ]
       },
       "output_schema": {
+        "type": "object"
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "liblib_ai_group"
+      ],
+      "tool_slug_ids": [],
+      "tool_id": "liblibai_model_search"
+    },
+    {
+      "name": "LibLib AI 模型详情",
+      "category": "cv",
+      "description": "通过检索返回的 uuid 和 version_uuid 读取指定大模型的完整上下文。常用来了解它的详细触发词、生成特点或特定参数推荐。",
+      "input_schema": {
         "type": "object",
-        "description": "上游返回 JSON 或 URL 数组,常见字段 image_urls / urls",
         "properties": {
-          "image_urls": {
-            "type": "array",
-            "items": {
-              "type": "string"
-            },
-            "description": "四张图片链接(字段名以实际服务为准)"
+          "uuid": {
+            "type": "string",
+            "description": "核心模型 ID (uuid)"
+          },
+          "version_uuid": {
+            "type": "string",
+            "description": "模型版本 ID (version_uuid 或 versionUuid)"
           }
-        }
+        },
+        "required": [
+          "uuid",
+          "version_uuid"
+        ]
+      },
+      "output_schema": {
+        "type": "object"
       },
       "stream_support": false,
       "status": "active",
       "backend_runtime": "local",
       "group_ids": [
-        "midjourney_lifecycle"
-      ]
+        "liblib_ai_group"
+      ],
+      "tool_slug_ids": [],
+      "tool_id": "liblibai_model_detail"
+    },
+    {
+      "name": "LibLib AI UUID 匹配规则指南",
+      "category": "cv",
+      "description": "读取 Liblib AI 模型 UUID 匹配规则和工作流底模使用大全。在生图参数涉及到 templateUuid,或者在组合 checkpoint 和 ControlNet 且不确定对应的 baseType 和匹配值时,调用此工具来阅读核心指南!以防止你的生图因为参数错误而崩溃。",
+      "input_schema": {
+        "type": "object",
+        "properties": {},
+        "required": []
+      },
+      "output_schema": {
+        "type": "object"
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "liblib_ai_group"
+      ],
+      "tool_slug_ids": [],
+      "tool_id": "liblibai_uuid_matching_rules"
     }
   ],
   "version": "2.0"

+ 781 - 0
data/registry_old.json

@@ -0,0 +1,781 @@
+{
+  "tools": [
+    {
+      "tool_id": "image_stitcher",
+      "name": "图片拼接工具",
+      "category": "cv",
+      "description": "将多张图片按指定方向(水平/垂直/网格)拼接成一张大图。支持间距设置、背景色填充和统一缩放模式。输入输出均为 Base64 编码的 PNG 图片。",
+      "input_schema": {
+        "properties": {
+          "background_color": {
+            "default": "#FFFFFF",
+            "description": "间距填充背景色,十六进制颜色值",
+            "type": "string"
+          },
+          "columns": {
+            "default": 2,
+            "description": "grid 模式下每行的列数",
+            "minimum": 1,
+            "type": "integer"
+          },
+          "direction": {
+            "default": "horizontal",
+            "description": "拼接方向:horizontal=水平,vertical=垂直,grid=网格",
+            "enum": [
+              "horizontal",
+              "vertical",
+              "grid"
+            ],
+            "type": "string"
+          },
+          "images": {
+            "description": "Base64 编码的图片列表,至少 2 张",
+            "items": {
+              "type": "string"
+            },
+            "minItems": 2,
+            "type": "array"
+          },
+          "resize_mode": {
+            "default": "none",
+            "description": "缩放模式:none=不缩放,fit_width=统一宽度,fit_height=统一高度",
+            "enum": [
+              "none",
+              "fit_width",
+              "fit_height"
+            ],
+            "type": "string"
+          },
+          "spacing": {
+            "default": 0,
+            "description": "图片间距(像素)",
+            "minimum": 0,
+            "type": "integer"
+          }
+        },
+        "reason": "图片拼接工具输入参数定义",
+        "required": [
+          "images"
+        ],
+        "type": "object"
+      },
+      "output_schema": {
+        "properties": {
+          "height": {
+            "description": "结果图高度(像素)",
+            "type": "integer"
+          },
+          "image": {
+            "description": "拼接结果,Base64 编码的 PNG 图片",
+            "type": "string"
+          },
+          "width": {
+            "description": "结果图宽度(像素)",
+            "type": "integer"
+          }
+        },
+        "reason": "图片拼接工具输出结果定义",
+        "required": [
+          "image",
+          "width",
+          "height"
+        ],
+        "type": "object"
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [],
+      "tool_slug_ids": []
+    },
+    {
+      "tool_id": "liblibai_controlnet",
+      "name": "LibLib ControlNet 图生图",
+      "category": "cv",
+      "description": "基于 LibLib AI 开放 API 的 ControlNet Canny 图生图工具,支持通过边缘检测控制图像生成",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "image": {
+            "type": "string",
+            "description": "图片来源,支持 Base64 编码的图片数据或 HTTP 图片 URL"
+          },
+          "prompt": {
+            "type": "string",
+            "description": "正向提示词"
+          },
+          "negative_prompt": {
+            "type": "string",
+            "description": "反向提示词",
+            "default": "lowres, bad anatomy, text, error"
+          },
+          "width": {
+            "type": "integer",
+            "description": "输出宽度",
+            "default": 512
+          },
+          "height": {
+            "type": "integer",
+            "description": "输出高度",
+            "default": 512
+          },
+          "steps": {
+            "type": "integer",
+            "description": "采样步数",
+            "default": 20
+          },
+          "cfg_scale": {
+            "type": "number",
+            "description": "CFG Scale",
+            "default": 7
+          },
+          "img_count": {
+            "type": "integer",
+            "description": "生成图片数量",
+            "default": 1
+          },
+          "control_weight": {
+            "type": "number",
+            "description": "ControlNet 权重",
+            "default": 1
+          },
+          "preprocessor": {
+            "type": "integer",
+            "description": "预处理器枚举",
+            "default": 1
+          },
+          "canny_low": {
+            "type": "integer",
+            "description": "Canny 低阈值",
+            "default": 100
+          },
+          "canny_high": {
+            "type": "integer",
+            "description": "Canny 高阈值",
+            "default": 200
+          }
+        },
+        "required": [
+          "image",
+          "prompt"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "images": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "生成的图片 URL 列表"
+          },
+          "task_id": {
+            "type": "string",
+            "description": "任务 ID"
+          },
+          "status": {
+            "type": "string",
+            "description": "任务状态(success/failed/timeout)"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [],
+      "tool_slug_ids": []
+    },
+    {
+      "tool_id": "launch_comfy_env",
+      "name": "Launch ComfyUI Environment",
+      "category": "ai",
+      "description": "启动 RunComfy 云端机器并等待就绪。返回 server_id 用于后续 ComfyUI workflow 执行。需要环境变量 RUNCOMFY_USER_ID 和 API_TOKEN。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "version_id": {
+            "type": "string",
+            "description": "RunComfy workflow version ID",
+            "default": "90f77137-ba75-400d-870f-204c614ae8a3"
+          },
+          "server_type": {
+            "type": "string",
+            "enum": [
+              "medium",
+              "large",
+              "extra-large",
+              "2x-large",
+              "2xl-turbo"
+            ],
+            "description": "机器规格",
+            "default": "medium"
+          },
+          "duration": {
+            "type": "integer",
+            "description": "预估运行时长(秒)",
+            "default": 3600
+          }
+        }
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "server_id": {
+            "type": "string",
+            "description": "机器唯一标识,用于后续操作"
+          },
+          "comfy_url": {
+            "type": "string",
+            "description": "ComfyUI 访问地址"
+          },
+          "status": {
+            "type": "string",
+            "description": "机器状态"
+          },
+          "usage_instruction": {
+            "type": "string",
+            "description": "使用说明"
+          }
+        },
+        "required": [
+          "server_id",
+          "comfy_url",
+          "status",
+          "usage_instruction"
+        ]
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "runcomfy_lifecycle"
+      ],
+      "tool_slug_ids": [
+        "comfyui"
+      ]
+    },
+    {
+      "tool_id": "runcomfy_workflow_executor",
+      "name": "RunComfy Workflow Executor",
+      "category": "image_generation",
+      "description": "在已就绪的 RunComfy 机器上提交 ComfyUI 工作流,上传输入文件,监听执行状态,下载结果图片(不启动/关闭机器)",
+      "input_schema": {
+        "type": "object",
+        "required": [
+          "server_id",
+          "workflow_api"
+        ],
+        "properties": {
+          "server_id": {
+            "type": "string",
+            "description": "已启动的 RunComfy 机器 ID"
+          },
+          "workflow_api": {
+            "type": "object",
+            "description": "ComfyUI workflow_api.json 内容(字典格式)"
+          },
+          "input_files": {
+            "type": "array",
+            "description": "可选的输入文件列表",
+            "items": {
+              "type": "object",
+              "required": [
+                "filename",
+                "type",
+                "base64_data"
+              ],
+              "properties": {
+                "filename": {
+                  "type": "string",
+                  "description": "文件名"
+                },
+                "type": {
+                  "type": "string",
+                  "description": "文件类型:images/loras/checkpoints/vae/controlnet"
+                },
+                "base64_data": {
+                  "type": "string",
+                  "description": "文件的 Base64 编码数据"
+                }
+              }
+            }
+          }
+        }
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "prompt_id": {
+            "type": "string",
+            "description": "任务 ID"
+          },
+          "images": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "结果图片的 Base64 数据列表"
+          },
+          "status": {
+            "type": "string",
+            "description": "执行状态"
+          },
+          "server_id": {
+            "type": "string",
+            "description": "机器 ID"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "runcomfy_lifecycle"
+      ],
+      "tool_slug_ids": [
+        "comfyui"
+      ]
+    },
+    {
+      "tool_id": "runcomfy_stop_env",
+      "name": "RunComfy Stop Service",
+      "category": "cloud",
+      "description": "Stop and delete RunComfy server instances to release resources. Works with launch_comfy_env for complete lifecycle management.",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "server_id": {
+            "type": "string",
+            "description": "The server ID to stop"
+          }
+        },
+        "required": [
+          "server_id"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "server_id": {
+            "type": "string",
+            "description": "The stopped server ID"
+          },
+          "status": {
+            "type": "string",
+            "description": "Deleted, NotFound, or Error"
+          },
+          "message": {
+            "type": "string",
+            "description": "Detailed result message"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "runcomfy_lifecycle"
+      ],
+      "tool_slug_ids": [
+        "comfyui"
+      ]
+    },
+    {
+      "tool_id": "ji_meng_add_task",
+      "name": "即梦-创建任务",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "提交异步任务到上游(本地服务转发 JI_MENG_API_BASE,POST /add_task)。需配置 tools/local/ji_meng/.env。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "task_type": {
+            "type": "string",
+            "enum": [
+              "image",
+              "video"
+            ],
+            "description": "任务类型:图生/文生图或视频"
+          },
+          "prompt": {
+            "type": "string",
+            "description": "任务描述 / 提示词"
+          },
+          "image_url": {
+            "type": "string",
+            "description": "可选,图生类任务时的参考图 URL"
+          }
+        },
+        "required": [
+          "task_type",
+          "prompt"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "task_id": {
+            "type": "string",
+            "description": "任务 ID,用于 ji_meng_query_task"
+          },
+          "status": {
+            "type": "string"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "ji_meng_task_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "ji_meng_query_task",
+      "name": "即梦-查询任务",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "按 task_id 查询任务状态与结果(本地服务转发上游,POST /query_task)。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "task_id": {
+            "type": "string",
+            "description": "ji_meng_add_task 返回的任务 ID"
+          }
+        },
+        "required": [
+          "task_id"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "task_id": {
+            "type": "string"
+          },
+          "status": {
+            "type": "string"
+          },
+          "message": {
+            "type": "string"
+          },
+          "images": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "ji_meng_task_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "nano_banana",
+      "name": "Nano Banana(Gemini 图模)",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "通过 Google Gemini 原生图模 REST generateContent 文生图/图生图。需 GEMINI_API_KEY;可选 gemini-2.5-flash-image、gemini-3.1-flash-image-preview 等。详见 https://ai.google.dev/gemini-api/docs/image-generation?hl=zh-cn#rest",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "prompt": {
+            "type": "string",
+            "description": "提示词(文生图或与参考图配合做编辑)"
+          },
+          "model": {
+            "type": "string",
+            "description": "模型 ID;省略则使用环境变量 GEMINI_IMAGE_MODEL,默认 gemini-2.5-flash-image。示例:gemini-3.1-flash-image-preview"
+          },
+          "aspect_ratio": {
+            "type": "string",
+            "description": "输出宽高比,如 1:1、16:9(对应 generationConfig.imageConfig.aspectRatio)"
+          },
+          "image_size": {
+            "type": "string",
+            "description": "Gemini 3.x 输出规格:512、1K、2K、4K(须大写 K,见官方文档)"
+          },
+          "response_modalities": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "如 [\"TEXT\",\"IMAGE\"] 或 [\"IMAGE\"];省略则由 API 默认"
+          },
+          "images": {
+            "type": "array",
+            "description": "可选参考图,每项为 {mime_type, data},data 为 Base64 或 data URL",
+            "items": {
+              "type": "object",
+              "properties": {
+                "mime_type": {
+                  "type": "string"
+                },
+                "data": {
+                  "type": "string"
+                }
+              },
+              "required": [
+                "data"
+              ]
+            }
+          }
+        },
+        "required": [
+          "prompt"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "images": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "data:mime;base64,... 列表"
+          },
+          "model": {
+            "type": "string",
+            "description": "实际调用的模型 ID"
+          },
+          "text": {
+            "type": "string",
+            "description": "若返回文本部分则在此汇总"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": []
+    },
+    {
+      "tool_id": "flux_submit",
+      "name": "FLUX-提交生图任务",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "向 BFL 提交异步生图任务(POST /v1/{model})。需 BFL_API_KEY;model 为端点名如 flux-2-pro-preview。返回 id、polling_url 供 flux_query 轮询。文档 https://docs.bfl.ai/quick_start/generating_images",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "model": {
+            "type": "string",
+            "description": "端点路径段,如 flux-2-pro-preview、flux-2-max、flux-dev、flux-kontext-pro 等"
+          },
+          "prompt": {
+            "type": "string",
+            "description": "提示词"
+          },
+          "width": {
+            "type": "integer",
+            "description": "输出宽度(像素),可选"
+          },
+          "height": {
+            "type": "integer",
+            "description": "输出高度(像素),可选"
+          },
+          "parameters": {
+            "type": "object",
+            "description": "合并进 BFL 请求体的额外字段(模型专有参数)"
+          }
+        },
+        "required": [
+          "model",
+          "prompt"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "string",
+            "description": "请求 ID,flux_query 的 request_id"
+          },
+          "polling_url": {
+            "type": "string",
+            "description": "轮询地址,须原样传给 flux_query(全球端点必须用返回的 URL)"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "flux_bfl_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "flux_query",
+      "name": "FLUX-查询任务结果",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "按 flux_submit 返回的 polling_url 与 id 轮询状态;Ready 时 result.sample 为签名图 URL(约 10 分钟有效)。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "polling_url": {
+            "type": "string",
+            "description": "提交响应中的 polling_url"
+          },
+          "request_id": {
+            "type": "string",
+            "description": "提交响应中的 id"
+          }
+        },
+        "required": [
+          "polling_url",
+          "request_id"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "properties": {
+          "status": {
+            "type": "string",
+            "description": "如 Ready、Pending、Error、Failed 等"
+          },
+          "result": {
+            "type": "object",
+            "description": "成功时常含 sample(图片签名 URL)"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "flux_bfl_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "midjourney_submit_job",
+      "name": "Midjourney-提交生图任务",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "提交 Midjourney 生图任务(转发至 MIDJOURNEY_API_BASE/submit_job)。需配置 tools/local/midjourney/.env。mode 为 relaxed 或 fast。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "cookie": {
+            "type": "string",
+            "description": "Midjourney 会话 cookie"
+          },
+          "prompt": {
+            "type": "string",
+            "description": "提示词"
+          },
+          "user_id": {
+            "type": "string",
+            "description": "用户 ID"
+          },
+          "mode": {
+            "type": "string",
+            "enum": [
+              "relaxed",
+              "fast"
+            ],
+            "description": "队列模式:relaxed 或 fast"
+          }
+        },
+        "required": [
+          "cookie",
+          "prompt",
+          "user_id",
+          "mode"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "description": "上游返回 JSON,通常含 job_id 或 id",
+        "properties": {}
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "midjourney_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "midjourney_query_job_status",
+      "name": "Midjourney-查询任务状态",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "查询指定任务状态(转发 MIDJOURNEY_API_BASE/query_job_status)。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "cookie": {
+            "type": "string",
+            "description": "Midjourney 会话 cookie"
+          },
+          "job_id": {
+            "type": "string",
+            "description": "submit_job 返回的任务 ID"
+          }
+        },
+        "required": [
+          "cookie",
+          "job_id"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "description": "上游状态 JSON,如 status / job_status 等",
+        "properties": {}
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "midjourney_lifecycle"
+      ]
+    },
+    {
+      "tool_id": "midjourney_get_image_urls",
+      "name": "Midjourney-获取结果图链接",
+      "tool_slug_ids": [],
+      "category": "cv",
+      "description": "根据 job_id 获取 4 张图 URL(转发 MIDJOURNEY_API_BASE/get_image_urls)。",
+      "input_schema": {
+        "type": "object",
+        "properties": {
+          "job_id": {
+            "type": "string",
+            "description": "任务 ID"
+          }
+        },
+        "required": [
+          "job_id"
+        ]
+      },
+      "output_schema": {
+        "type": "object",
+        "description": "上游返回 JSON 或 URL 数组,常见字段 image_urls / urls",
+        "properties": {
+          "image_urls": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "description": "四张图片链接(字段名以实际服务为准)"
+          }
+        }
+      },
+      "stream_support": false,
+      "status": "active",
+      "backend_runtime": "local",
+      "group_ids": [
+        "midjourney_lifecycle"
+      ]
+    }
+  ],
+  "version": "2.0"
+}

+ 79 - 20
data/sources.json

@@ -25,13 +25,13 @@
         "hub_api_key": "",
         "endpoint_path": "/generate",
         "http_method": "POST",
-        "internal_port": 8001
+        "internal_port": 8101
       }
     ],
     "launch_comfy_env": [
       {
         "type": "local",
-        "host_dir": "C:\\Users\\11304\\gitlab\\cybertogether\\tool_agent\\tools\\local\\launch_comfy_env",
+        "host_dir": "tools/local/launch_comfy_env",
         "container_id": "",
         "image": "",
         "hub_url": "",
@@ -39,13 +39,13 @@
         "hub_api_key": "",
         "endpoint_path": "/launch",
         "http_method": "POST",
-        "internal_port": 8001
+        "internal_port": 8101
       }
     ],
     "runcomfy_workflow_executor": [
       {
         "type": "local",
-        "host_dir": "C:\\Users\\11304\\gitlab\\cybertogether\\tool_agent\\tools\\local\\task_0cd69d84",
+        "host_dir": "tools/local/runcomfy_workflow_executor",
         "container_id": "",
         "image": "",
         "hub_url": "",
@@ -59,7 +59,7 @@
     "runcomfy_stop_env": [
       {
         "type": "local",
-        "host_dir": "C:\\Users\\11304\\gitlab\\cybertogether\\tool_agent\\tools\\local\\runcomfy_stop_env",
+        "host_dir": "tools/local/runcomfy_stop_env",
         "container_id": "",
         "image": "",
         "hub_url": "",
@@ -112,35 +112,35 @@
         "internal_port": 0
       }
     ],
-    "flux_submit": [
+    "midjourney_submit_job": [
       {
         "type": "local",
-        "host_dir": "tools/local/flux",
+        "host_dir": "tools/local/midjourney",
         "container_id": "",
         "image": "",
         "hub_url": "",
         "hub_tool_path": "",
         "hub_api_key": "",
-        "endpoint_path": "/submit",
+        "endpoint_path": "/submit_job",
         "http_method": "POST",
         "internal_port": 0
       }
     ],
-    "flux_query": [
+    "midjourney_query_job_status": [
       {
         "type": "local",
-        "host_dir": "tools/local/flux",
+        "host_dir": "tools/local/midjourney",
         "container_id": "",
         "image": "",
         "hub_url": "",
         "hub_tool_path": "",
         "hub_api_key": "",
-        "endpoint_path": "/query",
+        "endpoint_path": "/query_job_status",
         "http_method": "POST",
         "internal_port": 0
       }
     ],
-    "midjourney_submit_job": [
+    "midjourney_get_image_urls": [
       {
         "type": "local",
         "host_dir": "tools/local/midjourney",
@@ -149,38 +149,97 @@
         "hub_url": "",
         "hub_tool_path": "",
         "hub_api_key": "",
-        "endpoint_path": "/submit_job",
+        "endpoint_path": "/get_image_urls",
         "http_method": "POST",
         "internal_port": 0
       }
     ],
-    "midjourney_query_job_status": [
+    "flux_generate": [
       {
         "type": "local",
-        "host_dir": "tools/local/midjourney",
+        "host_dir": "tools/local/flux",
         "container_id": "",
         "image": "",
         "hub_url": "",
         "hub_tool_path": "",
         "hub_api_key": "",
-        "endpoint_path": "/query_job_status",
+        "endpoint_path": "/generate",
         "http_method": "POST",
-        "internal_port": 0
+        "internal_port": 8002
       }
     ],
-    "midjourney_get_image_urls": [
+    "seedream_generate": [
       {
         "type": "local",
-        "host_dir": "tools/local/midjourney",
+        "host_dir": "tools/local/seedream",
         "container_id": "",
         "image": "",
         "hub_url": "",
         "hub_tool_path": "",
         "hub_api_key": "",
-        "endpoint_path": "/get_image_urls",
+        "endpoint_path": "/generate",
+        "http_method": "POST",
+        "internal_port": 8003
+      }
+    ],
+    "runcomfy_check_status": [
+      {
+        "type": "local",
+        "host_dir": "tools/local/runcomfy_check_status",
+        "container_id": "",
+        "image": "",
+        "remote_url": "",
+        "remote_path": "",
+        "remote_api_key": "",
+        "hub_url": "",
+        "hub_tool_path": "",
+        "hub_api_key": "",
+        "endpoint_path": "/",
         "http_method": "POST",
         "internal_port": 0
       }
+    ],
+    "liblibai_model_search": [
+      {
+        "type": "local",
+        "host_dir": "tools/local/liblibai_controlnet",
+        "container_id": "",
+        "image": "",
+        "hub_url": "",
+        "hub_tool_path": "",
+        "hub_api_key": "",
+        "endpoint_path": "/search_models",
+        "http_method": "POST",
+        "internal_port": 8101
+      }
+    ],
+    "liblibai_model_detail": [
+      {
+        "type": "local",
+        "host_dir": "tools/local/liblibai_controlnet",
+        "container_id": "",
+        "image": "",
+        "hub_url": "",
+        "hub_tool_path": "",
+        "hub_api_key": "",
+        "endpoint_path": "/model_detail",
+        "http_method": "POST",
+        "internal_port": 8101
+      }
+    ],
+    "liblibai_uuid_matching_rules": [
+      {
+        "type": "local",
+        "host_dir": "tools/local/liblibai_controlnet",
+        "container_id": "",
+        "image": "",
+        "hub_url": "",
+        "hub_tool_path": "",
+        "hub_api_key": "",
+        "endpoint_path": "/uuid_matching_rules",
+        "http_method": "GET",
+        "internal_port": 8101
+      }
     ]
   }
 }

+ 1 - 0
pyproject.toml

@@ -45,4 +45,5 @@ members = [
     "tools/local/nano_banana",
     "tools/local/flux",
     "tools/local/midjourney",
+    "tools/local/runcomfy_check_status",
 ]

+ 1 - 1
src/tool_agent/__main__.py

@@ -29,7 +29,7 @@ async def main():
         try:
             await session_mgr.start_im(
                 contact_id="tool_agent",
-                server_url="ws://localhost:8000",
+                server_url=os.environ.get("IM_SERVER_URL", "ws://localhost:8105"),
             )
         except Exception as e:
             logger.warning(f"IM Client 启动失败(HTTP 不受影响): {e}")

BIN
src/tool_agent/__pycache__/__init__.cpython-312.pyc


BIN
src/tool_agent/__pycache__/__main__.cpython-312.pyc


BIN
src/tool_agent/__pycache__/config.cpython-312.pyc


BIN
src/tool_agent/__pycache__/models.cpython-312.pyc


BIN
src/tool_agent/registry/__pycache__/__init__.cpython-312.pyc


BIN
src/tool_agent/registry/__pycache__/registry.cpython-312.pyc


BIN
src/tool_agent/router/__pycache__/__init__.cpython-312.pyc


BIN
src/tool_agent/router/__pycache__/agent.cpython-312.pyc


BIN
src/tool_agent/router/__pycache__/dispatcher.cpython-312.pyc


BIN
src/tool_agent/router/__pycache__/server.cpython-312.pyc


BIN
src/tool_agent/router/__pycache__/status.cpython-312.pyc


+ 14 - 0
src/tool_agent/router/dispatcher.py

@@ -22,6 +22,20 @@ class Dispatcher:
 
     async def dispatch(self, tool_id: str, params: dict[str, Any], stream: bool = False) -> dict[str, Any]:
         """分发调用请求到工具的活跃端点;无可用端点时先尝试启动(本地 uv 进程等)。"""
+        
+        # 0. 拦截处于维护或不可用的工具
+        BLOCKED_TOOLS = {
+            "launch_comfy_env", "runcomfy_workflow_executor", "runcomfy_stop_env", "runcomfy_check_status",
+            "ji_meng_add_task", "ji_meng_query_task",
+            "midjourney_submit_job", "midjourney_query_job_status", "midjourney_get_image_urls"
+        }
+        if tool_id in BLOCKED_TOOLS:
+            logger.warning(f"Intercepted call to blocked tool: {tool_id}")
+            return {
+                "status": "error", 
+                "error": f"Tool '{tool_id}' 暂时不可用或正处于停机维护状态,请尝试换一种生图工具(如 flux, seedream, liblibai 等)。"
+            }
+
         sm = self._status_manager
         endpoint = sm.get_active_endpoint(tool_id)
         if not endpoint:

+ 57 - 0
src/tool_agent/router/server.py

@@ -24,6 +24,10 @@ logger = logging.getLogger(__name__)
 
 # ---- 请求/响应模型 ----
 
+class SearchToolsRequest(BaseModel):
+    keyword: str | None = None
+
+
 class RunToolRequest(BaseModel):
     tool_id: str
     params: dict[str, Any] = Field(default_factory=dict)
@@ -114,6 +118,59 @@ def create_app(router: Router, session_manager: SessionManager = None) -> FastAP
             "total": len(tools),
         }
 
+    # ---- 2.5. 检索工具表 ----
+
+    @app.post("/search_tools")
+    async def search_tools(request: SearchToolsRequest):
+        kw = (request.keyword or "").lower()
+        tools = []
+        for tool in router.registry.list_all():
+            if kw:
+                name_str = (tool.name or "").lower()
+                desc_str = (tool.description or "").lower()
+                id_str = (tool.tool_id or "").lower()
+                if kw not in name_str and kw not in desc_str and kw not in id_str:
+                    continue
+
+            source = router.status_manager.get_primary_source(tool.tool_id)
+            route = router.status_manager.get_status(tool.tool_id)
+            
+            # 解析 params 兼容老的 CLI 工具
+            params = []
+            if tool.input_schema and "properties" in tool.input_schema:
+                required_fields = tool.input_schema.get("required", [])
+                for param_name, param_def in tool.input_schema["properties"].items():
+                    params.append({
+                        "name": param_name,
+                        "type": param_def.get("type", "string"),
+                        "required": param_name in required_fields,
+                        "description": param_def.get("description", ""),
+                        "default": param_def.get("default"),
+                        "enum": param_def.get("enum")
+                    })
+
+            tools.append({
+                "tool_id": tool.tool_id,
+                "name": tool.name,
+                "description": tool.description,
+                "category": tool.category,
+                "backend_runtime": tool.backend_runtime.value if tool.backend_runtime else "unknown",
+                "group_ids": tool.group_ids,
+                "input_schema": tool.input_schema,
+                "output_schema": tool.output_schema,
+                "params": params,
+                "state": route.state.value if route else "stopped",
+                "port": route.port if route else None,
+                "host_dir": source.host_dir if source else None,
+                "endpoint_path": source.endpoint_path if source else None,
+                "http_method": source.http_method if source else None,
+            })
+
+        return {
+            "total": len(tools),
+            "tools": tools
+        }
+
     # ---- 3. 调用工具 ----
 
     @app.post("/run_tool")

+ 32 - 0
summarize.py

@@ -0,0 +1,32 @@
+import json
+
+def summarize_json(data, max_depth=2, current_depth=0, list_sample_size=1):
+    if isinstance(data, dict):
+        if current_depth >= max_depth:
+            return f"dict with keys: {list(data.keys())[:10]}" + ("..." if len(data.keys()) > 10 else "")
+        result = {}
+        for k, v in data.items():
+             result[k] = summarize_json(v, max_depth, current_depth + 1)
+        return result
+    elif isinstance(data, list):
+        if len(data) == 0: return []
+        if current_depth >= max_depth:
+            return f"list of length {len(data)}"
+        sample = [summarize_json(data[i], max_depth, current_depth + 1) for i in range(min(list_sample_size, len(data)))]
+        if len(data) > list_sample_size:
+             sample.append(f"... and {len(data)-list_sample_size} more items")
+        return sample
+    elif isinstance(data, str):
+        val = data.replace('\n', ' ')
+        return val[:100] + "..." if len(val) > 100 else val
+    else:
+        return data
+
+for file in ["tmp_api1.json", "tmp_api2.json"]:
+    print(f"\n================ {file} ================")
+    try:
+        with open(file, "r") as f:
+            js = json.load(f)
+            print(json.dumps(summarize_json(js, max_depth=4), indent=2, ensure_ascii=False))
+    except Exception as e:
+        print(f"Error: {e}")

+ 17 - 0
summarize2.py

@@ -0,0 +1,17 @@
+import json
+
+with open("tmp_api2.json", "r") as f:
+    js = json.load(f)
+    page_props = js.get("data", {}).get("data", {}).get("props", {}).get("pageProps", {})
+    
+    print("=== modelInfo keys ===")
+    model_info = page_props.get("modelInfo", {})
+    if isinstance(model_info, dict):
+        print(", ".join(model_info.keys()))
+        print("\n=== modelInfo summary ===")
+        print("Name:", model_info.get("name"))
+        print("Type:", model_info.get("modelTypeName"))
+        print("Desc length:", len(str(model_info.get("description", ""))))
+        print("Versions count:", len(model_info.get("versions", [])))
+    else:
+        print(type(model_info))

+ 16 - 0
summarize3.py

@@ -0,0 +1,16 @@
+import json
+
+with open("tmp_api2.json", "r") as f:
+    js = json.load(f)
+    page_props = js.get("data", {}).get("data", {}).get("props", {}).get("pageProps", {})
+    
+    for k, v in page_props.items():
+        if isinstance(v, dict):
+            print(f"=== {k} dict keys ===")
+            print(", ".join(v.keys()))
+        elif isinstance(v, list):
+            print(f"=== {k} list length ===")
+            print(len(v))
+        else:
+            print(f"=== {k} ===")
+            print(str(v)[:100])

+ 1 - 0
t2.jpg

@@ -0,0 +1 @@
+test2

+ 1 - 0
test.jpg

@@ -0,0 +1 @@
+test image data

+ 135 - 0
tests/call_nano_banana.py

@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+🍌 Nano Banana (千层套路·多模态生成) 外部调用脚本
+
+【工具定位】:它使用的是 Google 最新的多模态 Imagen 3 引擎(gemini-3.1-flash-image-preview)。
+【独门绝技 - 跨模态多图推理】:
+与传统的 SD / Flux (图生图) 的本质区别在于,你可以给它丢【任意张完全不同的图片】,
+然后在 prompt 里随心所欲地让它“融梗”交汇。
+比如传入图片 [猫.jpg] 和 [未来城.jpg],让它“把这只猫生成在这座未来城里”。
+
+【参数模式支持】:
+1. 纯文生图:只传 prompt
+2. 单图生图/重绘:传 1 张图 + prompt
+3. 多路意象融合:传 N 张图 + prompt
+
+【本地文件自动上传机制】:
+大模型 API 往往更喜欢吃稳定的 CDN 外链。
+此脚本在启动前会自动检测你传入的图片:如果是本地硬盘文件(比如 examples/cat.png),
+脚本会自动静默调用内部的 OSS 工具,把它秒传为 https://res.cybertogether.net/.. 干净外链,然后投喂给大脑!
+"""
+
+import asyncio
+import os
+import argparse
+import sys
+import httpx
+import json
+
+# 动态引入我们系统现成的 CDN 上传脚本
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+try:
+    from upload import upload_image
+except ImportError:
+    print("错误: 找不到 upload.py。请确保在 tests 目录下运行此脚本。")
+    sys.exit(1)
+
+ROUTER_URL = "http://127.0.0.1:8000/run_tool"
+
+async def process_images(images_list: list[str]) -> list[str]:
+    """处理图片数组,外链直接过,本地文件传 OSS"""
+    final_urls = []
+    for item in images_list:
+        if item.startswith("http://") or item.startswith("https://"):
+            print(f"✅ 检测到公网外链,无需转存: {item}")
+            final_urls.append(item)
+        elif os.path.exists(item):
+            print(f"📦 正在极速倒卖本地图片到 CDN: {item}")
+            try:
+                uploaded_url = await upload_image(item, 'aigc-admin', 'crawler/image')
+                if uploaded_url:
+                    print(f"🚀 上传成功: {uploaded_url}")
+                    final_urls.append(uploaded_url)
+                else:
+                    print(f"❌ 上传失败: {item}")
+            except Exception as e:
+                print(f"❌ 上传报错: {e}")
+        else:
+            print(f"⚠️ 跳过找不到的本地文件或无法识别的格式: {item}")
+    return final_urls
+
+async def run_nano_banana(prompt: str, images: list[str] = None, model: str = None):
+    print(f"\n=======================")
+    print(f"🍌 Nano Banana 启动中...")
+    print(f"=======================")
+
+    # 1. 整理图片
+    final_image_urls = []
+    if images and len(images) > 0:
+        print(f"🔍 检查到传了 {len(images)} 张神秘原图,准备过安检...")
+        final_image_urls = await process_images(images)
+    
+    # 2. 组装发给大模型中枢 Router 的参数
+    params = {
+        "prompt": prompt,
+        "image_urls": final_image_urls if final_image_urls else None
+    }
+    
+    if model:
+        params["model"] = model
+
+    payload = {
+        "tool_id": "nano_banana",
+        "params": params
+    }
+
+    # 3. 轰入 API
+    print("\n⚡ 正在呼叫总后台路由节点打怪...")
+    try:
+        async with httpx.AsyncClient(timeout=300.0) as client:
+            resp = await client.post(ROUTER_URL, json=payload)
+            resp.raise_for_status()
+            
+            result = resp.json()
+            if result.get("status") == "success":
+                gen_data = result.get("result", {})
+                
+                print("\n🎉 === 生成成功! ===")
+                # 打印文本回复
+                if gen_data.get("text"):
+                    print(f"\n💬 模型回话:\n{gen_data.get('text')}")
+                
+                # 打印图片输出
+                images_out = gen_data.get("images", [])
+                if images_out:
+                    print("\n🖼️ 吐出的神图 (Base64 数据流已转为你本地文件):")
+                    for idx, img_b64 in enumerate(images_out):
+                        # 处理前缀 data:image/jpeg;base64,
+                        if img_b64.startswith("data:"):
+                            mime_split = img_b64.split(";base64,")
+                            if len(mime_split) == 2:
+                                ext = mime_split[0].split("/")[-1]
+                                raw_data = mime_split[1]
+                                
+                                import base64
+                                save_path = f"banana_output_{idx}.{ext}"
+                                with open(save_path, "wb") as f:
+                                    f.write(base64.b64decode(raw_data))
+                                print(f" 💾 已保存到本地 -> {save_path}")
+            else:
+                print(f"❌ 大模型傲娇了: {result.get('error')}")
+                
+    except Exception as e:
+        print(f"💥 网络大爆炸报错: {e}")
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="调用 Nano Banana 进行任意风格的多段融合魔法")
+    parser.add_argument("-p", "--prompt", type=str, required=True, help="你想对 AI 喊瞎什么 (比如:用图1的赛博风画一只图2里的猫)")
+    parser.add_argument("-i", "--images", type=str, nargs="+", help="无限追加的垫图清单(可以是现成的 http 链接,也可以是你电脑里的硬盘文件如 example.png)")
+    parser.add_argument("-m", "--model", type=str, default=None, help="覆盖模型 (默认后台会走 gemini-3.1-flash-image-preview)")
+    
+    args = parser.parse_args()
+    
+    asyncio.run(run_nano_banana(prompt=args.prompt, images=args.images, model=args.model))

BIN
tests/features/background_asset/background_bokeh_img2.png


BIN
tests/features/background_asset/background_green_img1.png


BIN
tests/features/background_asset/background_green_img4.png


+ 62 - 0
tests/features/background_asset/mapping.json

@@ -0,0 +1,62 @@
+{
+  "dimension": "background_asset",
+  "type": "实质",
+  "highlight_cluster": "清新雅致的白绿配色 / 唯美梦幻的光影与景深",
+  "description": "户外自然背景素材,包含三种典型状态:清晰绿色草地+树木(img_1)、高饱和绿色背景(img_4)、逆光+散景梦幻背景(img_2)",
+  "files": {
+    "background_green_img1.png": {
+      "source_image": "img_1",
+      "state": "clear green trees and grass, soft natural light",
+      "purpose": "清晰自然绿色背景参考,白绿配色基础"
+    },
+    "background_green_img4.png": {
+      "source_image": "img_4",
+      "state": "vivid saturated green trees and grass, bright daylight",
+      "purpose": "高饱和绿色背景参考,白绿配色最典型"
+    },
+    "background_bokeh_img2.png": {
+      "source_image": "img_2",
+      "state": "backlit green trees with lens flare and bokeh light spots",
+      "purpose": "逆光散景梦幻背景参考,光影景深亮点"
+    }
+  },
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1.3",
+      "type": "实质",
+      "feature": "自然绿色背景(草地+树木)",
+      "highlight": "清新雅致的白绿配色"
+    },
+    {
+      "image": "img_2",
+      "paragraph": "段落2.3",
+      "type": "实质",
+      "feature": "逆光散景自然背景",
+      "highlight": "唯美梦幻的光影与景深"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3.3",
+      "type": "实质",
+      "feature": "自然背景(含远处建筑)",
+      "highlight": "唯美梦幻的光影与景深"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4.3",
+      "type": "实质",
+      "feature": "高饱和绿色自然背景",
+      "highlight": "清新雅致的白绿配色"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5.3",
+      "type": "实质",
+      "feature": "虚化绿色草地背景",
+      "highlight": "唯美梦幻的光影与景深"
+    }
+  ],
+  "consistency_elements": ["自然背景"],
+  "generation_notes": "背景主色调为高饱和度草木绿(HSV: 100-120°, S>150, V>100),包含草地(下半部分)和树木(上半部分),img_2/3/5有逆光散景效果"
+}

BIN
tests/features/character_asset/character_ref_back.png


BIN
tests/features/character_asset/character_ref_img1.png


BIN
tests/features/character_asset/character_ref_kneel.png


BIN
tests/features/character_asset/character_ref_main.png


BIN
tests/features/character_asset/character_ref_side.png


+ 65 - 0
tests/features/character_asset/mapping.json

@@ -0,0 +1,65 @@
+{
+  "dimension": "character_asset",
+  "type": "实质",
+  "highlight_cluster": "优雅的白裙写生少女",
+  "description": "白裙写生少女人物角色素材,作为图集一致性基座。棕色长直发,纯白宽松长袖连衣裙,手持画笔和调色板。",
+  "files": {
+    "character_ref_back.png": {
+      "source_image": "img_1",
+      "view": "3/4 back view",
+      "purpose": "IP-Adapter主参考图"
+    },
+    "character_ref_side.png": {
+      "source_image": "img_4",
+      "view": "side profile",
+      "purpose": "侧面参考"
+    },
+    "character_ref_kneel.png": {
+      "source_image": "img_3",
+      "view": "kneeling pose",
+      "purpose": "跪坐姿态参考"
+    }
+  },
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1.1",
+      "type": "实质",
+      "feature": "人物角色基座",
+      "highlight": "优雅的白裙写生少女"
+    },
+    {
+      "image": "img_2",
+      "paragraph": "段落2.1",
+      "type": "实质",
+      "feature": "人物角色基座",
+      "highlight": "优雅的白裙写生少女"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3.1",
+      "type": "实质",
+      "feature": "人物角色基座",
+      "highlight": "优雅的白裙写生少女"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4.1.1",
+      "type": "实质",
+      "feature": "人物角色基座",
+      "highlight": "优雅的白裙写生少女"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5.1",
+      "type": "实质",
+      "feature": "人物角色基座",
+      "highlight": "优雅的白裙写生少女"
+    }
+  ],
+  "consistency_elements": [
+    "女性",
+    "女性衣物"
+  ],
+  "generation_notes": "使用IP-Adapter注入人物一致性,配合DWPose控制姿态"
+}

Plik diff jest za duży
+ 2 - 0
tests/features/color_scheme/color_scheme.json


+ 150 - 0
tests/features/color_scheme/color_scheme_complete.json

@@ -0,0 +1,150 @@
+{
+  "dimension": "color_scheme",
+  "type": "形式",
+  "highlight_cluster": "清新雅致的白绿配色",
+  "description": "大面积高饱和度自然草木绿背景与人物衣着纯白形成鲜明对比,确立清新自然治愈感的森系视觉基调",
+  "core_contrast": {
+    "formula": "Pure White (#FFFFFF) vs Vivid Green (HSV: 100-120°, S>60%, V>40%)",
+    "contrast_ratio": ">4:1",
+    "sd_tokens": "pure white dress, vivid green background, high contrast, fresh natural color palette, forest aesthetic"
+  },
+  "color_groups": {
+    "white_green_contrast": {
+      "primary_white": {
+        "hex": "#FFFFFF",
+        "rgb": [
+          255,
+          255,
+          255
+        ],
+        "role": "人物白裙 - 纯净主体色"
+      },
+      "vivid_green_1": {
+        "hex": "#4A7C3F",
+        "rgb": [
+          74,
+          124,
+          63
+        ],
+        "role": "自然草木绿 - 高饱和背景"
+      },
+      "vivid_green_2": {
+        "hex": "#6C9255",
+        "rgb": [
+          108,
+          146,
+          85
+        ],
+        "role": "浅草绿 - 中景草地"
+      },
+      "deep_green": {
+        "hex": "#1F2D1E",
+        "rgb": [
+          31,
+          45,
+          30
+        ],
+        "role": "深绿 - 树木阴影"
+      }
+    },
+    "warm_light_accent": {
+      "golden_hour": {
+        "hex": "#D4A853",
+        "rgb": [
+          212,
+          168,
+          83
+        ],
+        "role": "逆光金色 - 光晕暖色"
+      },
+      "warm_skin": {
+        "hex": "#E8C4A0",
+        "rgb": [
+          232,
+          196,
+          160
+        ],
+        "role": "肤色暖调"
+      }
+    },
+    "palette_colors": {
+      "palette_green": {
+        "hex": "#4A8C3F",
+        "rgb": [
+          74,
+          140,
+          63
+        ],
+        "role": "调色板绿色颜料"
+      },
+      "palette_blue": {
+        "hex": "#2E5FA3",
+        "rgb": [
+          46,
+          95,
+          163
+        ],
+        "role": "调色板蓝色颜料"
+      },
+      "palette_red": {
+        "hex": "#C0392B",
+        "rgb": [
+          192,
+          57,
+          43
+        ],
+        "role": "调色板红色颜料"
+      },
+      "palette_yellow": {
+        "hex": "#F1C40F",
+        "rgb": [
+          241,
+          196,
+          15
+        ],
+        "role": "调色板黄色颜料"
+      },
+      "palette_purple": {
+        "hex": "#8E44AD",
+        "rgb": [
+          142,
+          68,
+          173
+        ],
+        "role": "调色板紫色颜料"
+      }
+    }
+  },
+  "per_image_dominant": {
+    "img_1": {
+      "primary": "#3C4C34",
+      "secondary": "#FFFFFF",
+      "accent": "#8E9E9F",
+      "mood": "清新自然"
+    },
+    "img_2": {
+      "primary": "#2D4A2A",
+      "secondary": "#FFFFFF",
+      "accent": "#D4A853",
+      "mood": "逆光梦幻"
+    },
+    "img_3": {
+      "primary": "#4A7C3F",
+      "secondary": "#FFFFFF",
+      "accent": "#C8A870",
+      "mood": "温暖田园"
+    },
+    "img_4": {
+      "primary": "#5A8A45",
+      "secondary": "#FFFFFF",
+      "accent": "#7AB060",
+      "mood": "清新明亮"
+    },
+    "img_5": {
+      "primary": "#4A7C3F",
+      "secondary": "#FFFFFF",
+      "accent": "#8B6914",
+      "mood": "艺术创作"
+    }
+  }
+}

BIN
tests/features/color_scheme/color_scheme_visual.png


BIN
tests/features/color_scheme/color_swatch.png


+ 132 - 0
tests/features/color_scheme/img_1_colors.json

@@ -0,0 +1,132 @@
+{
+  "dominant_colors": [
+    {
+      "hex": "#1f2d1e",
+      "rgb": [
+        31,
+        45,
+        30
+      ],
+      "hsv": [
+        58,
+        85,
+        45
+      ],
+      "ratio": 0.199,
+      "percentage": "19.9%"
+    },
+    {
+      "hex": "#3c4c34",
+      "rgb": [
+        60,
+        76,
+        52
+      ],
+      "hsv": [
+        50,
+        81,
+        76
+      ],
+      "ratio": 0.1802,
+      "percentage": "18.0%"
+    },
+    {
+      "hex": "#567642",
+      "rgb": [
+        86,
+        118,
+        66
+      ],
+      "hsv": [
+        48,
+        112,
+        118
+      ],
+      "ratio": 0.1618,
+      "percentage": "16.2%"
+    },
+    {
+      "hex": "#6c9255",
+      "rgb": [
+        108,
+        146,
+        85
+      ],
+      "hsv": [
+        49,
+        107,
+        146
+      ],
+      "ratio": 0.1498,
+      "percentage": "15.0%"
+    },
+    {
+      "hex": "#8e9e9f",
+      "rgb": [
+        142,
+        158,
+        159
+      ],
+      "hsv": [
+        92,
+        27,
+        159
+      ],
+      "ratio": 0.1126,
+      "percentage": "11.3%"
+    },
+    {
+      "hex": "#a6b6bd",
+      "rgb": [
+        166,
+        182,
+        189
+      ],
+      "hsv": [
+        99,
+        31,
+        189
+      ],
+      "ratio": 0.0862,
+      "percentage": "8.6%"
+    },
+    {
+      "hex": "#607d7e",
+      "rgb": [
+        96,
+        125,
+        126
+      ],
+      "hsv": [
+        91,
+        61,
+        126
+      ],
+      "ratio": 0.0554,
+      "percentage": "5.5%"
+    },
+    {
+      "hex": "#c5d3dd",
+      "rgb": [
+        197,
+        211,
+        221
+      ],
+      "hsv": [
+        103,
+        28,
+        221
+      ],
+      "ratio": 0.055,
+      "percentage": "5.5%"
+    }
+  ],
+  "color_temperature": "neutral",
+  "saturation_level": "medium",
+  "brightness_level": "dark",
+  "white_green_contrast": {
+    "description": "High contrast between pure white (dress) and vivid green (natural background)",
+    "white_ratio": 0,
+    "green_ratio": 0.3116
+  }
+}

BIN
tests/features/color_scheme/img_1_palette.png


+ 132 - 0
tests/features/color_scheme/img_2_colors.json

@@ -0,0 +1,132 @@
+{
+  "dominant_colors": [
+    {
+      "hex": "#94b164",
+      "rgb": [
+        148,
+        177,
+        100
+      ],
+      "hsv": [
+        41,
+        111,
+        177
+      ],
+      "ratio": 0.1993,
+      "percentage": "19.9%"
+    },
+    {
+      "hex": "#edf0ee",
+      "rgb": [
+        237,
+        240,
+        238
+      ],
+      "hsv": [
+        70,
+        3,
+        240
+      ],
+      "ratio": 0.1808,
+      "percentage": "18.1%"
+    },
+    {
+      "hex": "#c7d4d4",
+      "rgb": [
+        199,
+        212,
+        212
+      ],
+      "hsv": [
+        90,
+        16,
+        212
+      ],
+      "ratio": 0.1391,
+      "percentage": "13.9%"
+    },
+    {
+      "hex": "#454233",
+      "rgb": [
+        69,
+        66,
+        51
+      ],
+      "hsv": [
+        25,
+        67,
+        69
+      ],
+      "ratio": 0.1192,
+      "percentage": "11.9%"
+    },
+    {
+      "hex": "#808a5f",
+      "rgb": [
+        128,
+        138,
+        95
+      ],
+      "hsv": [
+        37,
+        79,
+        138
+      ],
+      "ratio": 0.1177,
+      "percentage": "11.8%"
+    },
+    {
+      "hex": "#5f604b",
+      "rgb": [
+        95,
+        96,
+        75
+      ],
+      "hsv": [
+        31,
+        56,
+        96
+      ],
+      "ratio": 0.1042,
+      "percentage": "10.4%"
+    },
+    {
+      "hex": "#c5b994",
+      "rgb": [
+        197,
+        185,
+        148
+      ],
+      "hsv": [
+        23,
+        63,
+        197
+      ],
+      "ratio": 0.0879,
+      "percentage": "8.8%"
+    },
+    {
+      "hex": "#8aa6a4",
+      "rgb": [
+        138,
+        166,
+        164
+      ],
+      "hsv": [
+        88,
+        43,
+        166
+      ],
+      "ratio": 0.0518,
+      "percentage": "5.2%"
+    }
+  ],
+  "color_temperature": "neutral",
+  "saturation_level": "low",
+  "brightness_level": "bright",
+  "white_green_contrast": {
+    "description": "High contrast between pure white (dress) and vivid green (natural background)",
+    "white_ratio": 0.1808,
+    "green_ratio": 0.5496
+  }
+}

BIN
tests/features/color_scheme/img_2_palette.png


+ 132 - 0
tests/features/color_scheme/img_3_colors.json

@@ -0,0 +1,132 @@
+{
+  "dominant_colors": [
+    {
+      "hex": "#f5f6f3",
+      "rgb": [
+        245,
+        246,
+        243
+      ],
+      "hsv": [
+        40,
+        3,
+        246
+      ],
+      "ratio": 0.1762,
+      "percentage": "17.6%"
+    },
+    {
+      "hex": "#839c56",
+      "rgb": [
+        131,
+        156,
+        86
+      ],
+      "hsv": [
+        41,
+        114,
+        156
+      ],
+      "ratio": 0.1738,
+      "percentage": "17.4%"
+    },
+    {
+      "hex": "#6b744d",
+      "rgb": [
+        107,
+        116,
+        77
+      ],
+      "hsv": [
+        37,
+        86,
+        116
+      ],
+      "ratio": 0.1655,
+      "percentage": "16.6%"
+    },
+    {
+      "hex": "#a1bd67",
+      "rgb": [
+        161,
+        189,
+        103
+      ],
+      "hsv": [
+        40,
+        116,
+        189
+      ],
+      "ratio": 0.1475,
+      "percentage": "14.8%"
+    },
+    {
+      "hex": "#4f5139",
+      "rgb": [
+        79,
+        81,
+        57
+      ],
+      "hsv": [
+        33,
+        76,
+        81
+      ],
+      "ratio": 0.1119,
+      "percentage": "11.2%"
+    },
+    {
+      "hex": "#cadee1",
+      "rgb": [
+        202,
+        222,
+        225
+      ],
+      "hsv": [
+        94,
+        26,
+        225
+      ],
+      "ratio": 0.1059,
+      "percentage": "10.6%"
+    },
+    {
+      "hex": "#c2c0a4",
+      "rgb": [
+        194,
+        192,
+        164
+      ],
+      "hsv": [
+        28,
+        39,
+        194
+      ],
+      "ratio": 0.0803,
+      "percentage": "8.0%"
+    },
+    {
+      "hex": "#749da1",
+      "rgb": [
+        116,
+        157,
+        161
+      ],
+      "hsv": [
+        93,
+        71,
+        161
+      ],
+      "ratio": 0.0389,
+      "percentage": "3.9%"
+    }
+  ],
+  "color_temperature": "neutral",
+  "saturation_level": "low",
+  "brightness_level": "medium",
+  "white_green_contrast": {
+    "description": "High contrast between pure white (dress) and vivid green (natural background)",
+    "white_ratio": 0.2821,
+    "green_ratio": 0.6629999999999999
+  }
+}

BIN
tests/features/color_scheme/img_3_palette.png


+ 132 - 0
tests/features/color_scheme/img_4_colors.json

@@ -0,0 +1,132 @@
+{
+  "dominant_colors": [
+    {
+      "hex": "#5e6f3f",
+      "rgb": [
+        94,
+        111,
+        63
+      ],
+      "hsv": [
+        41,
+        110,
+        111
+      ],
+      "ratio": 0.2054,
+      "percentage": "20.5%"
+    },
+    {
+      "hex": "#7a7b4c",
+      "rgb": [
+        122,
+        123,
+        76
+      ],
+      "hsv": [
+        31,
+        97,
+        123
+      ],
+      "ratio": 0.1574,
+      "percentage": "15.7%"
+    },
+    {
+      "hex": "#968e58",
+      "rgb": [
+        150,
+        142,
+        88
+      ],
+      "hsv": [
+        26,
+        105,
+        150
+      ],
+      "ratio": 0.1407,
+      "percentage": "14.1%"
+    },
+    {
+      "hex": "#dce2e2",
+      "rgb": [
+        220,
+        226,
+        226
+      ],
+      "hsv": [
+        90,
+        7,
+        226
+      ],
+      "ratio": 0.1277,
+      "percentage": "12.8%"
+    },
+    {
+      "hex": "#50562a",
+      "rgb": [
+        80,
+        86,
+        42
+      ],
+      "hsv": [
+        34,
+        130,
+        86
+      ],
+      "ratio": 0.127,
+      "percentage": "12.7%"
+    },
+    {
+      "hex": "#ad9d7a",
+      "rgb": [
+        173,
+        157,
+        122
+      ],
+      "hsv": [
+        21,
+        75,
+        173
+      ],
+      "ratio": 0.0945,
+      "percentage": "9.4%"
+    },
+    {
+      "hex": "#c2bcac",
+      "rgb": [
+        194,
+        188,
+        172
+      ],
+      "hsv": [
+        22,
+        29,
+        194
+      ],
+      "ratio": 0.0868,
+      "percentage": "8.7%"
+    },
+    {
+      "hex": "#2e321c",
+      "rgb": [
+        46,
+        50,
+        28
+      ],
+      "hsv": [
+        35,
+        112,
+        50
+      ],
+      "ratio": 0.0605,
+      "percentage": "6.0%"
+    }
+  ],
+  "color_temperature": "neutral",
+  "saturation_level": "medium",
+  "brightness_level": "medium",
+  "white_green_contrast": {
+    "description": "High contrast between pure white (dress) and vivid green (natural background)",
+    "white_ratio": 0.1277,
+    "green_ratio": 0.3628
+  }
+}

BIN
tests/features/color_scheme/img_4_palette.png


+ 132 - 0
tests/features/color_scheme/img_5_colors.json

@@ -0,0 +1,132 @@
+{
+  "dominant_colors": [
+    {
+      "hex": "#3d5c2a",
+      "rgb": [
+        61,
+        92,
+        42
+      ],
+      "hsv": [
+        49,
+        139,
+        92
+      ],
+      "ratio": 0.2671,
+      "percentage": "26.7%"
+    },
+    {
+      "hex": "#e9ecef",
+      "rgb": [
+        233,
+        236,
+        239
+      ],
+      "hsv": [
+        105,
+        6,
+        239
+      ],
+      "ratio": 0.2276,
+      "percentage": "22.8%"
+    },
+    {
+      "hex": "#c2cace",
+      "rgb": [
+        194,
+        202,
+        206
+      ],
+      "hsv": [
+        100,
+        15,
+        206
+      ],
+      "ratio": 0.195,
+      "percentage": "19.5%"
+    },
+    {
+      "hex": "#2d7065",
+      "rgb": [
+        45,
+        112,
+        101
+      ],
+      "hsv": [
+        85,
+        153,
+        112
+      ],
+      "ratio": 0.0843,
+      "percentage": "8.4%"
+    },
+    {
+      "hex": "#b0aba5",
+      "rgb": [
+        176,
+        171,
+        165
+      ],
+      "hsv": [
+        16,
+        16,
+        176
+      ],
+      "ratio": 0.0753,
+      "percentage": "7.5%"
+    },
+    {
+      "hex": "#1e2f1e",
+      "rgb": [
+        30,
+        47,
+        30
+      ],
+      "hsv": [
+        60,
+        92,
+        47
+      ],
+      "ratio": 0.0533,
+      "percentage": "5.3%"
+    },
+    {
+      "hex": "#927756",
+      "rgb": [
+        146,
+        119,
+        86
+      ],
+      "hsv": [
+        17,
+        105,
+        146
+      ],
+      "ratio": 0.0524,
+      "percentage": "5.2%"
+    },
+    {
+      "hex": "#6196b6",
+      "rgb": [
+        97,
+        150,
+        182
+      ],
+      "hsv": [
+        101,
+        119,
+        182
+      ],
+      "ratio": 0.045,
+      "percentage": "4.5%"
+    }
+  ],
+  "color_temperature": "neutral",
+  "saturation_level": "low",
+  "brightness_level": "medium",
+  "white_green_contrast": {
+    "description": "High contrast between pure white (dress) and vivid green (natural background)",
+    "white_ratio": 0.2276,
+    "green_ratio": 0.0843
+  }
+}

BIN
tests/features/color_scheme/img_5_palette.png


+ 56 - 0
tests/features/color_scheme/mapping.json

@@ -0,0 +1,56 @@
+{
+  "dimension": "color_scheme",
+  "type": "形式",
+  "highlight_cluster": "清新雅致的白绿配色",
+  "description": "大面积高饱和度自然草木绿背景与人物衣着纯白形成鲜明对比,确立清新自然治愈感的森系视觉基调",
+  "files": {
+    "color_scheme_visual.png": {
+      "type": "视觉参考图",
+      "purpose": "完整色彩方案可视化,包含白绿对比、暖光色调、调色板颜料色三组配色",
+      "tool": "Python PIL生成"
+    },
+    "color_scheme_complete.json": {
+      "type": "完整色彩数据",
+      "purpose": "包含所有颜色组的HEX值、RGB值、角色描述和SD提示词"
+    },
+    "img_1_palette.png": "img_1色板可视化(白绿配色典型)",
+    "img_4_palette.png": "img_4色板可视化(白绿配色最典型)"
+  },
+  "key_colors": {
+    "primary_white": {
+      "hex": "#FFFFFF",
+      "role": "人物白裙,纯净主体色",
+      "sd_token": "pure white dress, white clothing"
+    },
+    "primary_green": {
+      "hex": "#4A7C3F",
+      "role": "自然草木绿背景,高饱和度",
+      "sd_token": "lush green grass, vivid green trees, saturated green background"
+    },
+    "accent_warm": {
+      "hex": "#D4A853",
+      "role": "阳光暖色调,逆光光晕",
+      "sd_token": "warm sunlight, golden hour light"
+    }
+  },
+  "color_contrast_formula": "High contrast: Pure white (#FFFFFF) vs Vivid green (HSV: 100-120°, S>60%, V>40%)",
+  "sd_tokens": "pure white dress, vivid green background, high contrast, fresh natural color palette, forest aesthetic, white and green color scheme",
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1",
+      "type": "形式",
+      "feature": "白绿配色",
+      "specific": "白裙+绿色草地背景",
+      "path": "output/features/color_scheme/color_scheme_visual.png"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4",
+      "type": "形式",
+      "feature": "白绿配色",
+      "specific": "白裙+高饱和绿色背景(最典型)",
+      "path": "output/features/color_scheme/color_scheme_visual.png"
+    }
+  ]
+}

BIN
tests/features/depth_map/depth_img_1.png


BIN
tests/features/depth_map/depth_img_2.png


BIN
tests/features/depth_map/depth_img_3.png


BIN
tests/features/depth_map/depth_img_4.png


BIN
tests/features/depth_map/depth_img_5.png


+ 79 - 0
tests/features/depth_map/mapping.json

@@ -0,0 +1,79 @@
+{
+  "dimension": "depth_map",
+  "type": "形式",
+  "description": "深度图,用于ControlNet深度控制,反映场景空间层次和浅景深效果",
+  "valid_files": {
+    "img_1_depth.png": {
+      "source": "img_1",
+      "method": "局部方差法(清晰度代理深度)",
+      "mean": 72.3
+    },
+    "img_2_depth.png": {
+      "source": "img_2",
+      "method": "局部方差法(清晰度代理深度)",
+      "mean": 79.1
+    },
+    "img_3_depth.png": {
+      "source": "img_3",
+      "method": "局部方差法(清晰度代理深度)",
+      "mean": 93.1
+    },
+    "img_4_depth.png": {
+      "source": "img_4",
+      "method": "局部方差法(清晰度代理深度)",
+      "mean": 82.5
+    },
+    "img_5_depth.png": {
+      "source": "img_5",
+      "method": "局部方差法(清晰度代理深度)",
+      "mean": 65.8
+    }
+  },
+  "tool_limitation": "当前使用OpenCV局部方差法(清晰度作为深度代理),非真实深度估计。建议生产环境使用MiDaS/DPT-Large/Depth-Anything-V2模型获取更精准的深度图。",
+  "recommended_tool": "Depth-Anything-V2 (2024年最新,精度最高) 或 MiDaS v3.1",
+  "depth_interpretation": {
+    "white": "近景(清晰区域)- 人物、画架、调色板",
+    "gray": "中景",
+    "black": "远景(虚化区域)- 背景树木草地"
+  },
+  "mapping": [
+    {
+      "image": "img_2",
+      "paragraph": "段落2",
+      "type": "形式",
+      "feature": "景深空间关系",
+      "highlight": "唯美梦幻的光影与景深",
+      "path": "output/features/depth_map/img_2_depth.png"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3",
+      "type": "形式",
+      "feature": "景深空间关系",
+      "highlight": "唯美梦幻的光影与景深",
+      "path": "output/features/depth_map/img_3_depth.png"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5",
+      "type": "形式",
+      "feature": "景深空间关系",
+      "highlight": "唯美梦幻的光影与景深",
+      "path": "output/features/depth_map/img_5_depth.png"
+    },
+    {
+      "image": "img_1",
+      "paragraph": "段落1",
+      "type": "形式",
+      "feature": "景深空间关系",
+      "path": "output/features/depth_map/img_1_depth.png"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4",
+      "type": "形式",
+      "feature": "景深空间关系",
+      "path": "output/features/depth_map/img_4_depth.png"
+    }
+  ]
+}

BIN
tests/features/easel_asset/easel_blank_canvas_img4.png


+ 57 - 0
tests/features/easel_asset/mapping.json

@@ -0,0 +1,57 @@
+{
+  "dimension": "easel_asset",
+  "type": "实质",
+  "highlight_cluster": "构建叙事的写生道具",
+  "description": "木质三脚画架本体素材,包含两种状态:承载进行中油画的画架(img_3)和承载空白画布+白玫瑰的画架(img_4)",
+  "files": {
+    "easel_with_painting_img3.png": {
+      "source_image": "img_3",
+      "state": "easel with impressionistic oil painting on canvas",
+      "purpose": "画架+进行中油画参考,用于画中画叙事场景"
+    },
+    "easel_blank_canvas_img4.png": {
+      "source_image": "img_4",
+      "state": "easel with blank white canvas + white rose on crossbar",
+      "purpose": "画架+空白画布+白玫瑰参考,用于写生道具叙事场景"
+    }
+  },
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1.2",
+      "type": "实质",
+      "feature": "木质三脚画架",
+      "highlight": "构建叙事的写生道具"
+    },
+    {
+      "image": "img_2",
+      "paragraph": "段落2.2",
+      "type": "实质",
+      "feature": "木质三脚画架",
+      "highlight": "构建叙事的写生道具"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3.2",
+      "type": "实质",
+      "feature": "木质三脚画架(含油画)",
+      "highlight": "构建叙事的写生道具"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4.2",
+      "type": "实质",
+      "feature": "木质三脚画架(空白画布+白玫瑰)",
+      "highlight": "构建叙事的写生道具"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5.2",
+      "type": "实质",
+      "feature": "木质三脚画架(局部可见)",
+      "highlight": "构建叙事的写生道具"
+    }
+  ],
+  "consistency_elements": ["画架与画布", "画架与油画"],
+  "generation_notes": "画架为浅棕色木质三脚架结构,高约150-180cm,宽约60-80cm,顶部有可调节画板支撑架,底部有横向支撑杆"
+}

BIN
tests/features/edge_map/img_1_canny.png


BIN
tests/features/edge_map/img_2_canny.png


BIN
tests/features/edge_map/img_3_canny.png


BIN
tests/features/edge_map/img_4_canny.png


BIN
tests/features/edge_map/img_5_canny.png


+ 45 - 0
tests/features/edge_map/mapping.json

@@ -0,0 +1,45 @@
+{
+  "dimension": "edge_map",
+  "type": "形式",
+  "description": "Canny边缘检测图,用于ControlNet结构控制,保留人物轮廓、画架结构等关键线条",
+  "tool": "OpenCV Canny边缘检测 (threshold: 50-150)",
+  "files": {
+    "img_1_canny.png": "img_1 Canny边缘图",
+    "img_2_canny.png": "img_2 Canny边缘图",
+    "img_3_canny.png": "img_3 Canny边缘图",
+    "img_4_canny.png": "img_4 Canny边缘图",
+    "img_5_canny.png": "img_5 Canny边缘图"
+  },
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1",
+      "type": "形式",
+      "feature": "结构边缘控制"
+    },
+    {
+      "image": "img_2",
+      "paragraph": "段落2",
+      "type": "形式",
+      "feature": "结构边缘控制"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3",
+      "type": "形式",
+      "feature": "结构边缘控制"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4",
+      "type": "形式",
+      "feature": "结构边缘控制"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5",
+      "type": "形式",
+      "feature": "结构边缘控制"
+    }
+  ]
+}

+ 155 - 0
tests/features/lighting_bokeh/lighting_analysis.json

@@ -0,0 +1,155 @@
+{
+  "dimension": "lighting_bokeh",
+  "type": "形式",
+  "highlight_cluster": "唯美梦幻的光影与景深",
+  "description": "逆光/轮廓光与大光圈浅景深(Bokeh)的组合,营造梦幻浪漫的视觉氛围",
+  "per_image_analysis": {
+    "img_2": {
+      "paragraph": "段落2",
+      "analysis": {
+        "lighting_type": {
+          "direction": "Backlit, approximately 45° upper-left from the subject's perspective, emanating from behind the subject and slightly to the left, filtering through foliage.",
+          "quality": "Soft and diffused, originating from a large, natural light source (the sun), with localized hard spots and flaring where direct rays penetrate foliage.",
+          "color_temperature": "Approximately 4500K-5000K, consistent with golden hour or late afternoon sunlight, exhibiting a warm golden hue.",
+          "intensity_ratio": "High key-to-fill ratio, estimated 8:1 to 10:1. The key light (sun) is directly behind, creating significant backlighting and leaving the subject's front in deep shadow. Fill light is minimal, likely ambient bounce from the surroundings, allowing for soft detail in the shadows.",
+          "special_effects": "Pronounced lens flare emanating from the upper-left behind the subject, characterized by bright, amorphous glows and some visible starbursts. Strong rim lighting outlining the subject's hair, shoulders, and parts of the easel, providing separation from the background. Sun rays are partially visible filtering through leaves, creating bright light spots and specular highlights in the background bokeh."
+        },
+        "bokeh_dof": {
+          "estimated_aperture": "f/1.8-f/2.8, indicated by the extremely shallow depth of field, especially in the background foliage and the immediate foreground of the grass.",
+          "focus_plane_description": "The primary focus plane is on the subject's hair and back, extending to the canvas on the easel. The subject's dress and the easel are within the area of acceptable sharpness.",
+          "bokeh_quality": "Circular and smooth, characteristic of a wide aperture and quality lens. The background light sources are rendered as large, soft, circular or slightly elliptical disks of light (bokeh balls).",
+          "background_blur_intensity": "Heavy blur, almost entirely obscuring the detail of distant trees and foliage, rendering them as abstract shapes and colors.",
+          "foreground_blur": "Moderate blur in the grass immediately in front of the subject, softening the texture without completely dissolving it."
+        },
+        "atmosphere": {
+          "overall_mood_keywords": [
+            "dreamy",
+            "serene",
+            "ethereal",
+            "romantic",
+            "artistic"
+          ],
+          "haze_level": "Slight atmospheric haze, contributing to the soft diffusion of light and some reduction in overall contrast, particularly in the background.",
+          "contrast_ratio": "Medium-high contrast due to the strong backlighting and resultant shadow areas on the subject, balanced by the soft, diffused background. Overall image tone leans towards a luminous, slightly desaturated aesthetic."
+        },
+        "sd_prompt_tokens": [
+          "backlit",
+          "rim lighting",
+          "lens flare",
+          "bokeh",
+          "shallow depth of field",
+          "golden hour",
+          "warm light",
+          "f/2.0 aperture",
+          "dreamy atmosphere",
+          "painterly light",
+          "soft focus",
+          "diffused sunlight",
+          "cinematic lighting",
+          "portrait photography",
+          "85mm lens"
+        ]
+      }
+    },
+    "img_3": {
+      "paragraph": "段落3",
+      "analysis": {
+        "lighting_type": {
+          "direction": "15° upper-left backlight (implied sun position)",
+          "quality": "Direct, somewhat diffused by atmospheric haze/distance",
+          "color_temperature": "3200K-3800K warm golden (golden hour)",
+          "intensity_ratio": "Key:fill 4:1 (strong backlight, soft fill from ambient bounce)",
+          "special_effects": "pronounced rim light on subject (hair, dress edges), subtle lens flare top-left, atmospheric glow"
+        },
+        "bokeh_dof": {
+          "estimated_aperture": "f/2.2-f/3.5",
+          "focus_plane_description": "Sharpest focus is on the back of the subject's head/upper back and the painting on the easel. The ground immediately around the subject is also within the acceptable sharpness depth.",
+          "bokeh_quality": "Smooth, circular (implied) for distant background highlights, generally creamy.",
+          "background_blur_intensity": "Heavy for distant trees/structures, moderate for closer trees.",
+          "foreground_blur_if_any": "Slight to moderate blur on the grass immediately in front of the subject's knees, becoming sharper closer to the subject's body."
+        },
+        "atmosphere": {
+          "overall_mood_keywords": [
+            "dreamy",
+            "serene",
+            "idyllic",
+            "romantic",
+            "artistic"
+          ],
+          "haze_fog_level": "Low atmospheric haze, contributing to the soft glow and light diffusion.",
+          "contrast_ratio": "Moderate-low (due to strong backlight and ambient fill, shadows are lifted)"
+        },
+        "sd_prompt_tokens": [
+          "backlit",
+          "rim lighting",
+          "lens flare",
+          "bokeh",
+          "shallow depth of field",
+          "golden hour",
+          "warm light",
+          "f/2.8 aperture",
+          "85mm lens",
+          "dreamy atmosphere",
+          "outdoor portrait",
+          "natural light",
+          "soft focus",
+          "creamy bokeh",
+          "glowing edges"
+        ]
+      }
+    },
+    "img_5": {
+      "paragraph": "段落5",
+      "analysis": {
+        "lighting_type": {
+          "direction": "Approximately 75° upper-right backlight (over-the-shoulder light for the subject's left (our right) side, providing rim light to the right shoulder and subtle glow on the left shoulder and upper arm)",
+          "quality": "Soft and moderately diffused. While the source is direct sunlight, the angle inherently diffuses it for the front of the subject. The grass background suggests an open-sky condition rather than direct harsh sunlight on the primary subject.",
+          "color_temperature": "6000K-6500K cool daylight with a subtle warm undertone from the environment (e.g., golden hour nearing completion, or light reflecting off warm surfaces). There's no distinct 'golden hour' warmth, but a slight warmth compared to pure overcast.",
+          "intensity_ratio": "Key:Fill approximately 3:1 to 4:1. The backlighting creates a slightly higher contrast on the back/rim, but the subject's front is still well-lit by environmental fill, suggesting a relatively bright ambient scene.",
+          "special_effects": "Subtle top-right rim light on the subject's right arm and shoulder, and a gentle glow on the fabric. No discernable lens flare or light leaks."
+        },
+        "bokeh_dof": {
+          "estimated_aperture": "f/2.8-f/4.0",
+          "focus_plane_description": "The primary focus is sharply on the artist's hands and the paint palette. The artist's upper body (torso and arms) is also acceptably sharp, indicating a relatively wide focus plane. The easel (left edge) is slightly softer than the palette, and the background grass is distinctly blurred.",
+          "bokeh_quality": "Smooth, creamy, and uniform bokeh. The highlights in the grass are subtly rendered, suggesting well-rounded aperture blades. No noticeable hexagonal or busy artifacts.",
+          "background_blur_intensity": "Heavy. The background grass is rendered as a soft wash of green tones, completely indistinct, indicating a very shallow depth of field.",
+          "foreground_blur": "Minimal to none, with the exception of the very bottom-left corner of the easel stand being slightly less sharp than the palette."
+        },
+        "atmosphere": {
+          "overall_mood_keywords": [
+            "Serene",
+            "Artistic",
+            "Calm",
+            "Ethereal",
+            "Fresh"
+          ],
+          "haze_fog_level": "None",
+          "contrast_ratio": "Medium-low (soft shadows, highlights are not blown out, good detail retention across mid-tones, but the backlighting does create some subtle contrast on edges)."
+        },
+        "sd_prompt_tokens": [
+          "backlit",
+          "soft light",
+          "rim lighting",
+          "shallow depth of field",
+          "bokeh",
+          "creamy bokeh",
+          "f/2.8",
+          "natural light",
+          "outdoor photography",
+          "daylight",
+          "green background",
+          "artistic",
+          "painter",
+          "close-up",
+          "ethereal atmosphere"
+        ]
+      }
+    }
+  },
+  "common_features": {
+    "lighting_style": "Natural backlight / rim light from upper-left, warm golden hour quality",
+    "bokeh_style": "Shallow DOF with smooth circular bokeh, heavy background blur",
+    "atmosphere": "Dreamy, romantic, ethereal, air-like, warm",
+    "sd_tokens_base": "backlit, rim lighting, bokeh, shallow depth of field, golden hour, warm sunlight, dreamy atmosphere, soft light, lens flare, f/1.8, 85mm lens, outdoor natural light"
+  }
+}

+ 28 - 0
tests/features/lighting_bokeh/lighting_img_2.json

@@ -0,0 +1,28 @@
+{
+  "light_source": {
+    "direction": "Predominantly backlight. The sun is positioned behind the subject, acting as a strong backlight and creating a rim light effect.",
+    "quality": "Soft. The strong backlight is somewhat diffused, most likely due to atmospheric conditions or the presence of clouds dispersing the direct sunlight.",
+    "color_temperature": "Approximately 5500-6000K. Suggesting golden hour or late afternoon light."
+  },
+  "bokeh_dof": {
+    "aperture_estimate": "f/2.8 - f/4. The background exhibits noticeable blur, but not extreme. This suggests a moderate aperture.",
+    "bokeh_shape": "Circular. The highlights in the blurry background of tree leves appear round and smooth.",
+    "background_blur_intensity": "7",
+    "foreground_sharpness": "Soft, not a major emphasis on maximum microcontrast. Appears in focus but softened."
+  },
+  "light_interaction": {
+    "dress": "The white dress glows due to the backlight, producing highlights along its edges. Some translucency may be present if the fabric is thin. The texture is well defined.",
+    "hair": "The hair has significant highlights from the backlight, creating separation from the background creating a visual edge of light.",
+    "rim_light_effect": "Effective rim light creates a glowing outline around the subject, separating the figure from the background."
+  },
+  "atmosphere": {
+    "overall_mood": "Dreamy, romantic, and ethereal. The soft, golden light creates the mood that evokes nostalgia and a sense of peace.",
+    "atmosphere_description": "Relaxed and serene. Warm golden light creates a sense of peacefulness."
+  },
+  "controlnet_params": {
+    "brightness": "1.1",
+    "contrast": "0.95",
+    "saturation": "1.05",
+    "color_temperature": "5800"
+  }
+}

+ 26 - 0
tests/features/lighting_bokeh/lighting_img_3.json

@@ -0,0 +1,26 @@
+{
+  "light_source": {
+    "direction": "Backlight/Rim Light, slightly from the side",
+    "quality": "Soft",
+    "color_temperature": "Approximately 5800K-6200K (Golden Hour, warm)"
+  },
+  "bokeh_dof": {
+    "aperture_estimate": "Around f/2.8 to f/4",
+    "bokeh_shape": "Indiscernible, due to strong blur",
+    "background_blur_intensity": "7",
+    "foreground_sharpness": "The grass in the foreground is moderately sharp but becomes progressively softer with distance from the camera."
+  },
+  "light_interaction": {
+    "dress": "The white dress has a gentle glow and is almost translucent where light contacts it, showing the texture of the garment and creating subtle rim lighting along its edges. There is separation between the dress and the background.",
+    "hair_highlight": "Highlights catch the top layer of the hair and outlines it, creating nice separation. "
+  },
+  "atmosphere": {
+    "mood": "Dreamy, romantic, gentle, nostalgic"
+  },
+  "controlnet_params": {
+    "brightness": "1.05",
+    "contrast": "0.98",
+    "saturation": "1.02",
+    "color_temperature": "6000"
+  }
+}

+ 27 - 0
tests/features/lighting_bokeh/lighting_img_5.json

@@ -0,0 +1,27 @@
+{
+  "light_source": {
+    "direction": "Predominantly soft, diffused backlight from the left.",
+    "quality": "Soft light, lacking hard shadows. Likely overcast sky or large diffusion panel used",
+    "color_temperature": "Approximately 5800K-6000K (daylight)"
+  },
+  "bokeh_dof": {
+    "aperture_estimate": "Estimate of f/2.8 to f/4. The background is moderately blurred.",
+    "bokeh_shape": "Not applicable due to soft focus. What bokeh is discernible has a roundish quality.",
+    "background_blur_intensity": "4/10 (Moderate blur)",
+    "foreground_sharpness": "The artist's palette details and the dress are sharp."
+  },
+  "light_interaction": {
+    "white_dress": "The white dress reflects a significant amount of soft light. There is a slight rim light effect on the edges of the shoulder and sleeves giving it shape and dimension. Some folds show specular highlights due to satin finish. The dress also has some translucency due to the light coming from the other side",
+    "hair_highlight": "Minimal highlights on the hair. Light is even and diffused",
+    "paint_color": "The color looks natural and true."
+  },
+  "atmosphere": {
+    "overall_mood": "The lighting imparts a relaxed and natural tone. It's a bright, daytime scene with a soft, gentle ambiance."
+  },
+  "controlnet_params": {
+    "brightness": "0.9",
+    "contrast": "0.75",
+    "saturation": "0.9",
+    "color_temperature": "5900"
+  }
+}

BIN
tests/features/lighting_bokeh/lighting_visual.png


+ 49 - 0
tests/features/lighting_bokeh/mapping.json

@@ -0,0 +1,49 @@
+{
+  "dimension": "lighting_bokeh",
+  "type": "形式",
+  "highlight_cluster": "唯美梦幻的光影与景深",
+  "description": "逆光/轮廓光与大光圈浅景深(Bokeh)的组合,营造梦幻浪漫的视觉氛围",
+  "files": {
+    "lighting_visual.png": {
+      "type": "视觉参考图",
+      "purpose": "三张图的光影参数对比图,展示光照方向、光圈、色温、散景特征",
+      "tool": "Python PIL生成"
+    },
+    "lighting_analysis.json": {
+      "type": "详细分析",
+      "purpose": "各图光影景深专业分析,包含SD提示词"
+    }
+  },
+  "common_features": {
+    "lighting_style": "Natural backlight / rim light from upper-left, warm golden hour quality",
+    "bokeh_style": "Shallow DOF with smooth circular bokeh, heavy background blur",
+    "atmosphere": "Dreamy, romantic, ethereal, air-like, warm",
+    "sd_tokens_base": "backlit, rim lighting, bokeh, shallow depth of field, golden hour, warm sunlight, dreamy atmosphere, soft light, lens flare, f/1.8, 85mm lens, outdoor natural light"
+  },
+  "mapping": [
+    {
+      "image": "img_2",
+      "paragraph": "段落2",
+      "type": "形式",
+      "feature": "光影景深",
+      "specific": "强逆光+光斑光晕+浅景深 (f/1.8-2.8, 4500K-5000K)",
+      "path": "output/features/lighting_bokeh/lighting_visual.png"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3",
+      "type": "形式",
+      "feature": "光影景深",
+      "specific": "逆光+背景光晕+温暖氛围 (f/2.2-3.5, 3200K-3800K)",
+      "path": "output/features/lighting_bokeh/lighting_visual.png"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5",
+      "type": "形式",
+      "feature": "光影景深",
+      "specific": "自然光+背景虚化+浅景深 (f/2.8-4.0, 6000K-6500K)",
+      "path": "output/features/lighting_bokeh/lighting_visual.png"
+    }
+  ]
+}

+ 79 - 0
tests/features/palette_asset/mapping.json

@@ -0,0 +1,79 @@
+{
+  "dimension": "palette_asset",
+  "type": "实质",
+  "highlight_cluster": "斑斓厚重的油画颜料",
+  "description": "木质调色盘上堆积的厚重油画颜料(Impasto),杂乱而鲜艳的色彩肌理,与白色衣物形成强烈视觉反差",
+  "files": {
+    "palette_impasto_img5_v2.png": {
+      "source_image": "img_5",
+      "view": "close-up",
+      "purpose": "调色板颜料特写(主要参考),展示Impasto厚涂质感,颜色最丰富",
+      "quality": "高质量,38%非白色内容,颜色多样性强",
+      "tool": "gemini-2.5-flash-image"
+    },
+    "palette_impasto_img1_v2.png": {
+      "source_image": "img_1",
+      "view": "medium",
+      "purpose": "调色板整体形态参考",
+      "quality": "有效,20%非白色内容",
+      "tool": "gemini-2.5-flash-image"
+    },
+    "palette_ref_img5.png": {
+      "source_image": "img_5",
+      "view": "close-up",
+      "purpose": "调色板颜料特写(旧版参考)",
+      "quality": "有效,47%非白色内容"
+    },
+    "palette_ref_img1.png": {
+      "source_image": "img_1",
+      "view": "medium",
+      "purpose": "调色板整体形态参考(旧版)",
+      "quality": "有效,33%非白色内容"
+    }
+  },
+  "impasto_characteristics": {
+    "texture": "thick, textured paint buildup with visible brushstroke marks",
+    "colors": [
+      "deep green",
+      "light green",
+      "blue",
+      "red",
+      "yellow",
+      "white",
+      "purple",
+      "black",
+      "pink",
+      "brown"
+    ],
+    "sd_tokens": "impasto oil paint, thick paint texture, colorful palette, artist palette, oil painting supplies, vibrant paint colors"
+  },
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1.1.2.3",
+      "type": "实质",
+      "feature": "调色板颜料Impasto质感",
+      "highlight": "斑斓厚重的油画颜料",
+      "path": "output/features/palette_asset/palette_impasto_img1_v2.png"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5.1.3",
+      "type": "实质",
+      "feature": "调色板颜料Impasto质感(特写)",
+      "highlight": "斑斓厚重的油画颜料",
+      "path": "output/features/palette_asset/palette_impasto_img5_v2.png"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5.1.3.1",
+      "type": "实质",
+      "feature": "颜料色彩种类与分布",
+      "highlight": "斑斓厚重的油画颜料",
+      "path": "output/features/palette_asset/palette_impasto_img5_v2.png"
+    }
+  ],
+  "consistency_elements": [
+    "绘画工具"
+  ]
+}

BIN
tests/features/palette_asset/palette_impasto_img1_v2.png


BIN
tests/features/pose_skeleton/img_1_openpose.png


BIN
tests/features/pose_skeleton/img_2_openpose.png


BIN
tests/features/pose_skeleton/img_3_openpose.png


BIN
tests/features/pose_skeleton/img_4_openpose.png


+ 99 - 0
tests/features/pose_skeleton/mapping.json

@@ -0,0 +1,99 @@
+{
+  "dimension": "pose_skeleton",
+  "type": "形式",
+  "highlight_cluster": "优雅的白裙写生少女",
+  "description": "各图人物姿态骨骼图,用于ControlNet姿态控制,每张图姿态不同",
+  "valid_files": {
+    "img_1_pose.png": {
+      "source_image": "img_1",
+      "format": "黑底彩色骨骼图 (DWPose标准格式)",
+      "pose": "站立侧后方,右臂举起持笔,左臂弯曲持调色板",
+      "colored_pixels": 27332,
+      "quality": "✓ 有效"
+    },
+    "img_2_pose.png": {
+      "source_image": "img_2",
+      "format": "黑底彩色骨骼图 (DWPose标准格式)",
+      "pose": "站立正后方,双臂持绘画工具",
+      "colored_pixels": 23513,
+      "quality": "✓ 有效"
+    },
+    "img_3_pose.png": {
+      "source_image": "img_3",
+      "format": "黑底彩色骨骼图 (DWPose标准格式)",
+      "pose": "跪坐/蹲坐,后方视角,双臂伸向画架",
+      "colored_pixels": 45531,
+      "quality": "✓ 有效"
+    },
+    "img_4_pose.png": {
+      "source_image": "img_4",
+      "format": "黑底彩色骨骼图 (DWPose标准格式)",
+      "pose": "站立侧面,面向画架,绘画姿态",
+      "colored_pixels": 41132,
+      "quality": "✓ 有效"
+    },
+    "img_5_pose.png": {
+      "source_image": "img_5",
+      "format": "黑底彩色骨骼图 (DWPose标准格式)",
+      "pose": "上半身特写,持调色板,略俯视角度",
+      "colored_pixels": 79351,
+      "quality": "✓ 有效"
+    }
+  },
+  "deprecated_files": {
+    "pose_img_1.png": "几乎全白(>99%),提取失败,已废弃",
+    "pose_img_3.png": "几乎全白(>99%),提取失败,已废弃",
+    "pose_img_4.png": "几乎全白(>99%),提取失败,已废弃",
+    "pose_img_2.png": "白底骨骼图,部分有效(8.3%非白色)",
+    "pose_img_5.png": "白底骨骼图,部分有效(17.6%非白色)"
+  },
+  "consistency_validation": {
+    "img_1": "骨骼质心x=464/864=54%(右侧),符合人物在右侧的构图",
+    "img_3": "骨骼质心y=883/1184=75%(偏下),符合跪坐姿态",
+    "img_4": "骨骼质心x=534/864=62%(偏左),符合侧面视角",
+    "img_5": "骨骼覆盖100%y范围,符合上半身特写"
+  },
+  "mapping": [
+    {
+      "image": "img_1",
+      "paragraph": "段落1.1.2",
+      "type": "形式",
+      "feature": "人物姿态骨骼",
+      "specific": "站立侧后方绘画姿态",
+      "path": "output/features/pose_skeleton/img_1_pose.png"
+    },
+    {
+      "image": "img_2",
+      "paragraph": "段落2.1.2",
+      "type": "形式",
+      "feature": "人物姿态骨骼",
+      "specific": "站立正后方绘画姿态",
+      "path": "output/features/pose_skeleton/img_2_pose.png"
+    },
+    {
+      "image": "img_3",
+      "paragraph": "段落3.1.2",
+      "type": "形式",
+      "feature": "人物姿态骨骼",
+      "specific": "跪坐绘画姿态",
+      "path": "output/features/pose_skeleton/img_3_pose.png"
+    },
+    {
+      "image": "img_4",
+      "paragraph": "段落4.1.1.2",
+      "type": "形式",
+      "feature": "人物姿态骨骼",
+      "specific": "站立侧面绘画姿态",
+      "path": "output/features/pose_skeleton/img_4_pose.png"
+    },
+    {
+      "image": "img_5",
+      "paragraph": "段落5.1.1",
+      "type": "形式",
+      "feature": "人物姿态骨骼",
+      "specific": "上半身持调色板姿态",
+      "path": "output/features/pose_skeleton/img_5_pose.png"
+    }
+  ],
+  "tool_note": "DWPose骨骼图(黑底彩色格式),用于ControlNet OpenPose条件控制。img_*_pose.png为有效文件。"
+}

+ 231 - 0
tests/features/validation_report.md

@@ -0,0 +1,231 @@
+# 图像还原素材完整性验证报告
+
+**验证日期**: 2026-04-07  
+**素材目录**: `examples/production_restore/features`
+
+---
+
+## 📊 验证摘要
+
+| 素材类别 | 应存在文件数 | 实际存在文件数 | 缺失文件数 | 完整性 |
+|----------|-------------|---------------|-----------|--------|
+| 姿态骨骼图 (pose_skeleton) | 5 | 4 | 1 | ⚠️ 80% |
+| 深度图 (depth_map) | 5 | 5 | 0 | ✅ 100% |
+| 边缘图 (edge_map) | 5 | 5 | 0 | ✅ 100% |
+| 人物参考图 (character_asset) | 5 | 5 | 0 | ✅ 100% |
+| 背景参考图 (background_asset) | 5 | 3 | 2 | ⚠️ 60% |
+| 调色板参考图 (palette_asset) | 4 | 1 | 3 | ❌ 25% |
+| 画架参考图 (easel_asset) | 2 | 1 | 1 | ⚠️ 50% |
+
+**总体完整性**: 约 76% (24/31 文件存在)
+
+---
+
+## 📁 详细验证结果
+
+### 1. 姿态骨骼图 (pose_skeleton)
+
+**目录**: `examples/production_restore/features/pose_skeleton/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 状态 |
+|--------|------|------|------|
+| img_1_openpose.png | PNG RGB | 1088 x 1472 | ✅ 存在 |
+| img_2_openpose.png | PNG RGB | 1088 x 1472 | ✅ 存在 |
+| img_3_openpose.png | PNG RGB | 1088 x 1472 | ✅ 存在 |
+| img_4_openpose.png | PNG RGB | 1088 x 1472 | ✅ 存在 |
+| img_5_openpose.png | - | - | ❌ **缺失** |
+| mapping.json | JSON | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 符合 DWPose 黑底彩色标准格式
+- **尺寸**: ✅ 统一 1088 x 1472 像素
+- **内容**: ✅ 包含完整人体骨骼关键点(18 个关键点)
+
+#### ⚠️ 缺失文件
+- **img_5_openpose.png**: mapping.json 中定义了 img_5_pose.png(上半身特写姿态),但实际文件不存在
+
+---
+
+### 2. 深度图 (depth_map)
+
+**目录**: `examples/production_restore/features/depth_map/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 状态 |
+|--------|------|------|------|
+| depth_img_1.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| depth_img_2.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| depth_img_3.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| depth_img_4.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| depth_img_5.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| mapping.json | JSON | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 8-bit 灰度 PNG
+- **尺寸**: ✅ 统一 1080 x 1439 像素
+- **深度层次**: ✅ 可区分前景(白色/清晰)- 中景(灰色)- 背景(黑色/虚化)
+- **生成方法**: 局部方差法(清晰度作为深度代理)
+- **建议**: 生产环境建议使用 Depth-Anything-V2 或 MiDaS v3.1 获取更精准深度图
+
+---
+
+### 3. 边缘图 (edge_map)
+
+**目录**: `examples/production_restore/features/edge_map/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 状态 |
+|--------|------|------|------|
+| img_1_canny.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| img_2_canny.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| img_3_canny.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| img_4_canny.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| img_5_canny.png | PNG Grayscale | 1080 x 1439 | ✅ 存在 |
+| mapping.json | JSON | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 8-bit 灰度 PNG
+- **尺寸**: ✅ 统一 1080 x 1439 像素
+- **边缘检测**: ✅ OpenCV Canny (threshold: 50-150)
+- **内容**: ✅ 保留人物轮廓、画架结构等关键线条
+
+---
+
+### 4. 人物参考图 (character_asset)
+
+**目录**: `examples/production_restore/features/character_asset/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 用途 | 状态 |
+|--------|------|------|------|------|
+| character_ref_back.png | PNG RGB | 864 x 1184 | 3/4 背面视角,IP-Adapter 主参考 | ✅ 存在 |
+| character_ref_side.png | PNG RGB | 864 x 1184 | 侧面视角参考 | ✅ 存在 |
+| character_ref_kneel.png | PNG RGB | 864 x 1184 | 跪坐姿态参考 | ✅ 存在 |
+| character_ref_main.png | PNG RGB | 864 x 1184 | 主参考图 | ✅ 存在 |
+| character_ref_img1.png | PNG RGB | 864 x 1184 | img_1 专用参考 | ✅ 存在 |
+| mapping.json | JSON | - | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 8-bit RGB PNG
+- **尺寸**: ✅ 统一 864 x 1184 像素
+- **角色一致性**: ✅ 白裙写生少女(棕色长发、纯白宽松连衣裙、手持画笔和调色板)
+- **用途**: IP-Adapter 人物一致性注入
+
+---
+
+### 5. 背景参考图 (background_asset)
+
+**目录**: `examples/production_restore/features/background_asset/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 对应图像 | 状态 |
+|--------|------|------|---------|------|
+| background_green_img1.png | PNG RGB | 864 x 1184 | img_1 | ✅ 存在 |
+| background_green_img4.png | PNG RGB | 864 x 1184 | img_4 | ✅ 存在 |
+| background_bokeh_img2.png | PNG RGB | 864 x 1184 | img_2 | ✅ 存在 |
+| (img_3 背景参考) | - | - | img_3 | ❌ 缺失 |
+| (img_5 背景参考) | - | - | img_5 | ❌ 缺失 |
+| mapping.json | JSON | - | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 8-bit RGB PNG
+- **尺寸**: ✅ 统一 864 x 1184 像素
+- **背景类型**:
+  - img_1: 清晰绿色草地 + 树木,柔和自然光
+  - img_2: 逆光散景梦幻背景,镜头光晕
+  - img_4: 高饱和绿色背景,明亮日光
+- **缺失**: img_3(含远处建筑的自然背景)、img_5(虚化绿色草地背景)无独立参考图
+
+---
+
+### 6. 调色板参考图 (palette_asset)
+
+**目录**: `examples/production_restore/features/palette_asset/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 用途 | 状态 |
+|--------|------|------|------|------|
+| palette_impasto_img1_v2.png | PNG RGB | 864 x 1184 | 调色板整体形态参考 | ✅ 存在 |
+| palette_impasto_img5_v2.png | - | - | 调色板颜料特写(主要参考) | ❌ **缺失** |
+| palette_ref_img5.png | - | - | 调色板特写(旧版) | ❌ **缺失** |
+| palette_ref_img1.png | - | - | 调色板整体(旧版) | ❌ **缺失** |
+| mapping.json | JSON | - | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 8-bit RGB PNG(仅存文件)
+- **尺寸**: ✅ 864 x 1184 像素(仅存文件)
+- **Impasto 质感**: ⚠️ 仅有 img_1 的参考图,缺失 img_5 特写(颜色最丰富、质量最高)
+- **颜色多样性**: ⚠️ 无法完整评估(缺失主要参考文件)
+
+#### ❌ 严重缺失
+- **palette_impasto_img5_v2.png**: mapping.json 标注为"高质量,38% 非白色内容,颜色多样性强",是关键参考图
+- 调色板素材完整性仅 25%,严重影响 Impasto 质感还原
+
+---
+
+### 7. 画架参考图 (easel_asset)
+
+**目录**: `examples/production_restore/features/easel_asset/`
+
+#### 文件清单
+| 文件名 | 格式 | 尺寸 | 用途 | 状态 |
+|--------|------|------|------|------|
+| easel_blank_canvas_img4.png | PNG RGB | 864 x 1184 | 画架 + 空白画布 + 白玫瑰 | ✅ 存在 |
+| easel_with_painting_img3.png | - | - | 画架 + 进行中油画 | ❌ **缺失** |
+| mapping.json | JSON | - | - | ✅ 存在 |
+
+#### 可用性评估
+- **格式**: ✅ 8-bit RGB PNG(仅存文件)
+- **尺寸**: ✅ 864 x 1184 像素(仅存文件)
+- **画架类型**: 浅棕色木质三脚架结构
+- **缺失**: easel_with_painting_img3.png(用于画中画叙事场景)
+
+---
+
+## 🔴 关键缺失文件汇总
+
+| 优先级 | 文件路径 | 影响 |
+|--------|---------|------|
+| 🔴 高 | `pose_skeleton/img_5_openpose.png` | img_5 姿态控制缺失,无法生成上半身特写 |
+| 🔴 高 | `palette_asset/palette_impasto_img5_v2.png` | Impasto 质感主要参考缺失,影响色彩还原 |
+| 🟠 中 | `palette_asset/palette_ref_img5.png` | 调色板旧版参考缺失 |
+| 🟠 中 | `palette_asset/palette_ref_img1.png` | 调色板旧版参考缺失 |
+| 🟠 中 | `easel_asset/easel_with_painting_img3.png` | img_3 画中画叙事场景参考缺失 |
+| 🟡 低 | `background_asset/` (img_3, img_5 背景) | 背景参考不完整,但可通过其他图推断 |
+
+---
+
+## ✅ 可用性总结
+
+### 完全可用的素材
+- ✅ **深度图**: 5/5 完整,可直接用于 ControlNet Depth
+- ✅ **边缘图**: 5/5 完整,可直接用于 ControlNet Canny
+- ✅ **人物参考图**: 5/5 完整,可用于 IP-Adapter 人物一致性
+
+### 部分可用的素材
+- ⚠️ **姿态骨骼图**: 4/5 完整,缺失 img_5,需补充
+- ⚠️ **背景参考图**: 3/5 完整,img_3/img_5 缺失但可推断
+- ⚠️ **画架参考图**: 1/2 完整,缺失 img_3 油画状态
+
+### 严重不完整的素材
+- ❌ **调色板参考图**: 1/4 完整,缺失关键 Impasto 质感参考
+
+---
+
+## 📋 建议行动
+
+1. **立即补充** (高优先级):
+   - 生成/提取 `img_5_openpose.png`(DWPose 骨骼图)
+   - 生成/提取 `palette_impasto_img5_v2.png`(调色板 Impasto 特写)
+
+2. **建议补充** (中优先级):
+   - 补充 `easel_with_painting_img3.png`
+   - 补充 palette 旧版参考图
+
+3. **可选补充** (低优先级):
+   - 补充 img_3、img_5 背景参考图
+
+---
+
+**报告生成完成**

+ 209 - 0
tests/features/validation_summary.md

@@ -0,0 +1,209 @@
+# 图像还原素材完整性验证报告
+
+**验证日期**: 2026-04-08  
+**素材目录**: `examples/production_restore/features`
+
+---
+
+## 📊 验证结果摘要
+
+| 类别 | 预期数量 | 实际数量 | 状态 |
+|------|----------|----------|------|
+| 姿态骨骼图 (pose_skeleton) | 5 (img_1-5) | 4 | ⚠️ **缺失 img_5** |
+| 深度图 (depth_map) | 5 (img_1-5) | 5 | ✅ 完整 |
+| 边缘图 (edge_map) | 5 (img_1-5) | 5 | ✅ 完整 |
+| 人物参考图 (character_asset) | 5 视角 | 5 | ✅ 完整 |
+| 背景参考图 (background_asset) | 5 (img_1-5) | 3 | ⚠️ **缺失 img_3, img_5** |
+| 调色板参考图 (palette_asset) | 2+ | 1 | ⚠️ **缺失 img_5 特写** |
+| 画架参考图 (easel_asset) | 2 | 1 | ⚠️ **缺失 img_3 带油画版** |
+
+---
+
+## 📁 各子目录详细清单
+
+### 1. 姿态骨骼图 (pose_skeleton)
+
+**目录**: `examples/production_restore/features/pose_skeleton/`
+
+| 文件名 | 格式 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `img_1_openpose.png` | PNG RGB | 1088×1472 | ✅ 有效 |
+| `img_2_openpose.png` | PNG RGB | 1088×1472 | ✅ 有效 |
+| `img_3_openpose.png` | PNG RGB | 1088×1472 | ✅ 有效 |
+| `img_4_openpose.png` | PNG RGB | 1088×1472 | ✅ 有效 |
+| `img_5_openpose.png` | - | - | ❌ **缺失** |
+
+**评估**:
+- ✅ 现有文件格式正确:黑底彩色骨骼图 (DWPose 标准格式)
+- ✅ 分辨率统一 (1088×1472)
+- ❌ **img_5_openpose.png 缺失** - 需要补充上半身特写姿态骨骼图
+
+---
+
+### 2. 深度图 (depth_map)
+
+**目录**: `examples/production_restore/features/depth_map/`
+
+| 文件名 | 格式 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `depth_img_1.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `depth_img_2.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `depth_img_3.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `depth_img_4.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `depth_img_5.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+
+**评估**:
+- ✅ 5 张深度图全部存在
+- ✅ 格式正确:8-bit 灰度图
+- ✅ 分辨率统一 (1080×1439)
+- ✅ 使用局部方差法生成,可区分前景/背景(白色=近景,黑色=远景)
+
+---
+
+### 3. 边缘图 (edge_map)
+
+**目录**: `examples/production_restore/features/edge_map/`
+
+| 文件名 | 格式 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `img_1_canny.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `img_2_canny.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `img_3_canny.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `img_4_canny.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+| `img_5_canny.png` | PNG Grayscale | 1080×1439 | ✅ 有效 |
+
+**评估**:
+- ✅ 5 张 Canny 边缘图全部存在
+- ✅ 格式正确:8-bit 灰度图
+- ✅ 分辨率统一 (1080×1439)
+- ✅ 保留人物轮廓、画架结构等关键线条
+
+---
+
+### 4. 人物参考图 (character_asset)
+
+**目录**: `examples/production_restore/features/character_asset/`
+
+| 文件名 | 视角 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `character_ref_back.png` | 3/4 背面视角 | 864×1184 | ✅ 有效 |
+| `character_ref_side.png` | 侧面视角 | 864×1184 | ✅ 有效 |
+| `character_ref_kneel.png` | 跪坐姿态 | 864×1184 | ✅ 有效 |
+| `character_ref_main.png` | 主参考图 | 864×1184 | ✅ 有效 |
+| `character_ref_img1.png` | 正面/主要视角 | 864×1184 | ✅ 有效 |
+
+**评估**:
+- ✅ 5 张人物参考图全部存在
+- ✅ 视角覆盖完整:背面、侧面、跪坐、正面
+- ✅ 格式正确:PNG RGB 彩色图
+- ✅ 分辨率统一 (864×1184)
+- ✅ 可用于 IP-Adapter 人物一致性控制
+
+---
+
+### 5. 背景参考图 (background_asset)
+
+**目录**: `examples/production_restore/features/background_asset/`
+
+| 文件名 | 类型 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `background_green_img1.png` | 清晰绿色草地+树木 | 864×1184 | ✅ 有效 |
+| `background_green_img4.png` | 高饱和绿色背景 | 864×1184 | ✅ 有效 |
+| `background_bokeh_img2.png` | 逆光散景梦幻背景 | 864×1184 | ✅ 有效 |
+| `background_img3.png` | - | - | ❌ **缺失** |
+| `background_img5.png` | - | - | ❌ **缺失** |
+
+**评估**:
+- ⚠️ 仅有 3 张背景参考图(预期 5 张)
+- ✅ 现有文件涵盖三种典型类型:清晰绿、高饱和绿、逆光散景
+- ✅ 格式正确:PNG RGB 彩色图
+- ❌ **缺失 img_3 和 img_5 的背景参考图**
+
+---
+
+### 6. 调色板参考图 (palette_asset)
+
+**目录**: `examples/production_restore/features/palette_asset/`
+
+| 文件名 | 描述 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `palette_impasto_img1_v2.png` | 调色板整体形态 | 864×1184 | ✅ 有效 |
+| `palette_impasto_img5_v2.png` | 调色板颜料特写 | - | ❌ **缺失** |
+
+**评估**:
+- ⚠️ 仅有 1 张调色板参考图(mapping.json 中记录 2 张)
+- ✅ 现有文件展示 Impasto 厚涂质感
+- ✅ 格式正确:PNG RGB 彩色图
+- ❌ **缺失 img_5 调色板特写图**(高质量、颜色最丰富的版本)
+
+---
+
+### 7. 画架参考图 (easel_asset)
+
+**目录**: `examples/production_restore/features/easel_asset/`
+
+| 文件名 | 状态 | 分辨率 | 状态 |
+|--------|------|--------|------|
+| `easel_blank_canvas_img4.png` | 空白画布+白玫瑰 | 864×1184 | ✅ 有效 |
+| `easel_with_painting_img3.png` | 承载进行中油画 | - | ❌ **缺失** |
+
+**评估**:
+- ⚠️ 仅有 1 张画架参考图(mapping.json 中记录 2 张)
+- ✅ 现有文件展示空白画布状态
+- ✅ 格式正确:PNG RGB 彩色图
+- ❌ **缺失 img_3 带油画版本的画架参考图**
+
+---
+
+## 🔴 缺失文件清单
+
+### 高优先级缺失(影响核心功能)
+
+| 文件路径 | 类别 | 影响 |
+|----------|------|------|
+| `pose_skeleton/img_5_openpose.png` | 姿态骨骼图 | ControlNet 姿态控制缺失 img_5 |
+
+### 中优先级缺失(影响完整性)
+
+| 文件路径 | 类别 | 影响 |
+|----------|------|------|
+| `palette_asset/palette_impasto_img5_v2.png` | 调色板参考 | 缺失高质量调色板特写 |
+| `easel_asset/easel_with_painting_img3.png` | 画架参考 | 缺失画中画叙事场景参考 |
+
+### 低优先级缺失(可选补充)
+
+| 文件路径 | 类别 | 影响 |
+|----------|------|------|
+| `background_asset/background_img3.png` | 背景参考 | img_3 背景参考缺失 |
+| `background_asset/background_img5.png` | 背景参考 | img_5 背景参考缺失 |
+
+---
+
+## ✅ 有效性评估总结
+
+### 格式验证
+- ✅ 所有 PNG 文件格式正确
+- ✅ 姿态骨骼图:RGB 彩色,黑底彩色骨骼(DWPose 标准)
+- ✅ 深度图:8-bit 灰度,可区分前景/背景层次
+- ✅ 边缘图:8-bit 灰度,保留关键结构线条
+- ✅ 参考图:RGB 彩色,分辨率统一
+
+### 内容验证
+- ✅ 深度图使用局部方差法,白色=近景(人物/画架),黑色=远景(背景)
+- ✅ 边缘图使用 Canny 检测 (threshold: 50-150),保留人物轮廓和画架结构
+- ✅ 人物参考图覆盖背面、侧面、跪坐等多视角
+- ✅ 背景参考图包含清晰绿、高饱和绿、逆光散景三种典型类型
+
+---
+
+## 📋 建议操作
+
+1. **立即补充**: `img_5_openpose.png` - 使用 DWPose 从 img_5 源图提取骨骼
+2. **建议补充**: `palette_impasto_img5_v2.png` - 使用图像分割工具提取调色板特写
+3. **建议补充**: `easel_with_painting_img3.png` - 提取 img_3 中的画架+油画区域
+4. **可选补充**: img_3 和 img_5 的背景参考图 - 如需要更完整的背景一致性控制
+
+---
+
+**验证完成时间**: 2026-04-08 10:28:57  
+**验证工具**: 文件系统检查 + file 命令格式验证 + mapping.json 元数据比对

+ 194 - 0
tests/features/素材验证报告.md

@@ -0,0 +1,194 @@
+# 图像还原素材完整性与有效性验证报告
+
+**验证时间**: 2026-04-07 21:54  
+**素材目录**: `examples/production_restore/features`
+
+---
+
+## 1. 姿态骨骼图 (pose_skeleton)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| img_1_openpose.png | ✅ 存在 | 1088 x 1472 | PNG, 8-bit RGB |
+| img_2_openpose.png | ✅ 存在 | 1088 x 1472 | PNG, 8-bit RGB |
+| img_3_openpose.png | ✅ 存在 | 1088 x 1472 | PNG, 8-bit RGB |
+| img_4_openpose.png | ✅ 存在 | 1088 x 1472 | PNG, 8-bit RGB |
+| img_5_openpose.png | ❌ **缺失** | - | - |
+
+### 有效性评估
+- **存在性**: 4/5 文件存在,**img_5_openpose.png 缺失**
+- **格式**: 存在的文件均为 PNG 格式,8-bit RGB 彩色模式,符合 DWPose 黑底彩色格式要求
+- **尺寸一致性**: 所有存在的文件尺寸一致 (1088 x 1472)
+- **额外文件**: mapping.json (配置文件)
+
+### 问题标记
+⚠️ **严重**: img_5_openpose.png 缺失,可能导致第 5 帧的姿态控制无法正常工作
+
+---
+
+## 2. 深度图 (depth_map)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| depth_img_1.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| depth_img_2.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| depth_img_3.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| depth_img_4.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| depth_img_5.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+
+### 有效性评估
+- **存在性**: 5/5 文件完整
+- **格式**: 均为 8-bit 灰度 PNG,符合深度图标准格式
+- **尺寸一致性**: 所有文件尺寸一致 (1080 x 1439)
+- **深度区分能力**: 灰度格式支持 256 级深度值,能够区分前景/中景/背景
+- **额外文件**: mapping.json (配置文件)
+
+### 结论
+✅ **完全有效**
+
+---
+
+## 3. 边缘图 (edge_map)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| img_1_canny.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| img_2_canny.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| img_3_canny.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| img_4_canny.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+| img_5_canny.png | ✅ 存在 | 1080 x 1439 | PNG, 8-bit 灰度 |
+
+### 有效性评估
+- **存在性**: 5/5 文件完整
+- **格式**: 均为 8-bit 灰度 PNG,符合 Canny 边缘图标准格式
+- **尺寸一致性**: 所有文件尺寸一致 (1080 x 1439)
+- **边缘保留**: 灰度格式适合保留人物轮廓和道具结构信息
+- **额外文件**: mapping.json (配置文件)
+
+### 结论
+✅ **完全有效**
+
+---
+
+## 4. 人物参考图 (character_asset)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| character_ref_back.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+| character_ref_side.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+| character_ref_kneel.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+| character_ref_main.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+| character_ref_img1.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+
+### 有效性评估
+- **存在性**: 5/5 文件完整
+- **格式**: 均为 8-bit RGB 彩色 PNG,适合人物参考
+- **尺寸一致性**: 所有文件尺寸一致 (864 x 1184)
+- **覆盖角度**: 包含背面、侧面、跪姿、主视图、img1 参考,覆盖全面
+- **额外文件**: mapping.json (配置文件)
+
+### 结论
+✅ **完全有效**
+
+---
+
+## 5. 背景参考图 (background_asset)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| background_green_img1.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+| background_green_img4.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+| background_bokeh_img2.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+
+### 有效性评估
+- **存在性**: 3/3 文件完整
+- **格式**: 均为 8-bit RGB 彩色 PNG
+- **尺寸一致性**: 所有文件尺寸一致 (864 x 1184)
+- **类型覆盖**: 包含绿幕背景 (img1, img4) 和散景背景 (img2)
+- **额外文件**: mapping.json (配置文件)
+
+### 结论
+✅ **完全有效**
+
+---
+
+## 6. 调色板参考图 (palette_asset)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| palette_impasto_img1_v2.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+
+### 有效性评估
+- **存在性**: 1/1 文件完整
+- **格式**: 8-bit RGB 彩色 PNG
+- **版本**: v2 版本,可能为迭代优化后的调色板
+- **额外文件**: mapping.json (配置文件)
+
+### 结论
+✅ **完全有效**
+
+---
+
+## 7. 画架参考图 (easel_asset)
+
+### 文件清单
+| 文件名 | 状态 | 尺寸 | 格式 |
+|--------|------|------|------|
+| easel_blank_canvas_img4.png | ✅ 存在 | 864 x 1184 | PNG, 8-bit RGB |
+
+### 有效性评估
+- **存在性**: 1/1 文件完整
+- **格式**: 8-bit RGB 彩色 PNG
+- **用途**: 针对 img4 的空白画布画架参考
+- **额外文件**: mapping.json (配置文件)
+
+### 结论
+✅ **完全有效**
+
+---
+
+## 整体素材完备性结论
+
+### 统计汇总
+
+| 类别 | 应存在文件数 | 实际存在数 | 缺失数 | 完备率 |
+|------|-------------|-----------|--------|--------|
+| 姿态骨骼图 | 5 | 4 | 1 | 80% |
+| 深度图 | 5 | 5 | 0 | 100% |
+| 边缘图 | 5 | 5 | 0 | 100% |
+| 人物参考图 | 5 | 5 | 0 | 100% |
+| 背景参考图 | 3 | 3 | 0 | 100% |
+| 调色板参考图 | 1 | 1 | 0 | 100% |
+| 画架参考图 | 1 | 1 | 0 | 100% |
+| **总计** | **25** | **24** | **1** | **96%** |
+
+### 最终结论
+
+🟡 **素材基本完备,存在 1 项关键缺失**
+
+**主要问题**:
+- ❌ `pose_skeleton/img_5_openpose.png` 缺失
+
+**影响评估**:
+- 该缺失将导致第 5 帧图像生成时无法使用姿态控制
+- 可能影响最终输出的一致性,特别是人物姿态的准确性
+
+**建议措施**:
+1. **紧急**: 生成或补充 `img_5_openpose.png` 骨骼图
+2. 确认 img_5 对应的原始图像和姿态数据
+3. 使用 DWPose 或类似工具从 img_5 原图提取骨骼信息
+
+**其他发现**:
+- 所有存在的文件格式正确,符合预期规范
+- 各子目录均包含 mapping.json 配置文件,便于素材映射管理
+- color_scheme 和 lighting_bokeh 目录存在额外素材(未在验证要求中列出)
+
+---
+
+*报告生成完成*

+ 57 - 0
tests/find_keys.py

@@ -0,0 +1,57 @@
+import sys
+import os
+sys.path.append(os.path.join(os.path.dirname(__file__), '../tools/local/liblibai_controlnet'))
+from liblibai_client import LibLibAIClient
+
+client = LibLibAIClient()
+TEMPLATE_UUID = "e10adc3949ba59abbe56e057f20f883e"
+DEFAULT_CHECKPOINT_ID = "0ea388c7eb854be3ba3c6f65aac6bfd3" 
+
+import requests
+
+def test_key(preprocessor, key_name):
+    payload = {
+        "templateUuid": TEMPLATE_UUID,
+        "generateParams": {
+            "checkPointId": DEFAULT_CHECKPOINT_ID,
+            "prompt": "cat",
+            "width": 512,
+            "height": 512,
+            "imgCount": 1,
+            "controlNet": [{
+                "unitOrder": 1,
+                "sourceImage": "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png",
+                "width": 512,
+                "height": 512,
+                "preprocessor": preprocessor,
+                "model": "b6806516962f4e1599a93ac4483c3d23", # Random UUID
+                "controlWeight": 1.0,
+                "startingControlStep": 0.0,
+                "endingControlStep": 1.0,
+                "pixelPerfect": 1,
+                "controlMode": 0,
+                "annotationParameters": {
+                    key_name: {
+                        "preprocessorResolution": 512
+                    }
+                }
+            }]
+        }
+    }
+    url = client.generate_auth_url("/api/generate/webui/text2img")
+    resp = requests.post(url, json=payload).json()
+    if resp.get("code") == 100050:
+        return False
+    return True
+
+print("=== SoftEdge (preprocessor 5) ===")
+for k in ["softedge", "hed", "pidinet", "softedge_hed", "softedge_pidinet", "hedSafe", "hedBase"]:
+    if test_key(5, k):
+        print(f"FOUND SOFTEDGE KEY: {k}")
+        break
+        
+print("=== Lineart (preprocessor 32) ===")
+for k in ["lineart", "lineartStandard", "lineart_standard", "lineart_anime", "lineartAnime", "lineartRealistic", "lineartBase"]:
+    if test_key(32, k):
+        print(f"FOUND LINEART KEY: {k}")
+        break

BIN
tests/output/flux_output_1775647098_0.png


BIN
tests/output/openpose_result_1775635386_0.png


BIN
tests/output/seedream_output_1775647134_0.png


BIN
tests/output/workflow_result_1775635574_0.png


BIN
tests/output/workflow_result_1775635614_0.png


BIN
tests/output/workflow_result_1775635883_0.png


BIN
tests/output/workflow_result_1775635913_0.png


BIN
tests/output/workflow_result_1775638093_0.png


BIN
tests/output/workflow_result_1775638649_0.png


BIN
tests/output/workflow_result_1775638711_0.png


+ 6 - 5
tests/test_liblibai_tool.py

@@ -32,11 +32,12 @@ def main():
         sys.exit(1)
 
     # 2. 搜索工具,确认已注册
-    print("\n--- 搜索工具 ---")
-    resp = httpx.post(f"{ROUTER_URL}/search_tools", json={"keyword": "liblib"})
+    print("\n--- 获取工具 ---")
+    resp = httpx.get(f"{ROUTER_URL}/tools")
     tools = resp.json()
-    print(f"找到 {tools['total']} 个工具")
-    for t in tools["tools"]:
+    liblib_tools = [t for t in tools["tools"] if "liblib" in t["tool_id"] or "liblib" in t["name"].lower()]
+    print(f"找到 {len(liblib_tools)} 个匹配 liblib 的工具")
+    for t in liblib_tools:
         print(f"  {t['tool_id']}: {t['name']} (state={t['state']})")
 
     # 3. 调用 liblibai_controlnet 工具
@@ -46,7 +47,7 @@ def main():
     print("提交中...")
 
     resp = httpx.post(
-        f"{ROUTER_URL}/select_tool",
+        f"{ROUTER_URL}/run_tool",
         json={
             "tool_id": "liblibai_controlnet",
             "params": {

+ 466 - 0
tests/test_liblibai_workflows.py

@@ -0,0 +1,466 @@
+import os
+import sys
+import time
+import requests
+import json
+from typing import Dict, Any
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '../tools/local/liblibai_controlnet'))
+from liblibai_client import LibLibAIClient
+
+# 常量配置 (根据文档)
+TEMPLATE_UUID = "e10adc3949ba59abbe56e057f20f883e"  # SD1.5 & SDXL 通用自定义参数模板
+
+# 请确保这是一个有效的 SDXL 模型,这样才能匹配底下的 SDXL Canny 模型
+# 这里用的是代码里原本的 Checkpoint ID,请根据你们自己的 Liblib 模型库调整!
+DEFAULT_CHECKPOINT_ID = "0ea388c7eb854be3ba3c6f65aac6bfd3"
+
+class LibLibTestRunner:
+    def __init__(self):
+        self.client = LibLibAIClient()
+        self.models = self._load_models_from_json()
+        
+        # 动态匹配基础算法 XL 的各种控制网模型
+        self.sdxl_canny = self._get_model_uuid("线稿类", "Canny(硬边缘)", xl_only=True) or "b6806516962f4e1599a93ac4483c3d23"
+        self.sdxl_softedge = self._get_model_uuid("线稿类", "SoftEdge(软边缘)", xl_only=True) or "dda1a0c480bfab9833d9d9a1e4a71fff"
+        self.sdxl_lineart = self._get_model_uuid("线稿类", "Lineart(线稿)", xl_only=True) or "a0f01da42bf48b0ba02c86b6c26b5699"
+        self.sdxl_openpose = self._get_model_uuid("姿态类", "OpenPose(姿态)", xl_only=True) or "2fe4f992a81c5ccbdf8e9851c8c96ff2"
+        
+        self._verify_models()
+        
+    def _load_models_from_json(self):
+        json_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../data/liblibai_controlnet_models.json"))
+        if os.path.exists(json_path):
+            with open(json_path, "r", encoding="utf-8") as f:
+                return json.load(f)
+        return {}
+
+    def _get_model_uuid(self, category, subtype, xl_only=True):
+        if category in self.models and subtype in self.models[category]:
+            for model in self.models[category][subtype]:
+                if xl_only and model["base_algorithm"] != "基础算法 XL":
+                    continue
+                return model["uuid"]
+        return None
+
+    def _verify_models(self):
+        print("="*50)
+        print("校验底模信息 (使用 api/model/version/get)")
+        print("="*50)
+        
+        models_to_verify = {
+            "CheckPoint / 底模": DEFAULT_CHECKPOINT_ID,
+        }
+        
+        for name, uuid in models_to_verify.items():
+            info = self.client.get_model_version_info(uuid)
+            status = f"{info.get('model_name', 'Unknown')} (Base: {info.get('baseAlgo', 'Unknown')})" if info else "Failed to verify"
+            print(f"- {name}: [{uuid}] -> {status}")
+        print("\n")
+        
+    def _submit_task(self, payload: dict) -> str:
+        url = self.client.generate_auth_url("/api/generate/webui/text2img")
+        print(f"Submitting payload...")
+        resp = requests.post(url, json=payload, timeout=10)
+        data = resp.json()
+        if data.get("code") != 0:
+            raise Exception(f"Submit task failed: {data.get('msg')} (code: {data.get('code')})")
+        return data["data"]["generateUuid"]
+
+    def _wait_and_print_result(self, task_id: str):
+        print(f"Task submitted successfully! Task ID: {task_id}")
+        print("Waiting for result...")
+        timeout = 300
+        start_time = time.time()
+        while time.time() - start_time < timeout:
+            task_data = self.client.query_task_status(task_id)
+            status = task_data.get("generateStatus")
+            
+            if status == 5: # Success
+                images = [img["imageUrl"] for img in task_data.get("images", [])]
+                print(f"\n[SUCCESSS] Generated images: {images}")
+                
+                # --- 新增: 自动下载图片 ---
+                output_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "output"))
+                os.makedirs(output_dir, exist_ok=True)
+                for i, img_url in enumerate(images):
+                    try:
+                        img_resp = requests.get(img_url)
+                        img_resp.raise_for_status()
+                        timestamp = int(time.time())
+                        filename = f"workflow_result_{timestamp}_{i}.png"
+                        filepath = os.path.join(output_dir, filename)
+                        with open(filepath, "wb") as f:
+                            f.write(img_resp.content)
+                        print(f"[{i}] 已自动保存到本地: {filepath}")
+                    except Exception as e:
+                        print(f"图片自动下载失败: {e}")
+                return
+            elif status in [6, 7]: # Failed or Cancelled
+                print(f"\n[FAILED] Task failed. Audit status: {task_data.get('auditStatus')}")
+                return
+                
+            print(".", end="", flush=True)
+            time.sleep(5)
+            
+        print("\n[TIMEOUT] Wait for task timed out.")
+
+    # 1. 纯文生图测试
+    def test_text2img(self):
+        print("\n" + "="*50)
+        print("TEST 1: 纯文生图 (Text to Image)")
+        print("="*50)
+        
+        payload = {
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "prompt": "1girl, cute, beautiful landscape, masterpiece",
+                "negativePrompt": "lowres, bad anatomy, text, error",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+    # 2. 纯图生图测试
+    def test_img2img(self):
+        print("\n" + "="*50)
+        print("TEST 2: 纯图生图 (Image to Image)")
+        print("="*50)
+        
+        test_img_url = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
+        
+        payload = {
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "mode": 0,  # 0 代表图生图
+                "sourceImage": test_img_url,
+                "denoisingStrength": 0.75, # 去噪强度
+                "prompt": "white line art, cat",
+                "negativePrompt": "lowres, bad anatomy, error",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+    # 3. 文生图 + ControlNet (Canny) 测试
+    def test_text2img_controlnet(self):
+        print("\n" + "="*50)
+        print("TEST 3: 文生图 + ControlNet (Text2Img with ControlNet Canny)")
+        print("="*50)
+        print("注意: 如果默认 Checkpoint 是 SD1.5, 下面配置的 SDXL Canny 模型可能会导致访问拒绝!")
+        
+        test_img_url = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
+        
+        payload = {
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "prompt": "simple white line art, cat, black background",
+                "negativePrompt": "lowres, bad anatomy",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1,
+                "controlNet": [{
+                    "unitOrder": 1,
+                    "sourceImage": test_img_url,
+                    "width": 512,
+                    "height": 512,
+                    "preprocessor": 1,         # 1 = Canny
+                    "model": self.sdxl_canny,
+                    "controlWeight": 1.0,
+                    "startingControlStep": 0.0,
+                    "endingControlStep": 1.0,
+                    "pixelPerfect": 1,
+                    "controlMode": 0,
+                    "annotationParameters": {
+                        "canny": {
+                            "preprocessorResolution": 512,
+                            "lowThreshold": 100,
+                            "highThreshold": 200
+                        }
+                    }
+                }]
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+    # 4. 文生图 + 边缘 (SoftEdge/HED)
+    def test_text2img_softedge(self):
+        print("\n" + "="*50)
+        print("TEST 4: 文生图 + SoftEdge (软边缘 控制网)")
+        print("="*50)
+        
+        # 测试用图片
+        test_img_url = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
+        
+        payload = {
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "prompt": "Soft edge artwork, beautiful soft lighting, cat",
+                "negativePrompt": "lowres, bad anatomy",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1,
+                "controlNet": [{
+                    "unitOrder": 1,
+                    "sourceImage": test_img_url,
+                    "width": 512,
+                    "height": 512,
+                    "preprocessor": 5,         # 5 = HED / 软边缘
+                    "model": self.sdxl_softedge,
+                    "controlWeight": 1.0,
+                    "startingControlStep": 0.0,
+                    "endingControlStep": 1.0,
+                    "pixelPerfect": 1,
+                    "controlMode": 0,
+                    "annotationParameters": {
+                        "hed": {
+                            "preprocessorResolution": 512
+                        }
+                    }
+                }]
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+    # 5. 文生图 + 线稿 (Lineart)
+    def test_text2img_lineart(self):
+        print("\n" + "="*50)
+        print("TEST 5: 文生图 + Lineart (线稿 控制网)")
+        print("="*50)
+        
+        test_img_url = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
+        
+        payload = {
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "prompt": "Detailed coloring, colorful fantasy style, masterpiece, cat",
+                "negativePrompt": "lowres",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1,
+                "controlNet": [{
+                    "unitOrder": 1,
+                    "sourceImage": test_img_url,
+                    "width": 512,
+                    "height": 512,
+                    "preprocessor": 32,        # 32 = Lineart Standard
+                    "model": self.sdxl_lineart,
+                    "controlWeight": 1.0,
+                    "startingControlStep": 0.0,
+                    "endingControlStep": 1.0,
+                    "pixelPerfect": 1,
+                    "controlMode": 0,
+                    "annotationParameters": {
+                        "lineart": {
+                            "preprocessorResolution": 512
+                        }
+                    }
+                }]
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+    # 6. 文生图 + 骨骼 (OpenPose)
+    def test_text2img_openpose(self):
+        print("\n" + "="*50)
+        print("TEST 6: 文生图 + OpenPose (骨骼 控制网)")
+        print("="*50)
+        
+        # 测试用图片(最好使用含有人物动作的图)
+        test_img_url = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
+        
+        payload = {
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "prompt": "1girl, dancing pose, beautiful dress",
+                "negativePrompt": "lowres",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1,
+                "controlNet": [{
+                    "unitOrder": 1,
+                    "sourceImage": test_img_url,
+                    "width": 512,
+                    "height": 512,
+                    "preprocessor": 14,        # 14 = OpenPose Full
+                    "model": self.sdxl_openpose,
+                    "controlWeight": 1.0,
+                    "startingControlStep": 0.0,
+                    "endingControlStep": 1.0,
+                    "pixelPerfect": 1,
+                    "controlMode": 0,
+                    "annotationParameters": {
+                        "openposeFull": {
+                            "preprocessorResolution": 512
+                        }
+                    }
+                }]
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+    # 7. 局部重绘 (Inpaint Mode 4)
+    def test_inpaint_mode4(self):
+        print("\n" + "="*50)
+        print("TEST 7: 局部重绘 (Inpaint Mode 4 蒙版重绘)")
+        print("="*50)
+        
+        test_img_url = "https://liblibai-airship-temp.oss-cn-beijing.aliyuncs.com/aliyun-cn-prod/73ed6ae42b144d21bf566e05b5a6c138.png"
+        test_mask_url = test_img_url # 仅作演示,实际应用中应当是一个黑底白色的蒙版图片
+        
+        payload = {
+            # Inpainting 可能需要特殊模板,如不兼容请参考文档
+            "templateUuid": TEMPLATE_UUID,
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID,
+                "mode": 4,  # 4 = Inpaint 蒙版重绘
+                "sourceImage": test_img_url,
+                "denoisingStrength": 0.5,
+                "prompt": "A completely different background, sci-fi city",
+                "negativePrompt": "lowres",
+                "sampler": 15,
+                "steps": 20,
+                "cfgScale": 7.0,
+                "width": 512,
+                "height": 512,
+                "imgCount": 1,
+                "inpaintParam": {
+                    "maskImage": test_mask_url,
+                    "maskBlur": 4,
+                    "inpaintArea": 0
+                }
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+    # 8. 人像换脸 (InstantID)
+    def test_instantid_faceswap(self):
+        print("\n" + "="*50)
+        print("TEST 8: 人像换脸 (InstantID)")
+        print("="*50)
+        
+        # Note: InstantID faceswap uses a UNIQUE templateUuid
+        INSTANT_ID_TEMPLATE_UUID = "7d888009f81d4252a7c458c874cd017f"
+        
+        face_img_url = "https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/49943c0b-4d79-4e2f-8c55-bc1e5b8c69d8.png"
+        pose_img_url = "https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/e713676d-baaa-4dac-99b9-d5d814a29f9f.png"
+        
+        payload = {
+            "templateUuid": INSTANT_ID_TEMPLATE_UUID, 
+            "generateParams": {
+                "checkPointId": DEFAULT_CHECKPOINT_ID, # 仅 XL模型支持人像换脸
+                "prompt": "Asian portrait,A young woman wearing a green baseball cap, close shot, background is coffee store, masterpiece, best quality, ultra resolution",
+                "width": 768,
+                "height": 1152,
+                "sampler": 20,
+                "steps": 35,
+                "cfgScale": 2.0,
+                "imgCount": 1,
+                "controlNet": [
+                    {
+                        "unitOrder": 1, # 第一步:先识别要用的人像人脸
+                        "sourceImage": face_img_url,
+                        "width": 1080,
+                        "height": 1432            
+                    },
+                    {
+                        "unitOrder": 2, # 第二步:再识别要参考的人物面部朝向
+                        "sourceImage": pose_img_url,
+                        "width": 1024,
+                        "height": 1024 
+                    }
+                ]
+            }
+        }
+        task_id = self._submit_task(payload)
+        self._wait_and_print_result(task_id)
+
+
+if __name__ == "__main__":
+    try:
+        runner = LibLibTestRunner()
+        
+        print("选择要测试的模式:")
+        print("1. 纯文生图 (Text2Img)")
+        print("2. 纯图生图 (Img2Img)")
+        print("3. 硬边缘控制 (Canny ControlNet)")
+        print("4. 软边缘控制 (SoftEdge ControlNet)")
+        print("5. 线稿控制 (Lineart ControlNet)")
+        print("6. 骨骼控制 (OpenPose ControlNet)")
+        print("7. 局部重绘 (Inpaint Mode=4)")
+        print("8. 人像换脸 (InstantID)")
+        print("9. 全部测试 (All)")
+        
+        if len(sys.argv) > 1:
+            choice = sys.argv[1]
+        else:
+            # 默认测试文生图以验证 API Key 最基本权限
+            choice = "1"
+            
+        if choice == "1":
+            runner.test_text2img()
+        elif choice == "2":
+            runner.test_img2img()
+        elif choice == "3":
+            runner.test_text2img_controlnet()
+        elif choice == "4":
+            runner.test_text2img_softedge()
+        elif choice == "5":
+            runner.test_text2img_lineart()
+        elif choice == "6":
+            runner.test_text2img_openpose()
+        elif choice == "7":
+            runner.test_inpaint_mode4()
+        elif choice == "8":
+            runner.test_instantid_faceswap()
+        elif choice == "9":
+            runner.test_text2img()
+            runner.test_img2img()
+            runner.test_text2img_controlnet()
+            # 省略后面的测试避免同时发生太多请求...
+        else:
+            print("Unknown choice.")
+            
+    except Exception as e:
+        print(f"\n[FATAL ERROR] {e}")

+ 126 - 0
tests/test_local_openpose.py

@@ -0,0 +1,126 @@
+import os
+import sys
+import time
+import base64
+import requests
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '../tools/local/liblibai_controlnet'))
+from liblibai_client import LibLibAIClient
+
+TEMPLATE_UUID = "e10adc3949ba59abbe56e057f20f883e"
+DEFAULT_CHECKPOINT_ID = "0ea388c7eb854be3ba3c6f65aac6bfd3" 
+
+# 这里填入您实际能用的 OpenPose SDXL 模型的 UUID
+OPENPOSE_MODEL_ID = "b6806516962f4e1599a93ac4483c3d23" # 注意:这里默认放的是Canny,请记得更换成 OpenPose UUID
+
+
+def get_base64_image(image_path):
+    with open(image_path, "rb") as f:
+        return "data:image/png;base64," + base64.b64encode(f.read()).decode("utf-8")
+
+def main():
+    print("=" * 50)
+    print("测试: 读取本地图片并自动上传 -> 使用 OpenPose 生成图片")
+    print("=" * 50)
+
+    client = LibLibAIClient()
+    
+    # 1. 读取本地图片并转换为 Base64
+    local_image_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "features/pose_skeleton/img_1_openpose.png"))
+    print(f"Reading local image: {local_image_path}")
+    
+    if not os.path.exists(local_image_path):
+        print(f"Error: 找不到图片 {local_image_path}")
+        sys.exit(1)
+        
+    base64_image = get_base64_image(local_image_path)
+    
+    # 2. 借助已经写好的 `upload_base64_image` 上传到 OSS
+    print("Uploading Base64 to Liblib OSS...")
+    image_url = client.upload_base64_image(base64_image)
+    print(f"Uploaded successfully! Public URL: {image_url}")
+    
+    # 3. 发送任务
+    payload = {
+        "templateUuid": TEMPLATE_UUID,
+        "generateParams": {
+            "checkPointId": DEFAULT_CHECKPOINT_ID,
+            "prompt": "1girl, highly detailed, masterpieces, beautiful face, standing pose",
+            "negativePrompt": "lowres, bad anatomy",
+            "sampler": 15,
+            "steps": 20,
+            "cfgScale": 7.0,
+            "width": 512,
+            "height": 512,
+            "imgCount": 1,
+            "controlNet": [{
+                "unitOrder": 1,
+                "sourceImage": image_url,
+                "width": 512,
+                "height": 512,
+                "preprocessor": 14,        # 14 = OpenPose Full
+                "model": OPENPOSE_MODEL_ID, # !!需更换为OpenPose模型UUID
+                "controlWeight": 1.0,
+                "startingControlStep": 0.0,
+                "endingControlStep": 1.0,
+                "pixelPerfect": 1,
+                "controlMode": 0,
+                "annotationParameters": {
+                    "openposeFull": {
+                        "preprocessorResolution": 512
+                    }
+                }
+            }]
+        }
+    }
+    
+    auth_url = client.generate_auth_url("/api/generate/webui/text2img")
+    print(f"Submitting OpenPose payload...")
+    resp = requests.post(auth_url, json=payload, timeout=10)
+    data = resp.json()
+    if data.get("code") != 0:
+        print(f"Submit task failed: {data.get('msg')} (code: {data.get('code')})")
+        sys.exit(1)
+        
+    task_id = data["data"]["generateUuid"]
+    print(f"Task submitted! Task ID: {task_id}")
+    
+    timeout = 300
+    start_time = time.time()
+    while time.time() - start_time < timeout:
+        task_data = client.query_task_status(task_id)
+        status = task_data.get("generateStatus")
+        
+        if status == 5:
+            images = [img["imageUrl"] for img in task_data.get("images", [])]
+            print(f"\n[SUCCESSS] Generated images: {images}")
+            
+            # --- 新增: 自动下载图片到 output 文件夹 ---
+            output_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "output"))
+            os.makedirs(output_dir, exist_ok=True)
+            for i, img_url in enumerate(images):
+                try:
+                    img_resp = requests.get(img_url)
+                    img_resp.raise_for_status()
+                    # 以时间戳作为文件名前缀防止覆盖
+                    timestamp = int(time.time())
+                    filename = f"openpose_result_{timestamp}_{i}.png"
+                    filepath = os.path.join(output_dir, filename)
+                    with open(filepath, "wb") as f:
+                        f.write(img_resp.content)
+                    print(f"[{i}] 已自动下载到本地: {filepath}")
+                except Exception as e:
+                    print(f"[{i}] 图片自动下载失败: {e}")
+                    
+            return
+        elif status in [6, 7]:
+            print("\n[FAILED] Task failed.")
+            return
+            
+        print(".", end="", flush=True)
+        time.sleep(5)
+        
+    print("\n[TIMEOUT]")
+
+if __name__ == "__main__":
+    main()

+ 100 - 0
tests/test_router_api.py

@@ -377,6 +377,92 @@ def test_nano_banana():
         print("  [FAIL]")
 
 
+def test_flux():
+    print("=== Test Flux (图片生成) ===")
+    print("  调用 flux_generate 工具")
+    
+    prompt = "A majestic lion standing on a cliff at sunset, cinematic lighting, highly detailed"
+    params = {
+        "prompt": prompt,
+        "aspect_ratio": "16:9",
+        "model": "flux-kontext-pro"
+    }
+    
+    print(f"  calling flux_generate ...")
+    try:
+        ok, err, result = _run_tool("flux_generate", params, timeout=120.0)
+        if not ok:
+            print(f"  ERROR : {err}")
+            print("  [FAIL]")
+            return
+            
+        print(f"  Result : {json.dumps(result, ensure_ascii=False, indent=2)}")
+        if result and "images" in result and result["images"]:
+            print(f"    成功获取第一张图 URL: {result['images'][0]}")
+            
+            # 尝试下载图片
+            images = result["images"]
+            OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
+            for i, url in enumerate(images):
+                resp = httpx.get(url, timeout=30)
+                if resp.status_code == 200:
+                    out_path = OUTPUT_DIR / f"flux_output_{int(time.time())}_{i}.png"
+                    out_path.write_bytes(resp.content)
+                    print(f"    Saved downloaded image to {out_path}")
+                else:
+                    print(f"    WARN: Failed to download URL {url}")
+            print("  [PASS]")
+        else:
+            print("  ERROR : 结果中没有 images")
+            print("  [FAIL]")
+    except Exception as e:
+        print(f"  ERROR : {e}")
+        print("  [FAIL]")
+
+def test_seedream():
+    print("=== Test SeeDream 4.0 (图片生成) ===")
+    print("  调用 seedream_generate 工具")
+    
+    prompt = "A cute cartoon cat astronaut floating in space with planets in background, pixar style"
+    params = {
+        "prompt": prompt,
+        "size": "2048x2048",
+        "quality": "hd",
+        "n": 1
+    }
+    
+    print(f"  calling seedream_generate ...")
+    try:
+        ok, err, result = _run_tool("seedream_generate", params, timeout=120.0)
+        if not ok:
+            print(f"  ERROR : {err}")
+            print("  [FAIL]")
+            return
+            
+        print(f"  Result : {json.dumps(result, ensure_ascii=False, indent=2)}")
+        if result and "images" in result and result["images"]:
+            print(f"    成功获取第一张图 URL: {result['images'][0]}")
+            
+            # 尝试下载图片
+            images = result["images"]
+            OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
+            for i, url in enumerate(images):
+                resp = httpx.get(url, timeout=30)
+                if resp.status_code == 200:
+                    out_path = OUTPUT_DIR / f"seedream_output_{int(time.time())}_{i}.png"
+                    out_path.write_bytes(resp.content)
+                    print(f"    Saved downloaded image to {out_path}")
+                else:
+                    print(f"    WARN: Failed to download URL {url}")
+            print("  [PASS]")
+        else:
+            print("  ERROR : 结果中没有 images")
+            print("  [FAIL]")
+    except Exception as e:
+        print(f"  ERROR : {e}")
+        print("  [FAIL]")
+
+
 def load_task_spec(task_name: str) -> dict:
     task_file = TASKS_DIR / f"{task_name}.json"
     if not task_file.exists():
@@ -449,6 +535,10 @@ def main():
                         help="test image stitcher with sample images")
     parser.add_argument("--nano_banana", action="store_true",
                         help="test nano_banana (Gemini); need GEMINI_API_KEY in tools/local/nano_banana/.env")
+    parser.add_argument("--flux", action="store_true",
+                        help="test flux_generate tool")
+    parser.add_argument("--seedream", action="store_true",
+                        help="test seedream_generate tool")
     parser.add_argument("--create", nargs="?", const="", metavar="TASK_NAME",
                         help="create tool, optional task file name")
     parser.add_argument("--launch-env", action="store_true",
@@ -492,6 +582,16 @@ def main():
         test_nano_banana()
         ran_any = True
 
+    if args.flux:
+        print()
+        test_flux()
+        ran_any = True
+
+    if args.seedream:
+        print()
+        test_seedream()
+        ran_any = True
+
     if args.create is not None:
         print()
         test_create_tool(args.create or None)

+ 59 - 135
tests/upload.py

@@ -1,146 +1,70 @@
-import requests
+import asyncio
 import os
 import sys
-import uuid
-import hmac
-import time
-import hashlib
-import base64
-from collections import OrderedDict
-from dotenv import load_dotenv
+import httpx
+from cyber_sdk.ali_oss import upload_localfile
 
-# 加载环境变量(AK/SK)
-load_dotenv()
-
-# ========== 核心配置(替换成你的信息) ==========
-LIBLIBAI_AK = os.getenv("LIBLIBAI_ACCESS_KEY")  # 从.env读取或直接写死
-LIBLIBAI_SK = os.getenv("LIBLIBAI_SECRET_KEY")
-LOCAL_IMAGE_PATH = r"C:\Users\11304\gitlab\cybertogether\tool_agent\control_reference.png"  # 你的本地图片路径
-LIBLIBAI_DOMAIN = "https://openapi.liblibai.cloud"
-
-# ========== 核心工具函数 ==========
-def generate_auth_url(uri, ak, sk):
-    """生成带签名的请求URL(仅用于获取OSS签名)"""
-    ts = str(int(time.time() * 1000))
-    nonce = uuid.uuid4().hex
-    sign_str = f"{uri}&{ts}&{nonce}"
-    # 生成签名
-    dig = hmac.new(sk.encode(), sign_str.encode(), hashlib.sha1).digest()
-    sig = base64.urlsafe_b64encode(dig).rstrip(b"=").decode()
-    # 拼接URL
-    return f"{LIBLIBAI_DOMAIN}{uri}?AccessKey={ak}&Timestamp={ts}&SignatureNonce={nonce}&Signature={sig}"
-
-def get_oss_upload_signature(file_name, ak, sk):
-    """获取OSS上传签名"""
-    # 1. 提取文件扩展名
-    ext = os.path.splitext(file_name)[1].lstrip('.').lower()
-    if ext not in ['jpg', 'jpeg', 'png']:
-        print(f"❌ 不支持的文件格式: {ext},仅支持jpg/png/jpeg")
-        return None
+async def upload_image(local_file_path: str, bucket_name: str = 'aigc-admin', bucket_path: str = 'template') -> str:
+    """Uploads a local image to OSS and returns the CDN URL."""
+    print(f"Uploading {local_file_path} to {bucket_name}/{bucket_path}...")
+    # Use forward slashes so cyber_sdk's .split('/') can correctly extract filename on Windows
+    safe_path = os.path.abspath(local_file_path).replace("\\", "/")
+    result = await upload_localfile(
+        file_path=safe_path, 
+        bucket_path=bucket_path, 
+        bucket_name=bucket_name
+    )
+    print("Upload SDK Response:", result)
     
-    # 2. 调用签名接口
-    uri = "/api/generate/upload/signature"
-    auth_url = generate_auth_url(uri, ak, sk)
-    headers = {'Content-Type': 'application/json'}
-    payload = {
-        "name": file_name,  # 完整文件名(含扩展名)
-        "extension": ext
-    }
+    oss_object_key = result.get('oss_object_key')
+    if oss_object_key:
+        cdn_url = f"https://res.cybertogether.net/{oss_object_key}"
+        return cdn_url
+    return None
 
-    try:
-        print(f"📡 正在请求OSS签名... URL: {auth_url[:100]}...")
-        resp = requests.post(auth_url, headers=headers, json=payload, timeout=10)
+async def download_image(url: str, save_path: str):
+    """Downloads an image from an HTTP link and saves it locally."""
+    print(f"Downloading from {url} to {save_path}...")
+    async with httpx.AsyncClient(timeout=30.0) as client:
+        resp = await client.get(url)
         resp.raise_for_status()
-        result = resp.json()
+        with open(save_path, 'wb') as f:
+            f.write(resp.content)
+    print(f"Download completed: {save_path}")
+
+async def main():
+    if len(sys.argv) < 2:
+        print("Usage:")
+        print("  Upload: python upload.py <file_path>")
+        print("  Download: python upload.py download <url> <save_path>")
         
-        if result.get("code") != 0:
-            print(f"❌ 获取签名失败: {result.get('msg')}")
-            return None
+        # Self-test block if no param is given
+        print("\n--- Running Self Test ---")
+        test_file = 'img_1_gen.png'
+        if not os.path.exists(test_file):
+            print(f"Creating a dummy 1x1 PNG at {test_file} for testing.")
+            with open(test_file, 'wb') as f:
+                f.write(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\x00\x01\x00\x00\x05\x00\x01\r\n-\xb4\x00\x00\x00\x00IEND\xaeB`\x82')
         
-        sig_data = result.get("data")
-        print(f"✅ 获取签名成功!")
-        print(f"   - postUrl: {sig_data['postUrl']}")
-        print(f"   - key: {sig_data['key']}")
-        return sig_data
-    except Exception as e:
-        print(f"❌ 请求签名接口异常: {str(e)}")
-        return None
-
-def upload_to_oss(sig_data, local_file_path):
-    """上传本地文件到OSS"""
-    # 1. 校验文件
-    if not os.path.exists(local_file_path):
-        print(f"❌ 本地文件不存在: {local_file_path}")
-        return None
-    
-    file_name = os.path.basename(local_file_path)
-    ext = os.path.splitext(local_file_path)[1].lower()
-    content_type = 'image/jpeg' if ext in ['.jpg', '.jpeg'] else 'image/png'
-
-    # 2. 构造表单参数(必须用OrderedDict保证顺序,file放最后)
-    form_data = OrderedDict([
-        ('key', sig_data['key']),
-        ('policy', sig_data['policy']),
-        ('x-oss-date', sig_data['xOssDate']),
-        ('x-oss-expires', str(sig_data['xOssExpires'])),
-        ('x-oss-signature', sig_data['xOssSignature']),
-        ('x-oss-credential', sig_data['xOssCredential']),
-        ('x-oss-signature-version', sig_data['xOssSignatureVersion'])
-    ])
-
-    # 3. 读取文件并上传
-    try:
-        print(f"📤 正在上传文件到OSS...")
-        print(f"   - 本地文件: {local_file_path}")
-        print(f"   - OSS地址: {sig_data['postUrl']}")
-        with open(local_file_path, 'rb') as f:
-            files = {
-                'file': (file_name, f, content_type)  # file必须是最后一个字段
-            }
-            # 发送上传请求
-            resp = requests.post(
-                sig_data['postUrl'],
-                data=form_data,
-                files=files,
-                timeout=30,
-                headers={'User-Agent': 'Mozilla/5.0'}
-            )
+        url = await upload_image(test_file, 'aigc-admin', 'crawler/image')
+        print(f"\nExtracted URL: {url}")
         
-        # 4. 输出上传结果
-        print(f"\n📋 上传响应信息:")
-        print(f"   - 状态码: {resp.status_code}")
-        print(f"   - 响应头: {dict(resp.headers)}")
-        print(f"   - 响应体: {resp.text[:500] if resp.text else '空'}")
-
-        if resp.status_code in [200, 204]:
-            # 拼接最终的OSS URL(仅用于LiblibAI API调用,浏览器访问会403)
-            oss_url = f"{sig_data['postUrl']}/{sig_data['key']}"
-            print(f"\n✅ 上传成功!OSS URL: {oss_url}")
-            print(f"⚠️  注意:该URL仅能用于LiblibAI API调用,浏览器直接访问会返回403(私有读权限)")
-            return oss_url
+        if url:
+            # Download it back
+            download_path = "downloaded_dummy.png"
+            await download_image(url, download_path)
+            
+    elif sys.argv[1] == 'download':
+        if len(sys.argv) >= 4:
+            await download_image(sys.argv[2], sys.argv[3])
         else:
-            print(f"\n❌ 上传失败!状态码: {resp.status_code}")
-            return None
-    except Exception as e:
-        print(f"\n❌ 上传过程异常: {str(e)}")
-        return None
+            print("Error: Missing parameters for download.")
+            print("Usage: python upload.py download <url> <save_path>")
+    else:
+        # Upload context
+        file_path = sys.argv[1]
+        url = await upload_image(file_path, 'aigc-admin', 'crawler/image')
+        print(f"\nFinal CDN URL: {url}")
 
-# ========== 主执行逻辑 ==========
-if __name__ == "__main__":
-    # 1. 基础校验
-    if not LIBLIBAI_AK or not LIBLIBAI_SK:
-        print("❌ 请先配置LIBLIBAI_ACCESS_KEY和LIBLIBAI_SECRET_KEY(.env文件或直接写死)")
-        sys.exit(1)
-    
-    if not os.path.exists(LOCAL_IMAGE_PATH):
-        print(f"❌ 本地图片不存在: {LOCAL_IMAGE_PATH}")
-        sys.exit(1)
-    
-    # 2. 获取OSS签名
-    file_name = os.path.basename(LOCAL_IMAGE_PATH)
-    sig_data = get_oss_upload_signature(file_name, LIBLIBAI_AK, LIBLIBAI_SK)
-    if not sig_data:
-        sys.exit(1)
-    
-    # 3. 上传图片到OSS
-    upload_to_oss(sig_data, LOCAL_IMAGE_PATH)
+if __name__ == '__main__':
+    asyncio.run(main())

Plik diff jest za duży
+ 0 - 0
tmp_api1.json


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików