Skip to main content

LocalAdapter

Struct LocalAdapter 

Source
pub struct LocalAdapter { /* private fields */ }
Expand description

A crate::ModelAdapter that runs a GGUF Qwen model entirely on this machine’s CPU via kopitiam-runtime — no network, no cloud account, no API key. This is what makes CLAUDE.md’s Offline First pipeline (“existing knowledge, then native Rust, then local AI, then cloud AI as the final fallback”) a real, callable rung rather than a promise: before this type existed, “local AI” had nothing behind it but crate::EchoAdapter, which invokes no model at all.

Construct via LocalAdapter::load; see this module’s docs for why depending on kopitiam-runtime here does not violate the Semantic Runtime’s dependency rule.

Implementations§

Source§

impl LocalAdapter

Source

pub fn gpu_output_head_active(&self) -> bool

Whether the output projection is running on the GPU for this model.

Exposed so the CLI can tell the user which path it actually took. The notice used to say “on CPU” unconditionally, which stopped being true the moment the output head could be offloaded — and a status line that is confidently wrong is worse than no status line.

Source

pub fn load(path: impl AsRef<Path>) -> Result<Self>

Loads a GGUF Qwen model and its embedded tokenizer from path, once, and holds both for the lifetime of the returned adapter.

§Errors

Returns Err — never panics — if path does not exist, is not a valid GGUF/SafeTensors file, is missing metadata kopitiam_runtime::QwenConfig::from_metadata requires, or has no embedded tokenizer.ggml.tokens vocabulary (tokenizer_from_gguf’s own error cases). This is deliberate, not incidental: per crate::ModelAdapter::complete’s own docs, a failing adapter is how kopitiam-workflow falls back to the next rung of the Offline First pipeline, so every failure mode here has to surface as Err rather than a panic that would take the whole workflow process down with it.

Trait Implementations§

Source§

impl ModelAdapter for LocalAdapter

Source§

fn stream(&self, request: &CompletionRequest) -> Receiver<StreamChunk>

Streams the model’s reply token by token off a background thread, so a chat UI renders each token as kopitiam-runtime decodes it instead of freezing for the whole generation — the non-negotiable shape on a phone doing a few tokens per second (temp_ai_design.md §10.4).

§How it stays off the foreground thread

The heavy state — model weights and tokenizer — lives behind Arcs (see LocalAdapter::model), so this clones the two Arcs (cheap: two refcount bumps, no weight copy) and moves them into a spawned std thread. That thread owns the channel’s Sender and runs [generate], whose on_token callback fires once per decoded token; each fire sends a StreamChunk::Token. On a clean finish it sends StreamChunk::Done; if generate errors it sends StreamChunk::Error instead. This is the AID-0028 actor discipline (worker owns the resource, streams over a channel, never blocks the caller) — no async runtime, just a thread and an mpsc.

Moving the Arcs (rather than borrowing self) is what lets the thread outlive this call safely: it holds its own strong references, so the weights stay alive for exactly as long as generation needs them even if the LocalAdapter itself is dropped meanwhile.

§If the caller stops listening

generate’s on_token can’t itself abort the loop, so if the receiver is dropped mid-reply the send starts failing but generation runs to its max_new_tokens/EOS on the background thread before the thread exits. Wasted compute, never a hang or a panic — acceptable for phase 1; a cancellation token is a later refinement.

Source§

fn name(&self) -> &str

Stable identifier for this adapter (e.g. "local-qwen", "claude"), used in logs and recorded provenance — not necessarily the same as CompletionResponse::model, which names the specific model that answered.
Source§

fn complete(&self, request: &CompletionRequest) -> Result<CompletionResponse>

Sends request to the backend and returns its reply. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,