gmn2a / tests /test_upstream_errors.py
longxing's picture
上游错误码给出可执行建议;新增上下文裁剪以规避 1096
5ae00c0
Raw History Blame Contribute Delete
4.39 kB
"""上游错误码建议与上下文裁剪的验证。
背景:上游对超长上下文会返回模糊错误(例如错误码 1096)。gemini-webapi 只映射了
1013/1037/1050/1052/1060,其余走兜底分支并附一句"可能是临时 Google 服务问题"——那是库
自己的猜测,对 1096 并不准确(1096 属于后端/会话层面的临时故障,常见诱因是上下文过长)。
所以本服务自己补了一张"怎么办"的表,并在超长时裁剪历史。
"""
import os
import sys
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
os.environ["SECURE_1PSID"] = "PSID-ERR"
os.environ["SECURE_1PSIDTS"] = "TS-ERR"
os.environ["GEMINI_COOKIE_PATH"] = tempfile.mkdtemp(prefix="gs-err-")
import main as app_main # noqa: E402
PASS, FAIL = [], []
def check(name, cond, extra=""):
(PASS if cond else FAIL).append(name)
print(("[OK] " if cond else "[FAIL] ") + name + (f" {extra}" if extra and not cond else ""))
def main():
# ---------------------------------------------------------------- 错误码建议
code_1096 = (
"APIError: Failed to generate contents (stream). Unknown API error code: 1096. "
"This might be a temporary Google service issue."
)
hint = app_main.describe_upstream_error(code_1096)
check("1 认得 1096", "错误码 1096" in hint, hint)
check("2 并给出可执行建议(上下文/新对话)", "上下文过长" in hint and "新对话" in hint, hint)
check("3 明确提示不要连续重试", "不要连续重试" in hint, hint)
check("4 认得 1037(配额)", "配额" in app_main.describe_upstream_error("error code: 1037"))
check("5 认得 1060(IP 被限)", "IP" in app_main.describe_upstream_error("error code: 1060"))
check("6 认得 1050(换模型)", "模型" in app_main.describe_upstream_error("error code: 1050"))
unknown = app_main.describe_upstream_error("APIError: ... error code: 9999 ...")
check("7 未知错误码给出兜底建议", "9999" in unknown and "issue" in unknown, unknown)
check("8 没有错误码时不瞎猜", app_main.describe_upstream_error("UsageLimitExceededError: 配额用完") == "")
check("9 空输入不报错", app_main.describe_upstream_error("") == "")
# ---------------------------------------------------------------- 上下文裁剪
check("10 默认上限为正数", app_main.MAX_CONTEXT_CHARS > 0, str(app_main.MAX_CONTEXT_CHARS))
system = app_main.Message(role="system", content="S" * 100)
history = [app_main.Message(role="user", content="X" * 1000) for _ in range(100)]
kept = app_main.trim_messages([system, *history])
check("11 超长时确实裁剪了", len(kept) < 101, str(len(kept)))
check("12 system 消息始终保留", kept[0].role == "system", kept[0].role)
check("13 最后一条消息始终保留", kept[-1] is history[-1])
check("14 裁剪后总量不超上限(最后一条例外)", sum(len(str(m.content)) for m in kept) <= app_main.MAX_CONTEXT_CHARS + 1000)
short = [app_main.Message(role="user", content="hi")]
check("15 短对话原样返回", app_main.trim_messages(short) == short)
check("16 空列表原样返回", app_main.trim_messages([]) == [])
# 单条就超限时也要保留(否则请求没意义)
huge = [app_main.Message(role="user", content="Y" * (app_main.MAX_CONTEXT_CHARS * 2))]
check("17 单条超限也保留", len(app_main.trim_messages(huge)) == 1)
# 关掉上限时不裁剪
saved = app_main.MAX_CONTEXT_CHARS
try:
app_main.MAX_CONTEXT_CHARS = 0
check("18 上限为 0 时不裁剪", len(app_main.trim_messages([system, *history])) == 101)
finally:
app_main.MAX_CONTEXT_CHARS = saved
# ---------------------------------------------------------------- 接线:prepare_conversation 会裁剪
saved = app_main.MAX_CONTEXT_CHARS
try:
app_main.MAX_CONTEXT_CHARS = 2000
conversation, _ = app_main.prepare_conversation([system, *history])
check("19 prepare_conversation 应用了裁剪", len(conversation) < 100000, str(len(conversation)))
check("20 系统指令仍在最前面", conversation.startswith("System: "), repr(conversation[:30]))
finally:
app_main.MAX_CONTEXT_CHARS = saved
print(f"\n通过 {len(PASS)} / {len(PASS) + len(FAIL)}")
if FAIL:
print("失败项:")
for name in FAIL:
print(" -", name)
return 1 if FAIL else 0
if __name__ == "__main__":
sys.exit(main())