From 4c43d893460056b5197ee3d9b429a802185664ed Mon Sep 17 00:00:00 2001 From: kang Date: Mon, 18 May 2026 01:02:30 +0800 Subject: [PATCH] auto-save 2026-05-18 01:02 (~2) --- .memory/worklog.json | 26 +++++++++++++------------- api/main.py | 41 ++++++++++++++--------------------------- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index 66d1034..ac0afd3 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,18 +1,5 @@ { "entries": [ - { - "files_changed": 4, - "hash": "eabec39", - "message": "auto-save 2026-05-15 15:54 (~4)", - "ts": "2026-05-15T15:54:34+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-15 15:54 (~4)", - "ts": "2026-05-15T07:54:47Z", - "type": "session-heartbeat" - }, { "files_changed": 4, "hash": "4b44c28", @@ -3257,6 +3244,19 @@ "message": "feat: route media models by provider", "hash": "29bfaef", "files_changed": 0 + }, + { + "ts": "2026-05-18T00:57:07+08:00", + "type": "commit", + "message": "auto-save 2026-05-18 00:57 (~4)", + "hash": "34ecab4", + "files_changed": 4 + }, + { + "ts": "2026-05-17T16:58:32Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:auto-save 2026-05-18 00:57 (~4)", + "files_changed": 1 } ] } diff --git a/api/main.py b/api/main.py index 1420a53..6f762e5 100644 --- a/api/main.py +++ b/api/main.py @@ -50,18 +50,14 @@ LOCAL_ASR_TIMEOUT_SECONDS = max(30, int(os.getenv("LOCAL_ASR_TIMEOUT_SECONDS", " TRANSLATE_MODEL = os.getenv("TRANSLATE_MODEL", "gemini-2.5-flash") REWRITE_MODEL = os.getenv("REWRITE_MODEL", "gemini-2.5-pro") VISION_MODEL = os.getenv("VISION_MODEL", "gemini-2.5-flash") -GPT_IMAGE_MODEL = os.getenv("GPT_IMAGE_MODEL", "gpt-image-2").strip() or "gpt-image-2" IMAGE_BASE_URL = os.getenv("IMAGE_BASE_URL", LLM_BASE_URL).strip() IMAGE_API_KEY = os.getenv("IMAGE_API_KEY", LLM_API_KEY).strip() -IMAGE_MODEL = os.getenv("IMAGE_MODEL", GPT_IMAGE_MODEL).strip() or GPT_IMAGE_MODEL -SUBJECT_ASSET_IMAGE_MODEL = os.getenv("SUBJECT_ASSET_IMAGE_MODEL", GPT_IMAGE_MODEL).strip() or GPT_IMAGE_MODEL -SUBJECT_ASSET_IMAGE_MODELS = [ - m.strip() - for m in os.getenv("SUBJECT_ASSET_IMAGE_MODELS", f"{SUBJECT_ASSET_IMAGE_MODEL},gpt-image-1.5").split(",") - if m.strip() -] -if SUBJECT_ASSET_IMAGE_MODEL not in SUBJECT_ASSET_IMAGE_MODELS: - SUBJECT_ASSET_IMAGE_MODELS.insert(0, SUBJECT_ASSET_IMAGE_MODEL) +# Product decision: every image-generation/editing path is locked to gpt-image-2. +# Environment variables may still choose the gateway URL/key, but not the model. +GPT_IMAGE_MODEL = "gpt-image-2" +IMAGE_MODEL = GPT_IMAGE_MODEL +SUBJECT_ASSET_IMAGE_MODEL = GPT_IMAGE_MODEL +SUBJECT_ASSET_IMAGE_MODELS = [GPT_IMAGE_MODEL] PRODUCT_ASSET_MAX_SIDE = max(1024, int(os.getenv("PRODUCT_ASSET_MAX_SIDE", "1600"))) PRODUCT_ASSET_MIN_LONG_SIDE = max(512, int(os.getenv("PRODUCT_ASSET_MIN_LONG_SIDE", "900"))) PRODUCT_ASSET_MIN_SHORT_SIDE = max(320, int(os.getenv("PRODUCT_ASSET_MIN_SHORT_SIDE", "600"))) @@ -2545,9 +2541,8 @@ def _image_edit_call( """通用 image edit 调用 · 失败重试 + 可选 text fallback。 返回 (image_bytes, effective_mode) where effective_mode in {"edit","text"}。 失败 raise RuntimeError。 - 输入图自动 resize 到 max_side(默认 1024)边长后再 base64,避免大图把 Gemini - function call 输入挤超阈值导致 incomplete_generation。 - models: 多模型轮换列表,重试时换 model;不传则单一 model 重试。""" + 输入图自动 resize 到 max_side(默认 1024)边长后再 base64。 + 生图模型按产品规则强制使用 gpt-image-2;model/models 参数只保留兼容旧调用。""" import base64 as b64lib import io as _io import time as _time @@ -2555,12 +2550,8 @@ def _image_edit_call( from PIL import Image as _PILImage if not IMAGE_API_KEY: raise RuntimeError("IMAGE_API_KEY 或 LLM_API_KEY 未配置") - # model 优先级:models 列表 > 单个 model 参数 > IMAGE_MODEL - if models and len(models) > 0: - models_cycle = list(models) - else: - models_cycle = [model or IMAGE_MODEL] - model = models_cycle[0] + models_cycle = [GPT_IMAGE_MODEL] + model = GPT_IMAGE_MODEL # 缩到 max_side 内 try: im = _PILImage.open(image_path) @@ -2583,7 +2574,6 @@ def _image_edit_call( resp_data: dict = {} effective_mode = "edit" for attempt, current_mode in enumerate(plan): - # 多模型轮换:第 N 次重试用第 N 个 model(不够时用最后一个) current_model = models_cycle[min(attempt, len(models_cycle) - 1)] try: if current_mode == "edit": @@ -2609,7 +2599,6 @@ def _image_edit_call( last_err = f"empty data · {err_obj.get('code', '')} · {str(err_obj.get('message', ''))[:200]} · model={current_model}" except httpx.HTTPStatusError as e: body = e.response.text - # 多模型轮换场景:除明确不可恢复(4xx 鉴权类)外都重试换 model sc = e.response.status_code fatal = sc in (401, 403) last_err = f"HTTP {sc}: {body[:200]} · model={current_model}" @@ -2619,8 +2608,7 @@ def _image_edit_call( last_err = f"{type(e).__name__}: {e} · model={current_model}" if attempt < len(plan) - 1: - next_model = models_cycle[min(attempt + 1, len(models_cycle) - 1)] - tag = f"retry {attempt + 1}/{len(plan)} → {next_model}" + tag = f"retry {attempt + 1}/{len(plan)} → {GPT_IMAGE_MODEL}" print(f"[image edit {tag}] {last_err}", flush=True) _time.sleep(1.0) @@ -2639,12 +2627,12 @@ def _image_text_call( models: list[str] | None = None, max_attempts: int = 3, ) -> tuple[bytes, str]: - """Text-only image generation with light model rotation.""" + """Text-only image generation. 生图模型强制使用 gpt-image-2。""" import base64 as b64lib import time as _time if not IMAGE_API_KEY: raise RuntimeError("IMAGE_API_KEY 或 LLM_API_KEY 未配置") - models_cycle = list(models) if models else [model or IMAGE_MODEL] + models_cycle = [GPT_IMAGE_MODEL] last_err = "" resp_data: dict = {} for attempt in range(max_attempts): @@ -2661,8 +2649,7 @@ def _image_text_call( except Exception as e: last_err = f"{type(e).__name__}: {e} · model={current_model}" if attempt < max_attempts - 1: - next_model = models_cycle[min(attempt + 1, len(models_cycle) - 1)] - print(f"[image text retry {attempt + 1}/{max_attempts} → {next_model}] {last_err}", flush=True) + print(f"[image text retry {attempt + 1}/{max_attempts} → {GPT_IMAGE_MODEL}] {last_err}", flush=True) _time.sleep(1.0) raise RuntimeError(f"image text failed after {max_attempts} attempts: {last_err}")