mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix: create AsyncOpenAI lazily in trajectory_compressor to avoid closed event loop (#4013)
The AsyncOpenAI client was created once at __init__ and stored as an instance attribute. process_directory() calls asyncio.run() which creates and closes a fresh event loop. On a second call, the client's httpx transport is still bound to the closed loop, raising RuntimeError: "Event loop is closed" — the same pattern fixed by PR #3398 for the main agent loop. Create the client lazily in _get_async_client() so each asyncio.run() gets a client bound to the current loop. Co-authored-by: binhnt92 <binhnt.ht.92@gmail.com>
This commit is contained in:
@@ -375,15 +375,34 @@ class TrajectoryCompressor:
|
||||
raise RuntimeError(
|
||||
f"Missing API key. Set {self.config.api_key_env} "
|
||||
f"environment variable.")
|
||||
from openai import OpenAI, AsyncOpenAI
|
||||
from openai import OpenAI
|
||||
self.client = OpenAI(
|
||||
api_key=api_key, base_url=self.config.base_url)
|
||||
self.async_client = AsyncOpenAI(
|
||||
api_key=api_key, base_url=self.config.base_url)
|
||||
# AsyncOpenAI is created lazily in _get_async_client() so it
|
||||
# binds to the current event loop — avoids "Event loop is closed"
|
||||
# when process_directory() is called multiple times (each call
|
||||
# creates a new loop via asyncio.run()).
|
||||
self.async_client = None
|
||||
self._async_client_api_key = api_key
|
||||
|
||||
print(f"✅ Initialized summarizer client: {self.config.summarization_model}")
|
||||
print(f" Max concurrent requests: {self.config.max_concurrent_requests}")
|
||||
|
||||
def _get_async_client(self):
|
||||
"""Return an AsyncOpenAI client bound to the current event loop.
|
||||
|
||||
Created lazily so that each ``asyncio.run()`` call in
|
||||
``process_directory()`` gets a client tied to its own loop,
|
||||
avoiding "Event loop is closed" errors on repeated calls.
|
||||
"""
|
||||
from openai import AsyncOpenAI
|
||||
# Always create a fresh client so it binds to the running loop.
|
||||
self.async_client = AsyncOpenAI(
|
||||
api_key=self._async_client_api_key,
|
||||
base_url=self.config.base_url,
|
||||
)
|
||||
return self.async_client
|
||||
|
||||
def _detect_provider(self) -> str:
|
||||
"""Detect the provider name from the configured base_url."""
|
||||
url = (self.config.base_url or "").lower()
|
||||
@@ -615,7 +634,7 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
|
||||
max_tokens=self.config.summary_target_tokens * 2,
|
||||
)
|
||||
else:
|
||||
response = await self.async_client.chat.completions.create(
|
||||
response = await self._get_async_client().chat.completions.create(
|
||||
model=self.config.summarization_model,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
temperature=self.config.temperature,
|
||||
|
||||
Reference in New Issue
Block a user