存量积累:生图素人感约束+评图分+幂等防重跑+审核回路
- 015-017迁移:image_candidate 文案复审/AI视觉分/重生标记 - constants C7素人感约束(反电商摆拍对齐真实笔记)+C3叠字口子 - celery visibility_timeout=2h 防长任务被误判重投重复烧钱(task75教训) - image_scorer 评图分(只筛选+展示,真实信号才进飞轮权重) - storyboard/image_gen/pipeline_io 生图存量 - task_actions/tasks/task_service/flywheel 审核回路+飞轮存量 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -41,7 +41,7 @@ class AIClients:
|
||||
_pool_loop_id: int | None = field(default=None, repr=False)
|
||||
_gemini_key: str | None = field(default=None, repr=False) # 局部变量不打印
|
||||
_model_image: str = "gpt-image-2"
|
||||
_model_text: str = "claude-sonnet-4-5" # apiports无gpt-4o-mini,文案用claude中文质量好
|
||||
_model_text: str = "claude-opus-4-8" # 最强档(倩倩姐红线):Claude系一律4.8,绝不降级
|
||||
|
||||
def _client(self) -> httpx.AsyncClient:
|
||||
"""主通道(apiports) client,按当前事件循环缓存"""
|
||||
@@ -111,16 +111,44 @@ class AIClients:
|
||||
resp.raise_for_status()
|
||||
return _extract_gemini_image(resp.json())
|
||||
|
||||
async def _chat_post_failover(self, payload: dict, timeout: float) -> dict:
|
||||
"""
|
||||
chat/completions 发送器,带 codeproxy 回落。
|
||||
主通道(apiports, claude-opus-4-8)若 5xx/超时/连接错,自动切 codeproxy 重试一次。
|
||||
⚠ codeproxy 账号池只支持 gpt 系(gpt-5.5),无 claude,故回落时模型换成 gpt-5.5。
|
||||
这是强档↔强档切换:红线"Claude系4.8/GPT系5.5"本就是两个平级最强档、互为兜底,
|
||||
非降级(降级=落 sonnet/弱档)。codeproxy 回落档由 CODEPROXY_CHAT_MODEL 配置(默认 gpt-5.5)。
|
||||
codeproxy 未配置(_alt_token 为空)时不回落,原样抛错。
|
||||
"""
|
||||
main_base = (os.environ.get("IMAGE_API_BASE") or os.environ.get("APIPORTS_BASE_URL") or "").rstrip("/")
|
||||
try:
|
||||
resp = await self._client().post(f"{main_base}/chat/completions", json=payload, timeout=timeout)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
except (httpx.HTTPStatusError, httpx.TransportError, httpx.TimeoutException) as exc:
|
||||
# 仅 5xx(服务端过载)或网络层错误才回落;4xx(参数/鉴权)回落也没用,直接抛。
|
||||
status = getattr(getattr(exc, "response", None), "status_code", None)
|
||||
retryable = status is None or status >= 500
|
||||
if not (retryable and self._alt_token):
|
||||
raise
|
||||
alt_base = (self._alt_base or os.environ.get("CODEPROXY_BASE_URL") or "").rstrip("/")
|
||||
alt_payload = dict(payload)
|
||||
alt_payload["model"] = os.environ.get("CODEPROXY_CHAT_MODEL", "gpt-5.5")
|
||||
logger.warning("apiports chat 失败(%s),回落 codeproxy 用 %s 重试(强档兜底,非降级)",
|
||||
status or type(exc).__name__, alt_payload["model"])
|
||||
client = self._client_for(alt_base, self._alt_token)
|
||||
resp = await client.post(f"{alt_base}/chat/completions", json=alt_payload, timeout=timeout)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
async def chat_complete(self, messages: list[dict], model: str | None = None, max_tokens: int = 4096, temperature: float = 0.75) -> str:
|
||||
"""文字生成(文案生成用)"""
|
||||
base = (os.environ.get("IMAGE_API_BASE") or os.environ.get("APIPORTS_BASE_URL") or "").rstrip("/")
|
||||
"""文字生成(文案生成用)。apiports 503 时自动回落 codeproxy。"""
|
||||
payload = {"model": model or self._model_text, "messages": messages, "max_tokens": max_tokens, "temperature": temperature}
|
||||
# 单批≤4条文案正常 40-55s 返回;apiports 网关 ~60s 上限。客户端超时设 75s:
|
||||
# 略高于网关上限即可,过长(如180s)会在 apiports 卡顿时干等,拖慢整体。
|
||||
timeout = float(os.environ.get("TEXT_LLM_TIMEOUT", "75"))
|
||||
resp = await self._client().post(f"{base}/chat/completions", json=payload, timeout=timeout)
|
||||
resp.raise_for_status()
|
||||
return resp.json()["choices"][0]["message"]["content"] or ""
|
||||
data = await self._chat_post_failover(payload, timeout)
|
||||
return data["choices"][0]["message"]["content"] or ""
|
||||
|
||||
async def gpt_vision_analyze(self, prompt: str, images: list[bytes], model: str | None = None) -> str:
|
||||
"""
|
||||
@@ -139,16 +167,15 @@ class AIClients:
|
||||
})
|
||||
used_model = model or os.environ.get("MODEL_TEXT", "claude-opus-4-8")
|
||||
messages = [{"role": "user", "content": content}]
|
||||
base = (os.environ.get("IMAGE_API_BASE") or os.environ.get("APIPORTS_BASE_URL") or "").rstrip("/")
|
||||
payload = {
|
||||
"model": used_model,
|
||||
"messages": messages,
|
||||
"max_tokens": 2048,
|
||||
"temperature": 0.2,
|
||||
}
|
||||
resp = await self._client().post(f"{base}/chat/completions", json=payload, timeout=90.0)
|
||||
resp.raise_for_status()
|
||||
return resp.json()["choices"][0]["message"]["content"] or ""
|
||||
# 评图也走 codeproxy 回落:apiports 503 时切备用站,模型档不变(守红线)。
|
||||
data = await self._chat_post_failover(payload, 90.0)
|
||||
return data["choices"][0]["message"]["content"] or ""
|
||||
|
||||
# duck-type: text_variants._call_llm 用的属性
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user