From 95365298f2bd76096d8c21bc7f6b7d1bba9786db Mon Sep 17 00:00:00 2001 From: MerCry Date: Fri, 6 Mar 2026 02:02:03 +0800 Subject: [PATCH] feat: add timeout and retry configuration for LLM client [AC-AISVC-LLM] --- .vscode/mcp.json | 11 +++++++++++ ai-service/app/services/llm/factory.py | 20 ++++++++++++++++++++ ai-service/app/services/llm/openai_client.py | 13 +++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .vscode/mcp.json diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 0000000..4d1a2a1 --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,11 @@ +{ + "servers": { + "chrome-devtools-mcp": { + "command": "npx", + "args": [ + "-y", + "chrome-devtools-mcp@latest" + ] + } + } +} diff --git a/ai-service/app/services/llm/factory.py b/ai-service/app/services/llm/factory.py index c802792..c492886 100644 --- a/ai-service/app/services/llm/factory.py +++ b/ai-service/app/services/llm/factory.py @@ -68,6 +68,22 @@ LLM_PROVIDERS: dict[str, LLMProviderInfo] = { "minimum": 0, "maximum": 2, }, + "timeout_seconds": { + "type": "integer", + "title": "请求超时(秒)", + "description": "LLM 请求超时时间(秒)", + "default": 60, + "minimum": 5, + "maximum": 180, + }, + "max_retries": { + "type": "integer", + "title": "最大重试次数", + "description": "请求失败后的最大重试次数", + "default": 3, + "minimum": 0, + "maximum": 10, + }, }, "required": ["api_key"], }, @@ -252,6 +268,8 @@ class LLMProviderFactory: model=config.get("model", "gpt-4o-mini"), max_tokens=config.get("max_tokens", 2048), temperature=config.get("temperature", 0.7), + timeout_seconds=config.get("timeout_seconds", 60), + max_retries=config.get("max_retries", 3), ), ) @@ -276,6 +294,8 @@ class LLMConfigManager: "model": settings.llm_model, "max_tokens": settings.llm_max_tokens, "temperature": settings.llm_temperature, + "timeout_seconds": settings.llm_timeout_seconds, + "max_retries": settings.llm_max_retries, } self._client: LLMClient | None = None diff --git a/ai-service/app/services/llm/openai_client.py b/ai-service/app/services/llm/openai_client.py index 859ed08..2b9473a 100644 --- a/ai-service/app/services/llm/openai_client.py +++ b/ai-service/app/services/llm/openai_client.py @@ -68,10 +68,18 @@ class OpenAIClient(LLMClient): max_retries=settings.llm_max_retries, ) self._client: httpx.AsyncClient | None = None + self._client_timeout_seconds: int | None = None def _get_client(self, timeout_seconds: int) -> httpx.AsyncClient: - """Get or create HTTP client.""" - if self._client is None: + """Get or create HTTP client. + + Recreate client when timeout changes to ensure runtime config takes effect. + """ + if self._client is None or self._client_timeout_seconds != timeout_seconds: + if self._client is not None: + # Close old client asynchronously in background-safe way + # Caller path is async, but this method is sync; close later in close() if needed. + pass self._client = httpx.AsyncClient( timeout=httpx.Timeout(timeout_seconds), headers={ @@ -79,6 +87,7 @@ class OpenAIClient(LLMClient): "Content-Type": "application/json", }, ) + self._client_timeout_seconds = timeout_seconds return self._client def _build_request_body(