Skill: lianghaoxun/ai-photo-pro (version 2.0.0)

--- name: ai-photo-pro version: 2.0.0 description: 通过 NVIDIA NIM API 或 SiliconFlow API 生成图片。支持 Kolors (快手可图)、Qwen-Image (通义千问)、flux.2-klein-4b 等模型。当用户要求"生成图片"、"画一张图"、"AI绘图"或类似表达时调用。支持中文提示词，返回图片文件路径。 --- # AiPhotoPro - AI 图片生成工具 支持双引擎：**NVIDIA NIM API**（flux.2-klein-4b）和 **SiliconFlow API**（Kolors / Qwen-Image）。 ## 调用方式 ### 命令行（推荐） ```bash # SiliconFlow - 可图 Kolors（默认） python /home/ubuntu/.openclaw/skills/ai-photo-pro/scripts/siliconflow_main.py "<提示词>" ["<负面提示词>"] # SiliconFlow - 通义千问 Qwen-Image（付费模型，建议按需选取） python /home/ubuntu/.openclaw/skills/ai-photo-pro/scripts/siliconflow_main.py "<提示词>" ["<负面提示词>"] --model Qwen/Qwen-Image # NVIDIA NIM API - flux.2-klein-4b python /home/ubuntu/.openclaw/skills/ai-photo-pro/scripts/nvid_main.py "<提示词>" ``` ### Python 导入 ```python import sys sys.path.insert(0, '/home/ubuntu/.openclaw/skills/ai-photo-pro/scripts') # SiliconFlow from siliconflow_main import generate_png img_list = generate_png(model="Kwai-Kolors/Kolors", base_str="<提示词>", negative_prompt="<负面提示词>") # NVIDIA from nvid_main import run_pngvidapi img_path = run_pngvidapi(model="flux.2-klein-4b", base_str="<提示词>") ``` ## 参数说明 ### SiliconFlow `generate_png()` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `model` | string | ❌ | 模型名，默认 `Kwai-Kolors/Kolors`，可选 `Qwen/Qwen-Image`（付费模型，建议按需选取） | | `base_str` | string | ✅ | 中文提示词 | | `negative_prompt` | string | ❌ | 负面提示词，可空 | | `batch_size` | int | ❌ | 批量大小，默认 1 | | `num_inference_steps` | int | ❌ | 推理步骤数，默认 20 | | `guidance_scale` | float | ❌ | 提示词匹配度，默认 2.5 | ### NVIDIA `run_pngvidapi()` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `model` | string | ✅ | 固定填 `flux.2-klein-4b` | | `base_str` | string | ✅ | 中文提示词 | ## 输出 - 图片保存路径：`/home/ubuntu/.openclaw/skills/ai-photo-pro/scripts/img_data/<model>_<timestamp>.png` - 函数返回值为图片路径列表 ## API Key 配置 首次使用需配置 API Key，运行交互式配置脚本： ```bash python /home/ubuntu/.openclaw/skills/ai-photo-pro/scripts/config_json.py ``` 或手动写入 `config.json`（位于 `scripts/` 目录）： ```json { "NVID": "nvapi-你的NVID密钥", "SILICONFLOW": "sk-你的SiliconFlow密钥" } ``` ### 获取 Key - **NVID API Key**: https://nim.nvidia.com/ 注册获取 - **SiliconFlow API Key**: https://cloud.siliconflow.cn/i/IOo0eaWy 注册获取 ## 示例提示词 **人物：** ``` 一位美丽的短发东亚女性坐在高层公寓的落地窗前,身穿紧身的白色衬衫,(光线是午后柔和的定向自然光,在人物身上形成优美的明暗轮廓),脸上带着温暖而亲密的微笑,皮肤毛孔清晰,虹膜清晰锐利 ``` **物体/场景：** ``` 一个小苹果，红彤彤的，挂在绿叶树枝上，阳光照射，背景是模糊的果园，摄影风格，高清细节 ``` **风格化：** ``` 赛博朋克城市夜景，霓虹灯光，雨后街道，反射，高对比度，电影感 ``` ## 注意事项 - SiliconFlow 默认尺寸 1024×1024，steps=20 - NVIDIA 默认尺寸 1024×1024，steps=4（更快） - 生成失败 SiliconFlow 会抛出异常；NVIDIA 会自动重试最多 5 次 - 图片路径通过函数返回值传递，方便 agent 捕获并发送

path: scripts/config_json.py
content: import json
import os
#  程序启动地址
ORGPATH = os.path.dirname(os.path.abspath(__file__))
os.chdir(ORGPATH)
config_file = "config.json"
a = input(
    "您需要配置API Key才能使用本工具。\n\n"
    "1. 配置NVID API Key\n"
    "2. 配置硅基流动API Key\n"
    "3. 退出\n"
    "请输入您的选择: 选择指定数字，其他输入退出程序"
)
config_data = {}
if os.path.exists(config_file):
    with open(config_file, "r") as f:
        config_data = json.load(f)

if a == "1":
    TOKEN_KEY = input("请输入您的NVID API Key: ")
    config_data["NVID"] = TOKEN_KEY
    with open(config_file, "w") as f:
        json.dump(config_data, f, indent=2)
    print("配置文件已创建")
elif a == "2":
    TOKEN_KEY = input("请输入您的硅基流动API Key: ")
    config_data["SILICONFLOW"] = TOKEN_KEY
    with open(config_file, "w") as f:
        json.dump(config_data, f, indent=2)
    print("配置文件已创建")
else:
    exit(1)

path: scripts/nvid_main.py
content: import requests
import base64
from io import BytesIO
from PIL import Image
import pandas as pd
import requests
import json
import os
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
#  程序启动地址
ORGPATH = os.path.dirname(os.path.abspath(__file__))
os.chdir(ORGPATH)
config_file = "config.json"
count_api = 0
if not os.path.exists(config_file):
    raise FileNotFoundError("配置文件 config.json 不存在，请运行 config_json.py 创建配置文件")
else:
    try:
        with open(config_file, "r") as f:
            config = json.load(f)
            TOKEN_KEY = config["NVID"]
    except:
        raise ValueError("配置文件 config.json 至少应该存在NVID API Key配置，请运行 config_json.py 创建配置文件")

@retry(
    stop=stop_after_attempt(5),  # 重试3次后停止
    wait=wait_fixed(2),  # 每次重试间隔60秒
    retry=retry_if_exception_type((requests.exceptions.HTTPError, requests.exceptions.RequestException))  # 仅针对特定异常重试
)
def run_pngvidapi(model,base_str=None):
    invoke_url = f"https://ai.api.nvidia.com/v1/genai/black-forest-labs/{model}"
    headers = {
        "Authorization": f"Bearer {TOKEN_KEY}",
        "Accept": "application/json",
    }
    payload = {
        "prompt": base_str,
        "width": 1024,
        "height": 1024,
        # "seed": 0,
        "steps": 4
    }
    try:
        response = requests.post(invoke_url, headers=headers, json=payload)
        response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        print(f"进行尝试重启中...HTTP错误: {e}")
        raise requests.exceptions.HTTPError(e)
    response_body = response.json()
    try:
        # 解码 base64 数据
        image_data = base64.b64decode(response_body['artifacts'][0]['base64'])
        # 从字节数据创建图片
        image = Image.open(BytesIO(image_data))
        # 保存为 PNG
        import time
        timestamp = int(time.time())  
        image.save(f"{ORGPATH}/img_data/{model.replace('/', '_')}_{timestamp}.png", "PNG")
        print(f"图片已保存为 {ORGPATH}/img_data/{model.replace('/', '_')}_{timestamp}.png")
        return f"{ORGPATH}/img_data/{model.replace('/', '_')}_{timestamp}.png"
    except Exception as e:
        # 如果以上字段都不对,先打印响应结构看看
        print("API响应结构:", response_body.keys())
        # 或者保存整个响应供调试
        with open(f"{ORGPATH}/response.json", "w") as f:
            import json
            json.dump(response_body, f, indent=2)
        print("图片生成失败,已将响应保存为 response.json,请检查其中的图片数据字段")
        print(f"进行尝试重启中...HTTP错误: {e}")
        raise requests.exceptions.HTTPError(e)

def main():
    """命令行入口：通过 sys.argv[1] 传入提示词"""
    import sys
    if len(sys.argv) < 2:
        print("用法: python nvid_main.py <提示词>")
        sys.exit(1)
    prompt = sys.argv[1]
    img = run_pngvidapi(model="flux.2-klein-4b", base_str=prompt)
    print(img)
    return img

if __name__ == "__main__":
    main()



path: scripts/siliconflow_main.py
content: import sys
import requests
import os
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
import numpy as np
import time
import json

ORGPATH = os.path.dirname(os.path.abspath(__file__))
os.chdir(ORGPATH)
config_file = "config.json"
count_api = 0
if not os.path.exists(config_file):
    raise FileNotFoundError("配置文件 config.json 不存在，请运行 config_json.py 创建配置文件")
else:
    try:
        with open(config_file, "r") as f:
            config = json.load(f)
            TOKEN_KEY = config["SILICONFLOW"]
    except:
        raise ValueError("配置文件 config.json 至少应该存在SiliconFlow API Key配置，请运行 config_json.py 创建配置文件")

def post_gui(payload,key):
    url = "https://api.siliconflow.cn/v1/images/generations"
    try:
        headers = {
            "Authorization": key,
            "Content-Type": "application/json"
        }
        response = requests.request("POST", url, json=payload, headers=headers)
        return response.json()
    except Exception as e:
        raise ValueError(f"请求失败：{e} {response}")


def nan_check(value):
    if value==np.nan or str(value)=="" or str(value)=="None"or value=="" or value is None:
        return True
    else:
        return False


def generate_png(model="Kwai-Kolors/Kolors",batch_size=1,num_inference_steps=20,guidance_scale=2.5,base_str=None,negative_prompt=None):
    """
    生成图片
    :param model: 模型名称，可选（Kwai-Kolors/Kolors、Qwen/Qwen-Image），默认值为Kwai-Kolors/Kolors
    :param batch_size: 批量大小，推荐值为1
    :param num_inference_steps: 推理步骤数，推荐值为20
    :param guidance_scale: 提示词之间的匹配度，推荐值为2.5
    :param base_str: 提示词，不能为空
    :param negative_prompt: 负提示词，可以为空
    :return: 图片列表
    """
    if nan_check(base_str):
        raise ValueError("提示词不能为空")
    payload = {
        "model": model,
        "prompt": base_str,
        "image_size": "1024x1024",
        "batch_size": batch_size,
        "num_inference_steps": num_inference_steps, #推理步骤数
        "guidance_scale": guidance_scale, #提示词之间的匹配度
        }
    if not nan_check(negative_prompt):
        payload["negative_prompt"] = negative_prompt
    try:
        response = post_gui(payload,key=f"Bearer {TOKEN_KEY}")
        img_list = []
        for i in range(len(response["images"])):
            image_url = response["images"][i]["url"]
            # 下载图片
            response_png = requests.get(image_url)
            # 根据时间编码命名图片 
            timestamp = int(time.time())  
            with open(f"{ORGPATH}/img_data/siliconflow_{model.replace('/', '_')}_{timestamp}.png", "wb") as f: 
                f.write(response_png.content)
            img_list.append(f"{ORGPATH}/img_data/siliconflow_{model.replace('/', '_')}_{timestamp}.png")
        print(f"图片已保存在: {img_list}")
        return img_list
    except Exception as e:
        print(f"生成图片失败：{e} 重试中...",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())))
        raise ValueError(f"生成图片失败：{e} {response}")

def main():
    """
    命令行入口，供其他 agent 调用
    用法: python siliconflow_main.py "<提示词>" ["<负面提示词>"] [--model <模型名>]
    负面提示词可省略
    模型可选: Kwai-Kolors/Kolors (默认), Qwen/Qwen-Image
    """
    if len(sys.argv) < 2:
        print("用法: python siliconflow_main.py <提示词> [负面提示词] [--model <模型名>]")
        sys.exit(1)

    model = "Kwai-Kolors/Kolors"
    base_str = None
    negative_prompt = None

    i = 1
    while i < len(sys.argv):
        if sys.argv[i] == "--model" and i + 1 < len(sys.argv):
            model = sys.argv[i + 1]
            i += 2
        elif base_str is None:
            base_str = sys.argv[i]
            i += 1
        else:
            negative_prompt = sys.argv[i]
            i += 1

    if base_str is None:
        print("用法: python siliconflow_main.py <提示词> [负面提示词] [--model <模型名>]")
        sys.exit(1)

    return generate_png(model=model, base_str=base_str, negative_prompt=negative_prompt)

if __name__ == "__main__":
    main()

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)