Compare commits

...

2 Commits

Author SHA1 Message Date
60631a7ad1 Merge pull request 'fix: persistent event loop in CLI interview — no more Event loop is closed' (#42) from fix/cli-event-loop into main
All checks were successful
Tests / lint (push) Successful in 3s
Tests / test (push) Successful in 33s
2026-03-14 12:58:46 -04:00
b222b28856 fix: use persistent event loop in interview command
All checks were successful
Tests / lint (pull_request) Successful in 3s
Tests / test (pull_request) Successful in 32s
Replace repeated asyncio.run() calls with a single event loop that
persists across all interview questions. The old approach created and
destroyed loops per question, orphaning MCP stdio transports and
causing 'Event loop is closed' errors on ~50% of questions.

Also adds clean shutdown: closes MCP sessions before closing the loop.

Ref #36
2026-03-14 12:58:11 -04:00

View File

@@ -179,28 +179,44 @@ def interview(
typer.echo("Initializing Timmy for interview...\n")
# Force agent creation by calling chat once with a warm-up prompt
# Use a single persistent event loop for the entire interview.
# Calling asyncio.run() per question kills the loop between calls,
# orphaning MCP stdio transports and causing "Event loop is closed."
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
asyncio.run(
chat("Hello, Timmy. We're about to start your interview.", session_id="interview")
# Force agent creation by calling chat once with a warm-up prompt
try:
loop.run_until_complete(
chat("Hello, Timmy. We're about to start your interview.", session_id="interview")
)
except Exception as exc:
typer.echo(f"Warning: Initialization issue — {exc}", err=True)
def _on_answer(entry: InterviewEntry) -> None:
typer.echo(f"[{entry.category}]")
typer.echo(f" Q: {entry.question}")
typer.echo(f" A: {entry.answer}")
typer.echo()
typer.echo("Starting interview...\n")
transcript = run_interview(
chat_fn=lambda msg: loop.run_until_complete(chat(msg, session_id="interview")),
on_answer=_on_answer,
)
except Exception as exc:
typer.echo(f"Warning: Initialization issue — {exc}", err=True)
def _on_answer(entry: InterviewEntry) -> None:
typer.echo(f"[{entry.category}]")
typer.echo(f" Q: {entry.question}")
typer.echo(f" A: {entry.answer}")
typer.echo()
# Print full transcript at the end
typer.echo("\n" + format_transcript(transcript))
finally:
# Clean shutdown: close MCP sessions, then the loop
try:
from timmy.mcp_tools import close_mcp_sessions
typer.echo("Starting interview...\n")
transcript = run_interview(
chat_fn=lambda msg: asyncio.run(chat(msg, session_id="interview")),
on_answer=_on_answer,
)
# Print full transcript at the end
typer.echo("\n" + format_transcript(transcript))
loop.run_until_complete(close_mcp_sessions())
except Exception:
pass
loop.close()
@app.command()