feat: 故事生成时自动用豆包 Seedream 4.5 文生图模型生成封面
- llm_service: 添加 _generate_and_upload_cover,调用 doubao-seedream-4-5-251128 生成 1920x1920 儿童绘本风格封面,下载后上传至 OSS,URL 随 done 事件返回 - SSE 新增 cover 阶段(progress=90),非致命异常不影响故事主流程 - settings: LLM_CONFIG 新增 IMAGE_MODEL_NAME / IMAGE_SIZE,可通过环境变量覆盖 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
487b258bbe
commit
cbfe747553
@ -122,12 +122,28 @@ def generate_story_stream(characters, scenes, props):
|
||||
|
||||
result = _parse_story_json(full_content)
|
||||
|
||||
# ── Generate cover image ──
|
||||
yield _sse_event('stage', {
|
||||
'stage': 'cover',
|
||||
'progress': 90,
|
||||
'message': '正在绘制故事封面...',
|
||||
})
|
||||
|
||||
cover_url = ''
|
||||
try:
|
||||
cover_url = _generate_and_upload_cover(
|
||||
result['title'], characters, scenes, props, config
|
||||
)
|
||||
except Exception as cover_err:
|
||||
logger.warning(f'Cover generation failed (non-fatal): {cover_err}')
|
||||
|
||||
yield _sse_event('done', {
|
||||
'stage': 'done',
|
||||
'progress': 100,
|
||||
'message': '大功告成!',
|
||||
'title': result['title'],
|
||||
'content': result['content'],
|
||||
'cover_url': cover_url,
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
@ -157,6 +173,64 @@ def _parse_story_json(text):
|
||||
}
|
||||
|
||||
|
||||
def _generate_and_upload_cover(title, characters, scenes, props, config):
|
||||
"""
|
||||
使用豆包文生图模型生成故事封面,上传到 OSS 并返回 URL。
|
||||
失败时抛出异常(由调用方捕获,不影响主流程)。
|
||||
"""
|
||||
import uuid
|
||||
import requests as req_lib
|
||||
from datetime import datetime
|
||||
from django.conf import settings
|
||||
from volcenginesdkarkruntime import Ark
|
||||
|
||||
# Build a concise Chinese image prompt
|
||||
elements = []
|
||||
if characters:
|
||||
elements.append('、'.join(characters))
|
||||
if scenes:
|
||||
elements.append('、'.join(scenes))
|
||||
if props:
|
||||
elements.append('、'.join(props))
|
||||
|
||||
element_str = ','.join(elements) if elements else title
|
||||
image_prompt = (
|
||||
f"儿童绘本封面插画,{title},{element_str},"
|
||||
"卡通可爱风格,色彩明亮鲜艳,温馨有趣,适合3-8岁儿童,高质量插画"
|
||||
)
|
||||
|
||||
client = Ark(api_key=config['API_KEY'])
|
||||
image_model = config.get('IMAGE_MODEL_NAME', 'doubao-seedream-4-5-251128')
|
||||
image_size = config.get('IMAGE_SIZE', '1920x1920')
|
||||
|
||||
result = client.images.generate(
|
||||
model=image_model,
|
||||
prompt=image_prompt,
|
||||
size=image_size,
|
||||
response_format='url',
|
||||
watermark=False,
|
||||
)
|
||||
|
||||
image_url = result.data[0].url
|
||||
|
||||
# Download from temporary URL and upload to OSS
|
||||
resp = req_lib.get(image_url, timeout=60)
|
||||
resp.raise_for_status()
|
||||
|
||||
from utils.oss import get_oss_client
|
||||
oss_client = get_oss_client()
|
||||
key = f"stories/covers/{datetime.now().strftime('%Y%m%d')}/{uuid.uuid4().hex}.jpg"
|
||||
oss_client.bucket.put_object(
|
||||
key, resp.content,
|
||||
headers={'Content-Type': 'image/jpeg'},
|
||||
)
|
||||
|
||||
oss_config = settings.ALIYUN_OSS
|
||||
if oss_config.get('CUSTOM_DOMAIN'):
|
||||
return f"https://{oss_config['CUSTOM_DOMAIN']}/{key}"
|
||||
return f"https://{oss_config['BUCKET_NAME']}.{oss_config['ENDPOINT']}/{key}"
|
||||
|
||||
|
||||
def _sse_event(event, data):
|
||||
"""格式化 SSE 事件"""
|
||||
return f"event: {event}\ndata: {json.dumps(data, ensure_ascii=False)}\n\n"
|
||||
|
||||
@ -198,6 +198,8 @@ ALIYUN_PHONE_AUTH = {
|
||||
LLM_CONFIG = {
|
||||
'API_KEY': os.environ.get('VOLCENGINE_API_KEY', ''),
|
||||
'MODEL_NAME': os.environ.get('VOLCENGINE_MODEL_NAME', 'doubao-seed-1-6-lite-251015'),
|
||||
'IMAGE_MODEL_NAME': os.environ.get('VOLCENGINE_IMAGE_MODEL_NAME', 'doubao-seedream-4-5-251128'),
|
||||
'IMAGE_SIZE': os.environ.get('VOLCENGINE_IMAGE_SIZE', '1920x1920'),
|
||||
}
|
||||
|
||||
# Swagger/OpenAPI Settings
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user