feat(api): wire LlmClient injection seam; stop stderr writes; prune dead Planner trait#68
Merged
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
LlmClientis already a public trait and the whole engine runs onArc<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_clientnow returns a host-supplied client when present, else the existingprovider/modelfactory (unchanged default).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) withtracing::error!, consistent with the rest of the crate.Removed — dead
Plannertrait (planning::Planner)Re-exported but had zero
dyn Plannerdispatch and no consumer; every call site usesLlmPlanner's inherent methods directly. Pruned per the pruning rule — the only real variability (the LLM) is now swappable viawith_llm_client.LlmPlanneritself is unchanged.Testing
provider/modelconfig; the control case (no client + nodefault_model) errors.cargo test -p a3s-code-core --lib: 1757 passed, 0 failed.clippyclean.cargo fmt --checkclean.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.