2025-12-17 17:40:45 +08:00
#!/usr/bin/env python3
"""
2026-01-23 19:32:06 +08:00
Qwen-Image OpenAI-compatible image generation client.
2025-12-17 17:40:45 +08:00
Usage:
python openai_chat_client.py --prompt "A beautiful landscape" --output output.png
python openai_chat_client.py --prompt "A sunset" --height 1024 --width 1024 --steps 50 --seed 42
"""
import argparse
import base64
from pathlib import Path
import requests
def generate_image (
prompt : str ,
server_url : str = "http://localhost:8091" ,
height : int | None = None ,
width : int | None = None ,
steps : int | None = None ,
true_cfg_scale : float | None = None ,
seed : int | None = None ,
negative_prompt : str | None = None ,
num_outputs_per_prompt : int = 1 ,
2026-02-26 16:48:29 +08:00
lora_path : str | None = None ,
lora_name : str | None = None ,
lora_scale : float | None = None ,
lora_int_id : int | None = None ,
2026-04-07 15:37:17 +08:00
use_system_prompt : str | None = None ,
system_prompt : str | None = None ,
2025-12-17 17:40:45 +08:00
) -> bytes | None :
2026-01-23 19:32:06 +08:00
"""Generate an image using the images generation API.
2025-12-17 17:40:45 +08:00
Args:
prompt: Text description of the image
server_url: Server URL
height: Image height in pixels
width: Image width in pixels
2026-01-23 19:32:06 +08:00
steps: Number of diffusion steps
2025-12-17 17:40:45 +08:00
true_cfg_scale: Qwen-Image CFG scale
seed: Random seed
negative_prompt: Negative prompt
num_outputs_per_prompt: Number of images to generate
2026-02-26 16:48:29 +08:00
lora_path: Server-local LoRA adapter folder path (PEFT format)
lora_name: LoRA name (optional, defaults to path stem)
lora_scale: LoRA scale factor (default: 1.0)
lora_int_id: LoRA integer ID (optional, derived from path if not provided)
2026-04-07 15:37:17 +08:00
use_system_prompt: System prompt for generation.
system_prompt: Custom system prompt.
2025-12-17 17:40:45 +08:00
Returns:
Image bytes or None if failed
"""
2026-01-23 19:32:06 +08:00
payload : dict [ str , object ] = {
"prompt" : prompt ,
"response_format" : "b64_json" ,
"n" : num_outputs_per_prompt ,
}
if width is not None and height is not None :
payload [ "size" ] = f " { width } x { height } "
elif width is not None :
payload [ "size" ] = f " { width } x { width } "
elif height is not None :
payload [ "size" ] = f " { height } x { height } "
2025-12-17 17:40:45 +08:00
if steps is not None :
2026-01-23 19:32:06 +08:00
payload [ "num_inference_steps" ] = steps
2025-12-17 17:40:45 +08:00
if true_cfg_scale is not None :
2026-01-23 19:32:06 +08:00
payload [ "true_cfg_scale" ] = true_cfg_scale
2025-12-17 17:40:45 +08:00
if negative_prompt :
2026-01-23 19:32:06 +08:00
payload [ "negative_prompt" ] = negative_prompt
if seed is not None :
payload [ "seed" ] = seed
2026-04-07 15:37:17 +08:00
if use_system_prompt is not None :
payload [ "use_system_prompt" ] = use_system_prompt
if system_prompt is not None :
payload [ "system_prompt" ] = system_prompt
2026-02-26 16:48:29 +08:00
# Add LoRA if provided
if lora_path :
lora_body : dict = {
"local_path" : lora_path ,
"name" : lora_name or Path ( lora_path ) . stem ,
}
if lora_scale is not None :
lora_body [ "scale" ] = float ( lora_scale )
if lora_int_id is not None :
lora_body [ "int_id" ] = int ( lora_int_id )
payload [ "lora" ] = lora_body
2025-12-17 17:40:45 +08:00
try :
response = requests . post (
2026-01-23 19:32:06 +08:00
f " { server_url } /v1/images/generations" ,
2025-12-17 17:40:45 +08:00
headers = { "Content-Type" : "application/json" },
json = payload ,
timeout = 300 ,
)
response . raise_for_status ()
data = response . json ()
2026-01-23 19:32:06 +08:00
items = data . get ( "data" )
if isinstance ( items , list ) and items :
first = items [ 0 ] . get ( "b64_json" ) if isinstance ( items [ 0 ], dict ) else None
if isinstance ( first , str ):
return base64 . b64decode ( first )
2025-12-17 17:40:45 +08:00
2026-01-23 19:32:06 +08:00
print ( f "Unexpected response format: { data } " )
2025-12-17 17:40:45 +08:00
return None
except Exception as e :
print ( f "Error: { e } " )
return None
def main ():
parser = argparse . ArgumentParser ( description = "Qwen-Image chat client" )
parser . add_argument ( "--prompt" , "-p" , default = "a cup of coffee on the table" , help = "Text prompt" )
parser . add_argument ( "--output" , "-o" , default = "qwen_image_output.png" , help = "Output file" )
parser . add_argument ( "--server" , "-s" , default = "http://localhost:8091" , help = "Server URL" )
parser . add_argument ( "--height" , type = int , default = 1024 , help = "Image height" )
parser . add_argument ( "--width" , type = int , default = 1024 , help = "Image width" )
parser . add_argument ( "--steps" , type = int , default = 50 , help = "Inference steps" )
parser . add_argument ( "--cfg-scale" , type = float , default = 4.0 , help = "True CFG scale" )
2026-01-15 11:18:28 +08:00
parser . add_argument ( "--seed" , type = int , default = 0 , help = "Random seed" )
2025-12-17 17:40:45 +08:00
parser . add_argument ( "--negative" , help = "Negative prompt" )
2026-02-26 16:48:29 +08:00
parser . add_argument ( "--lora-path" , default = None , help = "Server-local LoRA adapter folder (PEFT format)" )
parser . add_argument ( "--lora-name" , default = None , help = "LoRA name (optional)" )
parser . add_argument ( "--lora-scale" , type = float , default = 1.0 , help = "LoRA scale" )
parser . add_argument (
"--lora-int-id" ,
type = int ,
default = None ,
help = "LoRA integer id (cache key). If omitted, the server derives a stable id from lora_path." ,
)
2026-04-07 15:37:17 +08:00
parser . add_argument (
"--use-system-prompt" ,
type = str ,
default = None ,
help = (
"System prompt for generation. Use predefined types: 'en_unified', 'en_vanilla', 'en_recaption', 'en_think_recaption', 'dynamic', or 'None'; Or provide custom text string directly. Recommended en_unified. "
),
)
parser . add_argument (
"--system-prompt" ,
type = str ,
default = None ,
help = ( "Custom system prompt. Used when --use-system-prompt is custom. " ),
)
2025-12-17 17:40:45 +08:00
args = parser . parse_args ()
print ( f "Generating image for: { args . prompt } " )
image_bytes = generate_image (
prompt = args . prompt ,
server_url = args . server ,
height = args . height ,
width = args . width ,
steps = args . steps ,
true_cfg_scale = args . cfg_scale ,
seed = args . seed ,
negative_prompt = args . negative ,
2026-02-26 16:48:29 +08:00
lora_path = args . lora_path ,
lora_name = args . lora_name ,
lora_scale = args . lora_scale if args . lora_path else None ,
lora_int_id = args . lora_int_id if args . lora_path else None ,
2026-04-07 15:37:17 +08:00
use_system_prompt = args . use_system_prompt ,
system_prompt = args . system_prompt ,
2025-12-17 17:40:45 +08:00
)
if image_bytes :
output_path = Path ( args . output )
output_path . write_bytes ( image_bytes )
print ( f "Image saved to: { output_path } " )
print ( f "Size: { len ( image_bytes ) / 1024 : .1f } KB" )
else :
print ( "Failed to generate image" )
exit ( 1 )
if __name__ == "__main__" :
main ()