Skip to content

feat(api): wire LlmClient injection seam; stop stderr writes; prune dead Planner trait#68

Merged
ZhiXiao-Lin merged 2 commits into
mainfrom
feat/framework-boundary-llm-client-seam
Jun 14, 2026
Merged

feat(api): wire LlmClient injection seam; stop stderr writes; prune dead Planner trait#68
ZhiXiao-Lin merged 2 commits into
mainfrom
feat/framework-boundary-llm-client-seam

Conversation

@ZhiXiao-Lin

Copy link
Copy Markdown
Contributor

Framework-vs-host boundary tightening from an architecture audit of core. Independent of #67 (prompt hardening) — touches a disjoint set of files.

The audit's guiding rule: the framework owns mechanism/protocol; the host owns presentation/input-syntax/business-policy. A built-in should be a replaceable extension point only when a concrete present consumer needs it (no future-proofing).

Added — SessionOptions::with_llm_client(Arc<dyn LlmClient>)

The one real missing seam. LlmClient is already a public trait and the whole engine runs on Arc<dyn LlmClient>, but no public path let a host inject one — every session resolved via a hardcoded provider-string factory, and the only injecting constructor (build_session) is #[cfg(test)]. This violated the repo's own "Typed Extension Options" rule (backends must be swappable as typed objects, not selected by a raw provider name) — and it was the only Action-layer backend that did, while workspace/memory/store/security are all object-injectable.

  • resolve_session_llm_client now returns a host-supplied client when present, else the existing provider/model factory (unchanged default).
  • Unblocks: providers the factory doesn't cover, a deterministic record/replay client (mirroring FixedClock/SequentialIdGenerator), and proxy/audit HTTP wrappers — without forking core.

Fixed — framework no longer writes to the host's stderr

Replaced a stray eprintln!("[DEBUG] HTTP error...") in the OpenAI client (fired on every transport error, dumping [DEBUG] to every consumer's terminal) with tracing::error!, consistent with the rest of the crate.

Removed — dead Planner trait (planning::Planner)

Re-exported but had zero dyn Planner dispatch and no consumer; every call site uses LlmPlanner's inherent methods directly. Pruned per the pruning rule — the only real variability (the LLM) is now swappable via with_llm_client. LlmPlanner itself is unchanged.

Testing

  • 2 new resolver unit tests: a host-supplied client bypasses provider/model config; the control case (no client + no default_model) errors.
  • cargo test -p a3s-code-core --lib: 1757 passed, 0 failed. clippy clean. cargo fmt --check clean.

Notes

No version bump (left to the maintainer's release flow). Slash-command de-UX (the larger boundary item from the same audit) is intentionally not included — it changes a public behavior surface and needs a product decision first.

claude added 2 commits June 14, 2026 16:24
…ead Planner trait

Framework-vs-host boundary tightening from the architecture audit.

Added — SessionOptions::with_llm_client(Arc<dyn LlmClient>):
- The LlmClient trait + Arc<dyn> engine already existed but were injectable
  only in #[cfg(test)] code (build_session); every public session path resolved
  via a hardcoded provider-string factory. This adds the public seam and has
  resolve_session_llm_client prefer a host-supplied client, bringing the
  Action-layer backend to parity with workspace/memory/store/security (all
  object-injectable). The provider/model factory stays the default when unset.
  Enables custom/unsupported providers, record/replay clients, and proxy/audit
  wrappers without forking core.

Fixed — framework no longer writes to the host's stderr:
- Replaced a stray eprintln!("[DEBUG] HTTP error...") in the OpenAI client
  (fired on every transport error) with tracing::error!, matching the rest of
  the crate.

Removed — dead Planner trait (planning::Planner):
- Re-exported but had zero dyn-dispatch and no consumer; all call sites use
  LlmPlanner's inherent methods. Pruned per Rule 9 (the only real variability,
  the LLM, is now swappable via with_llm_client). LlmPlanner unchanged.

Tests: 2 new resolver tests (override bypasses config; control case errors).
cargo test -p a3s-code-core --lib: 1757 passed, 0 failed. clippy clean.
…ry-llm-client-seam

# Conflicts:
#	CHANGELOG.md
@ZhiXiao-Lin ZhiXiao-Lin merged commit 74c7fc0 into main Jun 14, 2026
1 check passed
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.

2 participants