51 lines
1.9 KiB
Python
51 lines
1.9 KiB
Python
"""Shared detection for adaptive context recovery (no assumed model window)."""
|
|
|
|
from collections.abc import Mapping
|
|
|
|
CONTEXT_RECOVERY_MESSAGE = "请补充本次需要处理的具体问题或关键材料,我会继续为你解答。"
|
|
|
|
_PATTERNS = (
|
|
"context_length_exceeded",
|
|
"maximum context length",
|
|
"max context length",
|
|
"context window exceeds limit",
|
|
"context window exceeded",
|
|
"exceeds the context window",
|
|
"max_model_len",
|
|
"prompt is too long",
|
|
"input is too long",
|
|
"too many tokens",
|
|
"input token count",
|
|
"prompt token count",
|
|
"exceeds the model's context",
|
|
"exceeds model context",
|
|
"上下文长度",
|
|
"最大上下文",
|
|
"输入 token 数超过",
|
|
"输入token数超过",
|
|
)
|
|
|
|
|
|
def is_context_length_error(exc: BaseException) -> bool:
|
|
"""Inspect provider error codes as well as text, without matching all 400s."""
|
|
values = [str(exc), str(getattr(exc, "code", "")), str(getattr(exc, "error_code", ""))]
|
|
# Compatible gateways may use numeric codes and put the only context-size
|
|
# signal in their message. Our adapter keeps that text private so __str__
|
|
# and framework logs cannot expose provider bodies; classify it in-process.
|
|
classification_detail = getattr(exc, "_classification_detail", None)
|
|
if isinstance(classification_detail, str):
|
|
values.append(classification_detail)
|
|
body = getattr(exc, "body", None)
|
|
if isinstance(body, Mapping):
|
|
error = body.get("error", body)
|
|
if isinstance(error, Mapping):
|
|
values.extend(str(error.get(key, "")) for key in ("code", "type", "message"))
|
|
return any(pattern in value.lower() for value in values for pattern in _PATTERNS)
|
|
|
|
|
|
class ContextRecoveryExhausted(RuntimeError):
|
|
"""Internal failure for contracts that cannot degrade to conversational text."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__("context_length_exceeded: adaptive recovery exhausted")
|