auto-save 2026-05-18 01:02 (~2)
This commit is contained in:
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
41
api/main.py
41
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}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user