feat(config): align LLM timeout/retry SLA with FastGPT client (60s/3)

Replaces the unequal SLA (LLM 30s/2 retries vs FastGPT client 60s/3)
that made the LangGraph backend look less stable under gray release.

- core/fastgpt_client: FASTGPT_TIMEOUT_SECONDS / FASTGPT_MAX_RETRIES
  module constants (were inline literals in lifespan)
- agent/llm: OpenAILLMService defaults 60.0s / 3 retries
- core/config: named-profile fallback defaults 60/3 (prefixed and
  JSON-dict forms)
- contract test test/backends/test_sla_alignment.py pins both sides to
  the same constants so the SLA cannot drift apart again
- retry backoff intentionally differs (FastGPT fixed 1s vs LLM
  exponential) and is documented; aligned parameters are per-call
  timeout and max retry count

Verified: 215 unit tests (3 new SLA contract tests)
This commit is contained in:
Eric Wang
2026-10-22 22:31:28 +08:00
parent ca73b1e8c5
commit 833725d8a3
5 changed files with 60 additions and 8 deletions
+2 -2
View File
@@ -100,8 +100,8 @@ class OpenAILLMService:
model: str,
api_key: str,
base_url: str = "",
timeout_seconds: float = 30.0,
max_retries: int = 2,
timeout_seconds: float = 60.0,
max_retries: int = 3,
temperature: float | None = None,
extra_body: dict | None = None,
):
+4 -4
View File
@@ -91,8 +91,8 @@ class Config:
"base_url": base_url,
"api_key": api_key,
"model": model,
"timeout_seconds": float(spec.get("timeout_seconds", 30)),
"max_retries": int(spec.get("max_retries", 2)),
"timeout_seconds": float(spec.get("timeout_seconds", 60)),
"max_retries": int(spec.get("max_retries", 3)),
"temperature": float(temperature) if temperature is not None else None,
"extra_body": str(extra_body),
}
@@ -113,8 +113,8 @@ class Config:
"base_url": base_url,
"api_key": api_key,
"model": model,
"timeout_seconds": float(os.getenv(prefix + "TIMEOUT_SECONDS", "30")),
"max_retries": int(os.getenv(prefix + "MAX_RETRIES", "2")),
"timeout_seconds": float(os.getenv(prefix + "TIMEOUT_SECONDS", "60")),
"max_retries": int(os.getenv(prefix + "MAX_RETRIES", "3")),
"temperature": float(temperature) if temperature else None,
"extra_body": os.getenv(prefix + "EXTRA_BODY", ""),
}
+10 -2
View File
@@ -16,6 +16,14 @@ _chat_backend: ChatBackend | None = None
_langgraph_graph = None
_app_service: AccidentAppService | None = None
# 后端超时/重试 SLA(两后端对齐;契约测试防止再漂移)。
# FastGPT 侧:客户端每次请求总超时 + 重试次数;
# LangGraph 侧:LLM 每次调用总超时 + 重试次数(退避为指数式,
# 与 FastGPT 固定 1s 不同——总尝试次数与单次超时可比,见
# test/backends/test_sla_alignment.py)。
FASTGPT_TIMEOUT_SECONDS = 60.0
FASTGPT_MAX_RETRIES = 3
@asynccontextmanager
async def lifespan(app: FastAPI):
@@ -39,8 +47,8 @@ async def lifespan(app: FastAPI):
_fastgpt_client = AsyncChatClient(
api_key=Config.FASTGPT_API_KEY,
base_url=Config.FASTGPT_BASE_URL,
timeout=60.0,
max_retries=3,
timeout=FASTGPT_TIMEOUT_SECONDS,
max_retries=FASTGPT_MAX_RETRIES,
retry_delay=1.0,
enable_logging=Config.DEBUG,
)