Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3bd50d2
Model preparation: repeat a part read that fails in transit
andersalm Oct 9, 2026
78b51d9
Generalise model producer, add Qwen2.5 1.5B fixture, explain catalogu…
andersalm Oct 9, 2026
8e0c13b
Model preparation: repeat only part reads that failed in transit
andersalm Oct 9, 2026
73eb438
Model preparation: continue a stopped download from the kept stage
andersalm Oct 9, 2026
b923e2a
Harden model fixtures, check catalogue payloads with the signer, narr…
andersalm Oct 9, 2026
ec98c1f
Bound local model memory: free-memory admission, idle release, one en…
andersalm Oct 9, 2026
c798c7c
Merge la/piece-retry into la/model-resume
andersalm Oct 9, 2026
ef5c7db
Carrier: a read is in transit only if every attempt failed in transit
andersalm Oct 9, 2026
a781ebb
Merge branch 'la/piece-retry' into la/model-resume
andersalm Oct 9, 2026
0c4d34f
Conservative free memory: macOS excludes anonymous inactive pages, Li…
andersalm Oct 9, 2026
9afd85a
macOS admission: under normal memory pressure, admit when physical RA…
andersalm Oct 9, 2026
dd684cd
macOS admission: normal-pressure fallback only when need is at most 4…
andersalm Oct 9, 2026
223be87
Model preparation: charge, discard and expire a kept stage
andersalm Oct 9, 2026
c0a63a7
Model preparation: a capacity start takes the kept stage over
andersalm Oct 9, 2026
909293d
Qwen fixture: say it needs about 3 GB of free memory
andersalm Oct 9, 2026
aca1312
Model preparation: finished attempts no longer fill the record cap
andersalm Oct 9, 2026
c6182e6
Merge branch 'la/qwen-catalog' into train/alpha13
andersalm Oct 9, 2026
c5c75fb
Pin signed model catalogue 3: SmolLM2-135M + Qwen2.5-1.5B-Instruct (#84)
andersalm Oct 9, 2026
a03728a
Model preparation: prune only attempts finished over an hour ago
andersalm Oct 9, 2026
244ad68
Model preparation: a refused Use keeps an overdue reservation's expiry
andersalm Oct 9, 2026
3f965b9
Model preparation: save expiry before replay validation too
andersalm Oct 9, 2026
73bf43a
Merge branch 'la/model-resume' into train/alpha13; alpha.13 notes
andersalm Oct 9, 2026
2f192e9
docs: local engine admission, one engine and idle release (#98)
andersalm Oct 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions capsules/_shared/model-management.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
});
const failureText = p => FAILURE_TEXT[p.failure_class] || "Preparation failed.";
const DISPATCH_UNAVAILABLE_TEXT = Object.freeze({
unsupported_host: "Keep this model on this device. To run it, use a supported device.",
unsupported_host: "Local AI is not supported on this device.",
source_engine_required: "This source Home needs its local model engine. Install the engine through source setup, then Retry.",
catalog_updated: "Model list updated. Press Use to keep using this model.",
});
// One phase per Runtime projection. The full view (System) and the compact
// Marketplace detail read their copy from the same phase.
Expand Down Expand Up @@ -331,7 +332,9 @@
progress.setAttribute("aria-label", "Preparation progress"); row.append(progress, element("p", `${formatBytes(p.completed_bytes)} of ${formatBytes(p.total_bytes)} prepared`));
}
const controls = element("div", "", "model-controls");
const use = button((p && p.state !== "reclaimed") || unresolvedUse ? "Retry" : compact ? "Get" : "Download", () => void act("use"), active(p) || reconcileRequired);
const useLabel = r.dispatch_unavailable_reason === "catalog_updated" ? "Use"
: (p && p.state !== "reclaimed") || unresolvedUse ? "Retry" : compact ? "Get" : "Download";
const use = button(useLabel, () => void act("use"), active(p) || reconcileRequired);
use.dataset.modelControl = "use";
if (!active(p) && !r.dispatch_ready) controls.append(use);
if (r.dispatch_ready && typeof onReadyOpen === "function") {
Expand Down
9 changes: 8 additions & 1 deletion capsules/marketplace/browser/marketplace.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ import { sharedModelOffers, modelAccessOpportunities, REMOTE_MODEL_ID, SERVICE_O
name: publicTitle(capsule),
developer: String(model ? capsule.publisher_did : capsule.author || "").trim(),
category: appCategory(capsule, role),
description: model ? "Language model for Assistant." : publicDescription(capsule),
description: model ? modelDescription(capsule) : publicDescription(capsule),
version: String(capsule.version || ""),
installed,
launchable,
Expand All @@ -465,6 +465,13 @@ import { sharedModelOffers, modelAccessOpportunities, REMOTE_MODEL_ID, SERVICE_O
};
}

// A signed catalogue entry may carry its own one-line description; without
// one, Runtime's generic capsule text is replaced by the model's purpose.
function modelDescription(capsule) {
const description = String(capsule.description || "").trim();
return description && !/\bruntime\b/i.test(description) ? description : "Language model for Assistant.";
}

function appCategory(capsule, role) {
if (capsule.source === "signed-model-catalog") return "models";
const category = String(capsule.category || "").toLowerCase();
Expand Down
7 changes: 5 additions & 2 deletions capsules/marketplace/browser/model-management.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
});
const failureText = p => FAILURE_TEXT[p.failure_class] || "Preparation failed.";
const DISPATCH_UNAVAILABLE_TEXT = Object.freeze({
unsupported_host: "Keep this model on this device. To run it, use a supported device.",
unsupported_host: "Local AI is not supported on this device.",
source_engine_required: "This source Home needs its local model engine. Install the engine through source setup, then Retry.",
catalog_updated: "Model list updated. Press Use to keep using this model.",
});
// One phase per Runtime projection. The full view (System) and the compact
// Marketplace detail read their copy from the same phase.
Expand Down Expand Up @@ -331,7 +332,9 @@
progress.setAttribute("aria-label", "Preparation progress"); row.append(progress, element("p", `${formatBytes(p.completed_bytes)} of ${formatBytes(p.total_bytes)} prepared`));
}
const controls = element("div", "", "model-controls");
const use = button((p && p.state !== "reclaimed") || unresolvedUse ? "Retry" : compact ? "Get" : "Download", () => void act("use"), active(p) || reconcileRequired);
const useLabel = r.dispatch_unavailable_reason === "catalog_updated" ? "Use"
: (p && p.state !== "reclaimed") || unresolvedUse ? "Retry" : compact ? "Get" : "Download";
const use = button(useLabel, () => void act("use"), active(p) || reconcileRequired);
use.dataset.modelControl = "use";
if (!active(p) && !r.dispatch_ready) controls.append(use);
if (r.dispatch_ready && typeof onReadyOpen === "function") {
Expand Down
40 changes: 40 additions & 0 deletions capsules/model-provider/src/adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,9 @@ async fn run_local_text_worker_with_timing(
)
.then(HostedBackendReport::default);
let mut local_socket = None;
// Holds the local execution slot until this run ends; dropping it arms
// the engine's idle release.
let mut _local_run = None;
let (api_url, api_key, body, private_endpoint, context_window_tokens) = match &task.backend {
LocalTextBackend::OpenRouterDecisions {
api_url,
Expand Down Expand Up @@ -1816,6 +1819,19 @@ async fn run_local_text_worker_with_timing(
settings,
requested_output_tokens,
} => {
let run = tokio::select! {
biased;
_ = task.cancel_rx.wait_for(|cancelled| *cancelled) => {
return Ok(queued_local_cancelled());
}
run = tokio::time::timeout(
remaining_run_timeout(task.deadline_ms)?,
engines.begin_run(),
) => run
.map_err(|_| map_local_llama_fault(LocalLlamaFault::Timeout))?
.map_err(map_local_llama_fault)?,
};
_local_run = Some(run);
let endpoint = engines
.endpoint_with_timeout(
offer_id,
Expand Down Expand Up @@ -2205,6 +2221,16 @@ async fn run_decision_worker(

fn map_local_llama_fault(fault: LocalLlamaFault) -> AdapterFault {
match fault {
LocalLlamaFault::MemoryUnavailable => AdapterFault {
error: RunError {
class: ErrorClass::ContextRejected,
code: "model_memory_unavailable".into(),
message:
"Not enough free memory for this model. Close other apps or choose a smaller model."
.into(),
},
detail: None,
},
LocalLlamaFault::Timeout => AdapterFault::timeout(
"model backend timed out",
"local llama engine health deadline expired",
Expand All @@ -2216,6 +2242,20 @@ fn map_local_llama_fault(fault: LocalLlamaFault) -> AdapterFault {
}
}

fn queued_local_cancelled() -> ReconcileResult {
ReconcileResult::Terminal {
events: Vec::new(),
status: RunStatus::Cancelled,
output: None,
error: Some(RunError {
class: ErrorClass::Cancelled,
code: "cancelled".into(),
message: "Model run was cancelled before execution.".into(),
}),
backend_report: None,
}
}

fn text_generation_request_body(
offer: &ConfiguredOffer,
model: &str,
Expand Down
1 change: 1 addition & 0 deletions capsules/model-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod contract;
mod execution;
mod journal;
mod local_llama;
mod local_memory;
mod process;
mod state;
#[cfg(test)]
Expand Down
Loading
Loading