Skip to content

Add CLI providers runtime — Rust mirror of npcpy#2

Open
svax974 wants to merge 1 commit into
NPC-Worldwide:mainfrom
svax974:feat/cli-providers
Open

Add CLI providers runtime — Rust mirror of npcpy#2
svax974 wants to merge 1 commit into
NPC-Worldwide:mainfrom
svax974:feat/cli-providers

Conversation

@svax974

@svax974 svax974 commented May 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Rust mirror of NPC-Worldwide/npcpy#235. Routes CLI agent tools (claude_code, opencode, codex, kimi_code, kilo, gemini, amp, aider) through the Rust REPL and serve binaries identically to the Python core. Cross-language parity is required so the Rust shell behaves the same as the Python one when an NPC is bound to a CLI provider.

Depends on: npcpy#235 — same set of providers, same JSONL formats, same session-id semantics. Should land after npcpy#235.

What's in this PR

  • run_cli_provider in src/llm_funcs.rstokio::process::Command + BufReader::lines() live streaming, Arc<AtomicBool>/Mutex spinner coordination, \x1b7/\x1b8\x1b[J post-stream cursor restore. Same API parity with the litellm path as the Python implementation.
  • Parser structs (ClaudeStreamParser, OpencodeStreamParser, CodexStreamParser, KimiStreamParser) implementing the same feed/finalize pattern as the Python parser hierarchy. Wrapped in enum CliStreamParser { Claude, Opencode, Codex, Kimi } with zero-cost dispatch (no Box<dyn Trait>); for_provider() constructs the right variant from the provider string.
  • ParserResult { text, usage, cost, parsed_session_id } — uniform output type across all parsers.
  • resolve_session_id() — single match consolidating the two parallel per-provider chains that previously existed.
  • build_cli_cmd, wrap_with_system, fetch_*_session_id helpers mirroring the Python signatures.
  • parse_claude_output, parse_opencode_output, parse_codex_output — one-shot wrappers around the parsers (backward compatible).
  • md5_hex shells out to python3 -c hashlib... to avoid pulling in an MD5 crate.

Verification

  • cargo build clean: 0 errors, 0 warnings.
  • File length: 2553 → 2732 lines.
  • src/npc_compiler.rs and src/serve/mod.rs only forward two new None args to get_llm_response_ext call sites (internal-only signature update, no behaviour change for non-CLI providers).

Test plan

  • Rust REPL: bind an NPC to provider: opencode, send a prompt — response streams live, session ID is captured and reused on the next turn
  • Rust REPL: provider: claude_code with pre-assigned UUID — multi-turn continuity
  • cargo build && cargo clippy remain clean
  • No regression on existing litellm-only call sites (signatures backward-compatible at all call sites)

🤖 Generated with Claude Code

Mirrors npcpy/llm_funcs.py CLI provider support so the Rust REPL and
serve binaries route CLI agent tools (claude_code, opencode, codex,
kimi_code, kilo, gemini, amp, aider) identically to the Python core.
Cross-language parity is required by the project rule (see project
memory: Rust/Python CLI provider sync).

src/llm_funcs.rs:
- run_cli_provider: tokio::process::Command + BufReader::lines() live
  streaming, Arc<AtomicBool>/Mutex spinner coordination, \x1b7/\x1b8\x1b[J
  post-stream cursor restore. Same API parity with the litellm path as
  the Python implementation
- ClaudeStreamParser, OpencodeStreamParser, CodexStreamParser,
  KimiStreamParser: structs implementing the same feed/finalize pattern
  as the Python parser hierarchy. Wrapped in
  `enum CliStreamParser { Claude, Opencode, Codex, Kimi }` with zero-cost
  dispatch (no Box<dyn Trait>); for_provider() constructs the right
  variant from the provider string
- ParserResult { text, usage, cost, parsed_session_id }: uniform output
  type across all parsers
- resolve_session_id(): single match consolidating the two parallel
  per-provider chains that previously existed
- parse_claude_output, parse_opencode_output, parse_codex_output:
  one-shot wrappers around the parsers (backward compatible)
- build_cli_cmd, wrap_with_system, fetch_*_session_id helpers
- md5_hex shells out to python3 to avoid pulling in an MD5 crate
- LlmResponseResult.session_id: Option<String>

src/npc_compiler.rs, src/serve/mod.rs:
- Forward two new None args to get_llm_response_ext call sites
  (session_id + a CLI streaming flag) — internal-only signature update

cargo build clean: 0 errors, 0 warnings. File length 2553 -> 2732 lines.
cagostino added a commit to NPC-Worldwide/npcsh that referenced this pull request May 11, 2026
- Remove unused _is_cli_provider import (F401)
- Fix f-string without placeholders in ui.py (F541)
- Update test_jinxes.py to look in lib/search/ for search jinxes
- Comment out Rust CLI provider intercept block until npcrs publishes
  CLI_PROVIDERS + run_cli_provider (depends on NPC-Worldwide/npcrs#2)
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