gen.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. #!/usr/bin/env python3
  2. """skill-image: 架构图 + 场景插图生成器
  3. 用法:
  4. python3 gen.py --arch "prompt" [name] # 架构图 1792x1024 ¥0.5/张
  5. python3 gen.py --scene "prompt" [name] # 场景图 1024x1024 ¥0.3/张
  6. python3 gen.py --batch-arch <json_file> # 批量架构图
  7. 凭据: 自动读取 FMODE_API_KEY (env/config)
  8. """
  9. import json, base64, urllib.request, os, sys
  10. API = 'https://api.fmode.cn/v1/images/generations'
  11. def _get_key():
  12. for src_name, src_val in [
  13. ('FMODE_API_KEY env', os.environ.get('FMODE_API_KEY', '')),
  14. ('ANTHROPIC_AUTH_TOKEN env', os.environ.get('ANTHROPIC_AUTH_TOKEN', '')),
  15. ]:
  16. if src_val and '***' not in src_val and len(src_val) > 10:
  17. return src_val
  18. try:
  19. with open(os.path.expanduser('~/.fmode/config.json')) as f:
  20. c = json.load(f)
  21. for k in ('api_key', 'FMODE_API_KEY'):
  22. if c.get(k): return c[k]
  23. except: pass
  24. try:
  25. with open(os.path.expanduser('~/.fmode/config.yaml')) as f:
  26. for l in f:
  27. for kw in ('api_key', 'FMODE_API_KEY'):
  28. if kw in l:
  29. v = l.split(':')[1].strip().strip('"\'')
  30. if v and v != '${FMODE_API_KEY}': return v
  31. except: pass
  32. for p in ['/opt/data/.env', '.env']:
  33. try:
  34. with open(p) as f:
  35. for l in f:
  36. if 'FMODE_API_KEY' in l:
  37. v = l.split('=')[1].strip().strip('"\'')
  38. if v and '***' not in v and len(v) > 10: return v
  39. except: pass
  40. print('❌ 未找到 API Key。设置环境变量 FMODE_API_KEY 或 ~/.fmode/config.json')
  41. sys.exit(1)
  42. API_KEY = _get_key()
  43. def _call_api(prompt, size):
  44. body = json.dumps({"model": "gpt-image-2.5-sunburst", "prompt": prompt, "n": 1, "size": size}).encode()
  45. req = urllib.request.Request(API, data=body,
  46. headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
  47. method='POST')
  48. resp = urllib.request.urlopen(req, timeout=300)
  49. data = json.loads(resp.read())
  50. if 'b64_json' in data['data'][0]:
  51. return base64.b64decode(data['data'][0]['b64_json'])
  52. if 'url' in data['data'][0]:
  53. return urllib.request.urlopen(data['data'][0]['url'], timeout=60).read()
  54. raise Exception('No image data')
  55. def gen_arch(prompt, name='arch'):
  56. """架构图: 1792x1024 ¥0.5/张,纯白底PPT用"""
  57. full = f'纯白背景扁平风格架构图。{prompt} 简洁扁平商务风格,纯白背景适合PPT。'
  58. out = _gen(full, '1792x1024', name)
  59. return out
  60. def gen_scene(prompt, name='scene'):
  61. """场景/插图: 1024x1024 ¥0.3/张,照片/示意图/画面"""
  62. full = f'扁平插画场景。{prompt} 清新明亮风格。'
  63. out = _gen(full, '1024x1024', name)
  64. return out
  65. def _gen(prompt, size, name):
  66. outdir = os.environ.get('SKILL_IMAGE_OUTPUT', '.')
  67. os.makedirs(outdir, exist_ok=True)
  68. path = f'{outdir}/{name}.png'
  69. data = _call_api(prompt, size)
  70. with open(path, 'wb') as f:
  71. f.write(data)
  72. sz = os.path.getsize(path)//1024
  73. print(f'✅ {sz}KB -> {path}')
  74. return path
  75. def batch_arch(items):
  76. """items: [(name, prompt), ...]"""
  77. for name, prompt in items:
  78. print(f'{name}:', end=' ', flush=True)
  79. try:
  80. gen_arch(prompt, name)
  81. except Exception as e:
  82. print(f'❌ {e}')
  83. if __name__ == '__main__':
  84. if len(sys.argv) < 3:
  85. print(__doc__)
  86. sys.exit(1)
  87. mode, content = sys.argv[1], sys.argv[2]
  88. name = sys.argv[3] if len(sys.argv) > 3 else 'output'
  89. {'--arch': lambda: gen_arch(content, name),
  90. '--scene': lambda: gen_scene(content, name)}.get(mode, lambda: print(f'Unknown mode {mode}'))()