fix(inference): chat 每次调用新建 httpx client,修复同步门禁 Event loop is closed

This commit is contained in:
lhl
2026-08-14 00:09:29 +08:00
parent 752fd2df6d
commit 4da5f7087d
2 changed files with 43 additions and 37 deletions
+1
View File
@@ -107,3 +107,4 @@
| 2026-08-13 23:11 | Agent 实现 | 接通真实 LLM 引擎工厂(P5-T10 门禁接线):新增 inference/factory.pyorchestrator/qa_loop 接入,脚本校验适配,补工厂测试 | src/genesis/inference/factory.py; src/genesis/writer/orchestrator.py; src/genesis/qa/qa_loop.py; scripts/run_phase5_slice.py; tests/test_inference_factory.py | hy3-free | | 2026-08-13 23:11 | Agent 实现 | 接通真实 LLM 引擎工厂(P5-T10 门禁接线):新增 inference/factory.pyorchestrator/qa_loop 接入,脚本校验适配,补工厂测试 | src/genesis/inference/factory.py; src/genesis/writer/orchestrator.py; src/genesis/qa/qa_loop.py; scripts/run_phase5_slice.py; tests/test_inference_factory.py | hy3-free |
| 2026-08-13 23:20 | 测试验证 | P5-T10 引擎工厂接线:补 engine=None 委托工厂测试(orchestrator/qa_loop),全量 296 passed / 99.04% | tests/test_phase5_writer_orchestrator.py; tests/test_phase5_qa_loop.py | hy3-free | | 2026-08-13 23:20 | 测试验证 | P5-T10 引擎工厂接线:补 engine=None 委托工厂测试(orchestrator/qa_loop),全量 296 passed / 99.04% | tests/test_phase5_writer_orchestrator.py; tests/test_phase5_qa_loop.py | hy3-free |
| 2026-08-13 23:35 | 测试验证 | P5-T10 门禁诊断:修复 WriterGenerationError 吞掉底层 LLM 错误(如 401 详情),补透传测试 | src/genesis/writer/writer_agent.py; tests/test_phase5_writer_agent.py | hy3-free | | 2026-08-13 23:35 | 测试验证 | P5-T10 门禁诊断:修复 WriterGenerationError 吞掉底层 LLM 错误(如 401 详情),补透传测试 | src/genesis/writer/writer_agent.py; tests/test_phase5_writer_agent.py | hy3-free |
| 2026-08-13 23:50 | Agent 实现 | 修复 HttpLLMClient 在同步门禁中多次 asyncio.run 复用已关闭事件循环致 Event loop is closedchat 改为每次调用新建 client(保留 async with 协议)| src/genesis/inference/client.py | hy3-free |
+6 -1
View File
@@ -50,6 +50,7 @@ class HttpLLMClient:
self._api_key = api_key self._api_key = api_key
self._timeout_sec = timeout_sec self._timeout_sec = timeout_sec
self._retry_backoff = retry_backoff self._retry_backoff = retry_backoff
self._transport = transport
self._client = httpx.AsyncClient(timeout=timeout_sec, transport=transport) self._client = httpx.AsyncClient(timeout=timeout_sec, transport=transport)
async def __aenter__(self) -> HttpLLMClient: async def __aenter__(self) -> HttpLLMClient:
@@ -79,13 +80,17 @@ class HttpLLMClient:
"Content-Type": "application/json", "Content-Type": "application/json",
} }
# 每次调用新建 httpx.AsyncClient,绑定到当前事件循环。
# 兼容「同步门禁中多次 asyncio.run 驱动 async chat_structured」场景,
# 避免复用 __init__ 中创建的 client 因首个循环关闭而报 Event loop is closed。
async with httpx.AsyncClient(timeout=self._timeout_sec, transport=self._transport) as client:
attempts = 1 + len(self._retry_backoff) attempts = 1 + len(self._retry_backoff)
last_error: Exception | None = None last_error: Exception | None = None
for attempt in range(attempts): for attempt in range(attempts):
if attempt > 0: if attempt > 0:
await asyncio.sleep(self._retry_backoff[attempt - 1]) await asyncio.sleep(self._retry_backoff[attempt - 1])
try: try:
resp = await self._client.post(url, json=payload, headers=headers) resp = await client.post(url, json=payload, headers=headers)
except httpx.TimeoutException as exc: except httpx.TimeoutException as exc:
last_error = exc last_error = exc
continue continue