Skip to content

Commit b81e4e2

Browse files
author
Chojan Shang
committed
fix: polish code and handle some case
Signed-off-by: Chojan Shang <chojan.shang@vesoft.com>
1 parent 7838211 commit b81e4e2

File tree

4 files changed

+36
-5
lines changed

4 files changed

+36
-5
lines changed

examples/mini_swe_agent/client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ async def _run_connection(self) -> None:
416416
return
417417

418418
# Autostep loop: take queued prompts and send; if none and mode != human, keep stepping
419-
while True:
419+
while self.agent_state != "STOPPED":
420420
blocks: list[ContentBlock1]
421421
try:
422422
blocks = self._outbox.get_nowait()
@@ -450,6 +450,11 @@ def _ask_new():
450450

451451
threading.Thread(target=_ask_new, daemon=True).start()
452452
except Exception as e:
453+
# Break on connection shutdowns to stop background thread cleanly
454+
msg = str(e)
455+
if isinstance(e, (BrokenPipeError, ConnectionResetError)) or "Broken pipe" in msg or "closed" in msg:
456+
self.agent_state = "STOPPED"
457+
break
453458
self.call_from_thread(lambda: self.enqueue_message(UIMessage("assistant", f"prompt error: {e}")))
454459
# Tiny delay to avoid busy-looping
455460
await asyncio.sleep(0.05)

examples/mini_swe_agent/duet.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ async def main() -> None:
1616
try:
1717
from dotenv import load_dotenv # type: ignore
1818

19-
load_dotenv(dotenv_path=str(root.parents[2] / ".env"), override=True)
19+
# Load .env from repo root: examples/mini_swe_agent -> examples -> REPO
20+
load_dotenv(dotenv_path=str(root.parents[1] / ".env"), override=True)
2021
except Exception:
2122
pass
2223

@@ -60,8 +61,30 @@ async def main() -> None:
6061
with contextlib.suppress(OSError):
6162
os.close(fd)
6263

63-
# Wait for processes to finish; no relay needed
64-
await asyncio.gather(agent.wait(), client.wait())
64+
# If either process exits, terminate the other gracefully
65+
agent_task = asyncio.create_task(agent.wait())
66+
client_task = asyncio.create_task(client.wait())
67+
done, pending = await asyncio.wait({agent_task, client_task}, return_when=asyncio.FIRST_COMPLETED)
68+
69+
# Terminate the peer process
70+
if agent_task in done and client.returncode is None:
71+
with contextlib.suppress(ProcessLookupError):
72+
client.terminate()
73+
if client_task in done and agent.returncode is None:
74+
with contextlib.suppress(ProcessLookupError):
75+
agent.terminate()
76+
77+
# Wait a bit, then kill if still running
78+
try:
79+
await asyncio.wait_for(asyncio.gather(agent.wait(), client.wait()), timeout=3)
80+
except asyncio.TimeoutError:
81+
with contextlib.suppress(ProcessLookupError):
82+
if agent.returncode is None:
83+
agent.kill()
84+
with contextlib.suppress(ProcessLookupError):
85+
if client.returncode is None:
86+
client.kill()
87+
await asyncio.gather(agent.wait(), client.wait())
6588

6689

6790
if __name__ == "__main__":

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dev = [
4040
"mkdocs-material>=8.5.10",
4141
"mkdocstrings[python]>=0.26.1",
4242
"mini-swe-agent>=1.10.0",
43+
"python-dotenv>=1.1.1",
4344
]
4445

4546
[build-system]

uv.lock

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)