Skip to content

Adopt felix 0.3.0 native async providers#32

Merged
jkbennitt merged 2 commits into
masterfrom
feat/felix-0.3.0-async
Jun 10, 2026
Merged

Adopt felix 0.3.0 native async providers#32
jkbennitt merged 2 commits into
masterfrom
feat/felix-0.3.0-async

Conversation

@jkbennitt

Copy link
Copy Markdown
Member

Summary

Follow-up to #31: actually adopt the 0.3.0 async provider interface instead of just bumping the pin.

  • ClaudeCodeProvider.acomplete() — true async subprocess via asyncio.create_subprocess_exec; timeout and cancellation now kill the CLI process instead of orphaning it. complete()/acomplete() share extracted _build_invocation/_parse_cli_output helpers.
  • RimWorldRoleAgent_acall_provider() (native acomplete() with a worker-thread fallback for providers that lack async), aprocess_task() (async mirror of the SDK pipeline — the SDK exposes async only at the provider layer, LLMAgent.process_task is still sync), adeliberate() + async parse-retry. No-think prefill, Weave wrapping, and token accounting all preserved on the async path.
  • Game loop — deliberation awaits adeliberate() directly; ROLE_TIMEOUT_S now cancels the in-flight request rather than leaving a hung thread running until the provider timeout (the old "Python threads can't be killed" caveat no longer applies).

Sync deliberate() is kept for tests and non-loop callers.

Verification

  • pytest: 458 passed (8 new async tests: adeliberate pipeline, no-async fallback, no-think prefill on async path, subprocess kill on timeout/cancellation)
  • ruff + mypy src/ (strict): clean
  • Smoke test: identical scores to the sync baseline, wall-clock 40.6s -> 27.8s

🤖 Generated with Claude Code

jkbennitt and others added 2 commits June 10, 2026 16:32
Deliberation now runs natively on the event loop via acomplete()
instead of asyncio.to_thread:

- ClaudeCodeProvider.acomplete(): true async subprocess
  (create_subprocess_exec); timeout and cancellation kill the CLI
  process instead of orphaning it. complete() and acomplete() share
  _build_invocation/_parse_cli_output.
- RimWorldRoleAgent: _acall_provider (native acomplete with
  worker-thread fallback for providers without async support),
  aprocess_task (async mirror of the SDK pipeline — the SDK exposes
  async only at the provider layer), adeliberate + async parse-retry.
- Game loop awaits adeliberate directly; a deliberation timeout now
  cancels the in-flight provider request rather than leaving a hung
  worker thread running until the provider timeout.

Sync deliberate() is kept for tests and non-loop callers. Smoke test
wall-clock dropped 40.6s -> 27.8s with identical scores.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI smoke test runs --dry-run, whose MagicMock provider only set
complete.return_value; the async deliberation path got a bare
AsyncMock coroutine back. Mirror complete.return_value on acomplete.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jkbennitt jkbennitt merged commit d0c9a62 into master Jun 10, 2026
3 checks passed
@jkbennitt jkbennitt deleted the feat/felix-0.3.0-async branch June 10, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant