Files
OpenMesh/coworker/providers/errors.py
zhaolei 6f402ffcee
Some checks failed
CI / pytest (push) Has been cancelled
CI / gui-unit (push) Has been cancelled
CI / gui-e2e (push) Has been cancelled
feat: OpenMesh 基础平台与 MD/PDF 转换技能
- 后端: coworker 智能体框架, WS API, 文件上传, 附件处理
- 前端: Open WebUI, 文件全量走 upload API (含 MD/TXT/JSON 等文本类)
- 技能: md-to-office (pandoc + wkhtmltopdf)
- 修复: 上传文件路径丢失, Agent 搜索浪费, 输出文件跑到 uploads/
- 打包: PyInstaller one-dir, 预打包 pandoc/wkhtmltopdf/chromium
2026-09-13 23:41:04 +08:00

58 lines
2.5 KiB
Python

"""Friendly translation of model access + quota failures.
The picker now defaults to brand-new flagships (GPT-5.6 Sol, Claude Fable 5), and not every
account can use them: OpenAI is still rolling GPT-5.6 out per-organization, and both vendors
reject calls once quota/credits run out. Those failures arrive as terse SDK exceptions
wrapping JSON error bodies; this maps the well-known shapes to one actionable sentence.
Anything unrecognized returns None and the caller surfaces the raw error unchanged.
Matching is on the error BODY text (error codes/types), not just HTTP status — a 404 also
means "wrong base_url" and a 429 also means "slow down", and neither of those should be
dressed up as an access problem.
"""
from __future__ import annotations
from typing import Optional
# Error-body markers, verbatim from the vendors' error codes/messages:
# OpenAI: {"error": {"code": "model_not_found", "message": "The model `X` does not exist or
# you do not have access to it."}} (404/403) and {"code": "insufficient_quota"} (429).
# Anthropic: {"type": "not_found_error", "message": "model: X"} (404),
# {"type": "permission_error"} (403), and "credit balance is too low" (400).
_NO_ACCESS = (
"model_not_found",
"does not exist or you do not have access",
"does not have access to model",
"permission_error",
"permission denied",
)
_NO_QUOTA = (
"insufficient_quota",
"exceeded your current quota",
"credit balance is too low",
"billing hard limit",
)
def friendly_model_error(model: str, exc: Exception) -> Optional[str]:
"""One actionable sentence for "your account can't use this model" failures, or None."""
text = str(exc).lower()
no_access = (
f"Your account doesn't have access to {model} — new models can roll out "
"gradually or require a plan upgrade. Pick a different model, or check "
"the provider's console for availability."
)
if any(marker in text for marker in _NO_QUOTA):
return (
f"Your account is out of quota for {model} — add credits or raise the limit "
"in the provider's billing console, or pick a different model."
)
if any(marker in text for marker in _NO_ACCESS):
return no_access
# Anthropic's 404 body is just "model: <id>" under type not_found_error; require both
# halves so unrelated 404s (bad base_url, deleted resource) keep their raw message.
if "not_found_error" in text and f"model: {model.split(':')[-1].lower()}" in text:
return no_access
return None