use std::collections::{BTreeMap, BTreeSet, VecDeque};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::collection::{
remove_matching_values, sorted_ref_deltas, sorted_unique_strings, sorted_values,
};
use crate::lifecycle::util::{
asset_refcount_mismatch, asset_summary, bump_projector_index_revision as bump_revision,
classified_asset, decrement_asset_refcount, empty_asset_id, increment_asset_refcount,
increment_expected_asset_refcount, invalid_asset_field, invalid_pairing, invalid_source,
manifest_key_mismatch, media_marker_for_modality, missing_model_asset, model_missing_asset,
model_not_found, sha256_hex, sorted_model_asset_ids, validate_registry_manifest_version,
AssetSummary,
};
use crate::lifecycle::{
AssetInspection, BackendCapabilities, BackendPlan, BackendPolicy, BackendPreference,
BackendSelection, ClassifiedAsset, ModelAssetKind, ModelError, ModelLoadOptions, ModelModality,
ModelPairingReason, ModelPairingState as CoreModelPairingState, ModelSourceKind, ModelStatus,
PairingPlan, PairingResolver, StatsMode, REGISTRY_MANIFEST_VERSION,
};
use crate::runtime::config::NativeRuntimeConfig;
use crate::runtime::numeric::{
MILLIS_PER_SECOND, SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE,
};
const EXPLICIT_PROJECTOR_MISSING_ID: &str = "explicit projector did not produce a projector id";
const NO_PENDING_BROWSER_MODEL_LOAD: &str = "no pending browser model load";
const BROWSER_LOAD_COMMIT_MISMATCH: &str =
"browser model load commit does not match the pending load";
const BROWSER_REGISTRY_MANIFEST_LABEL: &str = "browser registry manifest";
const CODE_INVALID_MODEL_SOURCE: &str = "INVALID_MODEL_SOURCE";
const CODE_INVALID_MODEL_PAIRING: &str = "INVALID_MODEL_PAIRING";
const CODE_STORAGE_UNAVAILABLE: &str = "STORAGE_UNAVAILABLE";
const CODE_STORAGE_CORRUPT: &str = "STORAGE_CORRUPT";
const CODE_MODEL_BROKEN: &str = "MODEL_BROKEN";
const CODE_MODEL_NOT_FOUND: &str = "MODEL_NOT_FOUND";
const CODE_REMOTE_LOAD_FAILED: &str = "REMOTE_LOAD_FAILED";
const CODE_UNSUPPORTED_OPERATION: &str = "UNSUPPORTED_OPERATION";
const CODE_QUERY_FAILED: &str = "QUERY_FAILED";
const QUERY_STATUS_FAILED: &str = "failed";
const BROWSER_MODEL_ID_HASH_CHARS: usize = 24;
const LIFECYCLE_SERIALIZATION_FALLBACK: &str =
"{\"ok\":false,\"error\":{\"code\":\"STORAGE_CORRUPT\",\"message\":\"failed to serialize lifecycle response\"}}";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserRegistryManifest {
pub version: u32,
#[serde(default)]
pub projector_index_revision: u64,
#[serde(default)]
pub assets: BTreeMap<String, BrowserAssetRecord>,
#[serde(default)]
pub models: BTreeMap<String, BrowserModelEntry>,
}
impl Default for BrowserRegistryManifest {
fn default() -> Self {
Self {
version: REGISTRY_MANIFEST_VERSION,
projector_index_revision: 0,
assets: BTreeMap::new(),
models: BTreeMap::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserAssetRecord {
pub id: String,
pub kind: ModelAssetKind,
pub name: String,
pub bytes: u64,
pub storage_path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_etag: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_last_modified: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_bytes: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_part_index: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_part_count: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_file_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_file_last_modified: Option<u64>,
#[serde(default)]
pub ref_count: u32,
pub created_at: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inspection: Option<AssetInspection>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserModelPairing {
pub state: CoreModelPairingState,
pub checked_projector_index_revision: u64,
#[serde(default)]
pub compatible_vision_projector_types: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason_code: Option<ModelPairingReason>,
pub updated_at: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserModelEntry {
pub id: String,
pub name: String,
pub modality: ModelModality,
pub status: ModelStatus,
#[serde(default)]
pub model_asset_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub projector_asset_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pairing: Option<BrowserModelPairing>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime_fingerprint: Option<String>,
pub created_at: String,
pub updated_at: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_loaded_at: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserModelInfo {
pub id: String,
pub name: String,
pub modality: ModelModality,
pub status: ModelStatus,
pub source: ModelSourceKind,
pub bytes: u64,
pub loaded: bool,
pub chat_template: Option<String>,
pub bos_text: String,
pub eos_text: String,
pub media_marker: Option<String>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserLoadedModelState {
pub id: String,
pub asset_fingerprint: String,
pub runtime_fingerprint: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chat_template: Option<String>,
#[serde(default)]
pub bos_text: String,
#[serde(default)]
pub eos_text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub media_marker: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BrowserLifecycleState {
Idle,
Loading,
Ready,
Querying,
Error,
Closed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum BrowserObservabilityMode {
#[default]
Off,
Runtime,
Profile,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum BrowserBackendPreference {
#[default]
Auto,
Cpu,
#[serde(rename = "webgpu")]
WebGpu,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserQueryObservation {
pub context_key: Option<String>,
pub status: String,
pub wall_ms: Option<f64>,
pub ttft_ms: Option<f64>,
pub output_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_message: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserObservabilitySnapshot {
pub mode: BrowserObservabilityMode,
pub state: BrowserLifecycleState,
pub updated_at: String,
pub model: Option<BrowserModelInfo>,
pub query: Option<BrowserQueryObservation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BrowserObservabilityEventType {
LoadStart,
LoadComplete,
QueryStart,
QueryComplete,
Error,
Close,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserObservabilityEvent {
#[serde(rename = "type")]
pub event_type: BrowserObservabilityEventType,
pub snapshot: BrowserObservabilitySnapshot,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserCreateConfig {
#[serde(default)]
pub manifest: Option<BrowserRegistryManifest>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct BrowserLoadOptions {
#[serde(default)]
pub backend: BrowserBackendPreference,
#[serde(default)]
pub runtime: Value,
#[serde(default)]
pub observability: BrowserObservabilityMode,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum BrowserLoadSource {
Installed {
id: String,
#[serde(default)]
classified_projectors: Vec<ClassifiedAsset>,
},
Assets {
assets: Vec<BrowserAssetRecord>,
classified: Vec<ClassifiedAsset>,
#[serde(default)]
explicit_projector_asset_id: Option<String>,
#[serde(default)]
classified_projectors: Vec<ClassifiedAsset>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserPlannedAsset {
pub asset_id: String,
pub kind: ModelAssetKind,
pub storage_path: String,
pub mount_name: String,
pub bytes: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserPrepareLoadResponse {
pub load_id: String,
pub model: BrowserModelInfo,
pub runtime_fingerprint: String,
pub runtime_config: Value,
pub load_required: bool,
#[serde(default)]
pub assets: Vec<BrowserPlannedAsset>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub projector: Option<BrowserPlannedAsset>,
pub manifest: BrowserRegistryManifest,
pub snapshot: BrowserObservabilitySnapshot,
#[serde(default)]
pub events: Vec<BrowserObservabilityEvent>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserCommitLoadRequest {
pub load_id: String,
pub model_id: String,
pub runtime_fingerprint: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chat_template: Option<String>,
#[serde(default)]
pub bos_text: String,
#[serde(default)]
pub eos_text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub media_marker: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserCommitLoadResponse {
pub model: BrowserModelInfo,
pub manifest: BrowserRegistryManifest,
pub snapshot: BrowserObservabilitySnapshot,
#[serde(default)]
pub events: Vec<BrowserObservabilityEvent>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserRemoveResponse {
pub removed: BrowserModelEntry,
#[serde(default)]
pub orphaned_assets: Vec<BrowserAssetRecord>,
pub manifest: BrowserRegistryManifest,
pub snapshot: BrowserObservabilitySnapshot,
#[serde(default)]
pub events: Vec<BrowserObservabilityEvent>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserLifecycleEnvelope<T>
where
T: Serialize,
{
pub ok: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<BrowserLifecycleError>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserLifecycleError {
pub code: &'static str,
pub message: String,
}
#[derive(Debug, Clone)]
struct PendingLoad {
load_id: String,
model_id: String,
runtime_fingerprint: String,
}
struct BrowserResponseContext {
manifest: BrowserRegistryManifest,
snapshot: BrowserObservabilitySnapshot,
events: Vec<BrowserObservabilityEvent>,
}
#[derive(Debug, Clone)]
pub struct BrowserLifecycleService {
pub manifest: BrowserRegistryManifest,
current: Option<BrowserLoadedModelState>,
pending: Option<PendingLoad>,
pub snapshot: BrowserObservabilitySnapshot,
events: VecDeque<BrowserObservabilityEvent>,
}
impl BrowserLifecycleService {
pub fn create(config: BrowserCreateConfig) -> Result<Self, ModelError> {
let manifest = migrate_manifest(config.manifest.unwrap_or_default())?;
validate_manifest(&manifest)?;
let now = now_iso();
Ok(Self {
manifest,
current: None,
pending: None,
snapshot: BrowserObservabilitySnapshot {
mode: BrowserObservabilityMode::Off,
state: BrowserLifecycleState::Idle,
updated_at: now,
model: None,
query: None,
runtime: None,
profile: None,
},
events: VecDeque::new(),
})
}
pub fn list(&self) -> Vec<BrowserModelInfo> {
self.manifest
.models
.values()
.map(|entry| self.model_info_from_entry(entry))
.collect()
}
pub fn current(&self) -> Option<BrowserModelInfo> {
self.current.as_ref().and_then(|current| {
self.manifest
.models
.get(¤t.id)
.map(|entry| self.model_info_from_entry(entry))
})
}
pub fn drain_events(&mut self) -> Vec<BrowserObservabilityEvent> {
self.events.drain(..).collect()
}
pub fn prepare_load(
&mut self,
source: BrowserLoadSource,
options: BrowserLoadOptions,
) -> Result<BrowserPrepareLoadResponse, ModelError> {
validate_manifest(&self.manifest)?;
let entry = match source {
BrowserLoadSource::Installed {
id,
classified_projectors,
} => {
let entry = self
.manifest
.models
.get(&id)
.cloned()
.ok_or_else(|| model_not_found(&id))?;
let base_plan = self.derive_base_plan_for_entry(&entry)?;
self.resolve_entry_for_loading(entry, &base_plan, &classified_projectors)?
}
BrowserLoadSource::Assets {
assets,
classified,
explicit_projector_asset_id,
classified_projectors,
} => {
self.upsert_assets(assets, &classified)?;
let source_projector = resolve_source_projector_asset_id(
&classified,
explicit_projector_asset_id.as_deref(),
);
let base_classified: Vec<_> = classified
.iter()
.filter(|asset| Some(asset.asset_id.as_str()) != source_projector.as_deref())
.cloned()
.collect();
let base_plan = PairingResolver::resolve(&base_classified)?;
let mut entry = self.upsert_base_model_entry(&base_plan)?;
if let Some(projector_id) = source_projector {
let previous = entry.clone();
match PairingResolver::resolve_explicit(&classified, &projector_id) {
Ok(plan) => {
let projector = plan
.projector_asset_id
.clone()
.ok_or_else(|| invalid_pairing(EXPLICIT_PROJECTOR_MISSING_ID))?;
entry = self.set_resolved_projector(
&entry.id,
&projector,
&plan.compatible_vision_projector_types,
)?;
}
Err(error) => {
self.restore_entry(previous)?;
return Err(error);
}
}
} else {
entry =
self.resolve_entry_for_loading(entry, &base_plan, &classified_projectors)?;
}
validate_manifest(&self.manifest)?;
entry
}
};
let backend_plan = browser_backend_plan(&options)?;
let runtime_config = serde_json::to_value(&backend_plan.config)?;
let runtime_fingerprint = runtime_fingerprint(&runtime_config, &backend_plan.selection);
let asset_fingerprint = asset_fingerprint(&entry);
let load_id = browser_load_id(&entry.id, &asset_fingerprint, &runtime_fingerprint);
let load_required = browser_load_required(
self.current.as_ref(),
&entry.id,
&asset_fingerprint,
&runtime_fingerprint,
);
let model = self.model_info_from_entry(&entry);
let (assets, projector) = self.planned_assets_for_entry(&entry)?;
self.pending = Some(PendingLoad {
load_id: load_id.clone(),
model_id: entry.id.clone(),
runtime_fingerprint: runtime_fingerprint.clone(),
});
self.emit(
BrowserObservabilityEventType::LoadStart,
SnapshotPatch {
mode: Some(options.observability),
state: Some(BrowserLifecycleState::Loading),
model: None,
query: Some(None),
runtime: Some(None),
profile: Some(None),
},
);
let response = self.response_context();
Ok(BrowserPrepareLoadResponse {
load_id,
model,
runtime_fingerprint,
runtime_config,
load_required,
assets,
projector,
manifest: response.manifest,
snapshot: response.snapshot,
events: response.events,
})
}
pub fn commit_load(
&mut self,
request: BrowserCommitLoadRequest,
) -> Result<BrowserCommitLoadResponse, ModelError> {
let pending = self
.pending
.take()
.ok_or_else(|| invalid_source(NO_PENDING_BROWSER_MODEL_LOAD))?;
if pending.load_id != request.load_id
|| pending.model_id != request.model_id
|| pending.runtime_fingerprint != request.runtime_fingerprint
{
self.pending = Some(pending);
return Err(invalid_source(BROWSER_LOAD_COMMIT_MISMATCH));
}
let loaded_at = now_iso();
{
let entry = self
.manifest
.models
.get_mut(&request.model_id)
.ok_or_else(|| model_not_found(&request.model_id))?;
entry.last_loaded_at = Some(loaded_at.clone());
entry.runtime_fingerprint = Some(request.runtime_fingerprint.clone());
entry.updated_at = loaded_at;
}
validate_manifest(&self.manifest)?;
let entry = self
.manifest
.models
.get(&request.model_id)
.ok_or_else(|| model_not_found(&request.model_id))?
.clone();
self.current = Some(BrowserLoadedModelState {
id: request.model_id,
asset_fingerprint: asset_fingerprint(&entry),
runtime_fingerprint: request.runtime_fingerprint,
chat_template: request.chat_template,
bos_text: request.bos_text,
eos_text: request.eos_text,
media_marker: request.media_marker,
});
let model = self.model_info_from_entry(&entry);
self.emit(
BrowserObservabilityEventType::LoadComplete,
SnapshotPatch {
mode: None,
state: Some(BrowserLifecycleState::Ready),
model: Some(Some(model.clone())),
query: None,
runtime: Some(request.runtime),
profile: Some(request.profile),
},
);
let response = self.response_context();
Ok(BrowserCommitLoadResponse {
model,
manifest: response.manifest,
snapshot: response.snapshot,
events: response.events,
})
}
pub fn abort_load(&mut self, message: Option<String>) -> BrowserObservabilitySnapshot {
self.pending = None;
self.emit(
BrowserObservabilityEventType::Error,
SnapshotPatch {
mode: None,
state: Some(BrowserLifecycleState::Error),
model: None,
query: Some(Some(failed_query_observation(message))),
runtime: None,
profile: None,
},
);
self.snapshot.clone()
}
pub fn remove(&mut self, model_id: &str) -> Result<BrowserRemoveResponse, ModelError> {
let removed = self
.manifest
.models
.remove(model_id)
.ok_or_else(|| model_not_found(model_id))?;
self.decrement_existing_refs(&removed);
let orphaned_assets = self.remove_orphaned_assets();
if self
.current
.as_ref()
.is_some_and(|current| current.id == model_id)
{
self.current = None;
}
if contains_projector_asset(&orphaned_assets) {
self.bump_projector_index_revision()?;
}
validate_manifest(&self.manifest)?;
let state = if self.current.is_some() {
BrowserLifecycleState::Ready
} else {
BrowserLifecycleState::Idle
};
self.emit(
BrowserObservabilityEventType::LoadComplete,
SnapshotPatch {
mode: None,
state: Some(state),
model: Some(self.current()),
query: Some(None),
runtime: Some(None),
profile: Some(None),
},
);
let response = self.response_context();
Ok(BrowserRemoveResponse {
removed,
orphaned_assets,
manifest: response.manifest,
snapshot: response.snapshot,
events: response.events,
})
}
pub fn unload(&mut self) -> BrowserObservabilitySnapshot {
self.current = None;
self.pending = None;
self.emit(
BrowserObservabilityEventType::LoadComplete,
SnapshotPatch {
mode: None,
state: Some(BrowserLifecycleState::Idle),
model: Some(None),
query: Some(None),
runtime: Some(None),
profile: Some(None),
},
);
self.snapshot.clone()
}
pub fn close(&mut self) -> BrowserObservabilitySnapshot {
self.current = None;
self.pending = None;
self.emit(
BrowserObservabilityEventType::Close,
SnapshotPatch {
mode: None,
state: Some(BrowserLifecycleState::Closed),
model: Some(None),
query: Some(None),
runtime: Some(None),
profile: Some(None),
},
);
self.snapshot.clone()
}
pub fn record_event(
&mut self,
event_type: BrowserObservabilityEventType,
patch: Value,
) -> Result<BrowserObservabilitySnapshot, ModelError> {
let patch = serde_json::from_value::<SnapshotPatch>(patch)?;
self.emit(event_type, patch);
Ok(self.snapshot.clone())
}
fn response_context(&mut self) -> BrowserResponseContext {
BrowserResponseContext {
manifest: self.manifest.clone(),
snapshot: self.snapshot.clone(),
events: self.drain_events(),
}
}
fn upsert_assets(
&mut self,
assets: Vec<BrowserAssetRecord>,
classified: &[ClassifiedAsset],
) -> Result<(), ModelError> {
let mut projector_index_changed = false;
let classified_by_id: BTreeMap<_, _> = classified
.iter()
.map(|asset| (asset.asset_id.as_str(), &asset.inspection))
.collect();
for mut asset in assets {
validate_asset_record(&asset)?;
let inspection = classified_by_id.get(asset.id.as_str()).copied();
if let Some(inspection) = inspection {
asset.inspection = Some((*inspection).clone());
if inspection.role == crate::lifecycle::AssetRole::Projector {
asset.kind = ModelAssetKind::Projector;
}
}
if let Some(existing) = self.manifest.assets.get(&asset.id) {
let next_kind = if existing.kind == ModelAssetKind::Projector
|| asset.kind == ModelAssetKind::Projector
{
ModelAssetKind::Projector
} else {
asset.kind
};
projector_index_changed |= existing.kind != next_kind
&& (existing.kind == ModelAssetKind::Projector
|| next_kind == ModelAssetKind::Projector);
asset.ref_count = existing.ref_count;
asset.created_at = existing.created_at.clone();
asset.kind = next_kind;
if asset.inspection.is_none() {
asset.inspection = existing.inspection.clone();
}
} else if asset.kind == ModelAssetKind::Projector {
projector_index_changed = true;
}
self.manifest.assets.insert(asset.id.clone(), asset);
}
if projector_index_changed {
self.bump_projector_index_revision()?;
}
Ok(())
}
fn upsert_base_model_entry(
&mut self,
plan: &PairingPlan,
) -> Result<BrowserModelEntry, ModelError> {
let id = base_model_id(plan);
let now = now_iso();
let next_refs = sorted_model_asset_ids(&plan.model_asset_ids, None);
let entry = if let Some(existing) = self.manifest.models.get(&id).cloned() {
let previous_refs = entry_asset_ids(&existing);
let mut updated = existing;
updated.name = plan.name.clone();
updated.model_asset_ids = plan.model_asset_ids.clone();
if updated.projector_asset_id.is_none() {
updated.modality = plan.modality;
updated.status = plan.status;
}
updated.updated_at = now;
self.rebalance_refs(&previous_refs, &entry_asset_ids(&updated))?;
updated
} else {
let entry = BrowserModelEntry {
id: id.clone(),
name: plan.name.clone(),
modality: plan.modality,
status: plan.status,
model_asset_ids: plan.model_asset_ids.clone(),
projector_asset_id: None,
pairing: None,
runtime_fingerprint: None,
created_at: now.clone(),
updated_at: now,
last_loaded_at: None,
};
self.increment_refs(&next_refs)?;
entry
};
self.manifest.models.insert(id, entry.clone());
validate_manifest(&self.manifest)?;
Ok(entry)
}
fn derive_base_plan_for_entry(
&self,
entry: &BrowserModelEntry,
) -> Result<PairingPlan, ModelError> {
let classified = self.map_model_assets(entry, |record| {
classified_asset(
record.id.clone(),
record.name.clone(),
record.inspection.clone(),
)
})?;
PairingResolver::resolve(&classified)
}
fn resolve_entry_for_loading(
&mut self,
mut entry: BrowserModelEntry,
base_plan: &PairingPlan,
classified_projectors: &[ClassifiedAsset],
) -> Result<BrowserModelEntry, ModelError> {
let normalized_base_projector_types =
normalize_projector_types(&base_plan.compatible_vision_projector_types);
let compatible_projector_types =
compatible_projector_type_set(&base_plan.compatible_vision_projector_types);
if let Some(projector_id) = entry.projector_asset_id.clone() {
if let Some(projector) = self.manifest.assets.get(&projector_id).cloned() {
if base_plan.compatible_vision_projector_types.is_empty() {
return Ok(entry);
}
let inspection = projector.inspection.clone().or_else(|| {
classified_projectors
.iter()
.find(|asset| asset.asset_id == projector_id)
.map(|asset| asset.inspection.clone())
});
if !projector_type_matches(inspection.as_ref(), &compatible_projector_types) {
entry = self.detach_projector(&entry.id, base_plan)?;
} else if match entry.pairing.as_ref() {
Some(pairing) => {
pairing.state != CoreModelPairingState::Resolved
|| normalize_projector_types(&pairing.compatible_vision_projector_types)
!= normalized_base_projector_types
}
None => true,
} {
entry = self.set_resolved_projector(
&entry.id,
&projector_id,
&base_plan.compatible_vision_projector_types,
)?;
}
} else {
entry = self.detach_projector(&entry.id, base_plan)?;
}
}
if base_plan.modality != ModelModality::Vision {
return self.set_unresolved_pairing(
&entry.id,
base_plan,
ModelPairingReason::BaseNotVision,
);
}
if base_plan.compatible_vision_projector_types.is_empty() {
return self.set_unresolved_pairing(
&entry.id,
base_plan,
ModelPairingReason::MissingMetadata,
);
}
if entry.pairing.as_ref().is_some_and(|pairing| {
pairing.state == CoreModelPairingState::Unresolved
&& pairing.checked_projector_index_revision
== self.manifest.projector_index_revision
&& normalize_projector_types(&pairing.compatible_vision_projector_types)
== normalized_base_projector_types
}) {
return Ok(entry);
}
let matches = self.find_compatible_installed_projector_ids(
&base_plan.compatible_vision_projector_types,
classified_projectors,
);
if matches.len() == 1 {
self.set_resolved_projector(
&entry.id,
&matches[0],
&base_plan.compatible_vision_projector_types,
)
} else {
self.set_unresolved_pairing(
&entry.id,
base_plan,
if matches.is_empty() {
ModelPairingReason::NoMatch
} else {
ModelPairingReason::MultipleMatches
},
)
}
}
fn find_compatible_installed_projector_ids(
&self,
compatible_vision_projector_types: &[String],
classified_projectors: &[ClassifiedAsset],
) -> Vec<String> {
let compatible = compatible_projector_type_set(compatible_vision_projector_types);
let supplied: BTreeMap<_, _> = classified_projectors
.iter()
.map(|asset| (asset.asset_id.as_str(), &asset.inspection))
.collect();
let mut matches = Vec::new();
for asset in self.manifest.assets.values() {
if asset.kind != ModelAssetKind::Projector || asset.ref_count == 0 {
continue;
}
let inspection = asset
.inspection
.as_ref()
.or_else(|| supplied.get(asset.id.as_str()).copied());
if projector_type_matches(inspection, &compatible) {
matches.push(asset.id.clone());
}
}
sorted_values(matches)
}
fn set_resolved_projector(
&mut self,
id: &str,
projector_asset_id: &str,
compatible_vision_projector_types: &[String],
) -> Result<BrowserModelEntry, ModelError> {
let now = now_iso();
let revision = self.manifest.projector_index_revision;
self.update_model_entry(id, |entry| {
entry.projector_asset_id = Some(projector_asset_id.to_string());
entry.modality = ModelModality::Vision;
entry.status = ModelStatus::Ready;
entry.pairing = Some(browser_pairing(
CoreModelPairingState::Resolved,
revision,
compatible_vision_projector_types,
None,
&now,
));
entry.updated_at = now;
})
}
fn set_unresolved_pairing(
&mut self,
id: &str,
plan: &PairingPlan,
reason_code: ModelPairingReason,
) -> Result<BrowserModelEntry, ModelError> {
let now = now_iso();
let revision = self.manifest.projector_index_revision;
self.update_model_entry(id, |entry| {
entry.projector_asset_id = None;
entry.modality = plan.modality;
entry.status = plan.status;
entry.pairing = Some(browser_pairing(
CoreModelPairingState::Unresolved,
revision,
&plan.compatible_vision_projector_types,
Some(reason_code),
&now,
));
entry.updated_at = now;
})
}
fn detach_projector(
&mut self,
id: &str,
base_plan: &PairingPlan,
) -> Result<BrowserModelEntry, ModelError> {
let now = now_iso();
self.update_model_entry(id, |entry| {
entry.projector_asset_id = None;
entry.modality = base_plan.modality;
entry.status = base_plan.status;
entry.pairing = None;
entry.updated_at = now;
})
}
fn update_model_entry(
&mut self,
id: &str,
update: impl FnOnce(&mut BrowserModelEntry),
) -> Result<BrowserModelEntry, ModelError> {
let mut entry = self
.manifest
.models
.get(id)
.cloned()
.ok_or_else(|| model_not_found(id))?;
let previous_refs = entry_asset_ids(&entry);
update(&mut entry);
let next_refs = entry_asset_ids(&entry);
self.rebalance_refs(&previous_refs, &next_refs)?;
self.manifest.models.insert(id.to_string(), entry.clone());
validate_manifest(&self.manifest)?;
Ok(entry)
}
fn restore_entry(&mut self, snapshot: BrowserModelEntry) -> Result<(), ModelError> {
let existing = self
.manifest
.models
.get(&snapshot.id)
.cloned()
.ok_or_else(|| model_not_found(&snapshot.id))?;
let previous_refs = entry_asset_ids(&existing);
let next_refs = entry_asset_ids(&snapshot);
self.rebalance_refs(&previous_refs, &next_refs)?;
self.manifest.models.insert(snapshot.id.clone(), snapshot);
validate_manifest(&self.manifest)
}
fn planned_assets_for_entry(
&self,
entry: &BrowserModelEntry,
) -> Result<(Vec<BrowserPlannedAsset>, Option<BrowserPlannedAsset>), ModelError> {
let assets = self.map_model_assets(entry, planned_asset)?;
let projector = entry
.projector_asset_id
.as_deref()
.map(|asset_id| {
let record = self
.manifest
.assets
.get(asset_id)
.ok_or_else(|| missing_model_asset(asset_id))?;
Ok::<BrowserPlannedAsset, ModelError>(planned_asset(record))
})
.transpose()?;
Ok((assets, projector))
}
fn map_model_assets<T>(
&self,
entry: &BrowserModelEntry,
mut map: impl FnMut(&BrowserAssetRecord) -> T,
) -> Result<Vec<T>, ModelError> {
entry
.model_asset_ids
.iter()
.map(|asset_id| {
self.manifest
.assets
.get(asset_id)
.ok_or_else(|| missing_model_asset(asset_id))
.map(&mut map)
})
.collect()
}
fn model_info_from_entry(&self, entry: &BrowserModelEntry) -> BrowserModelInfo {
let assets = entry_asset_ids(entry)
.into_iter()
.filter_map(|asset_id| self.manifest.assets.get(&asset_id));
let summary = browser_asset_summary(assets);
let current = current_loaded_model(self.current.as_ref(), &entry.id);
BrowserModelInfo {
id: entry.id.clone(),
name: entry.name.clone(),
modality: entry.modality,
status: entry.status,
source: summary.source,
bytes: summary.bytes,
loaded: current.is_some(),
chat_template: current.and_then(|current| current.chat_template.clone()),
bos_text: current.map_or_else(String::new, |current| current.bos_text.clone()),
eos_text: current.map_or_else(String::new, |current| current.eos_text.clone()),
media_marker: browser_media_marker(current, entry.modality),
created_at: entry.created_at.clone(),
updated_at: entry.updated_at.clone(),
}
}
fn increment_refs(&mut self, asset_ids: &[String]) -> Result<(), ModelError> {
self.adjust_refs(asset_ids, increment_asset_refcount)
}
fn decrement_refs(&mut self, asset_ids: &[String]) -> Result<(), ModelError> {
self.adjust_refs(asset_ids, decrement_asset_refcount)
}
fn adjust_refs(
&mut self,
asset_ids: &[String],
adjust_refcount: fn(&mut u32, &str) -> Result<(), ModelError>,
) -> Result<(), ModelError> {
for id in sorted_unique_strings(asset_ids.to_vec()) {
let asset = self
.manifest
.assets
.get_mut(&id)
.ok_or_else(|| missing_model_asset(&id))?;
adjust_refcount(&mut asset.ref_count, &id)?;
}
Ok(())
}
fn decrement_existing_refs(&mut self, entry: &BrowserModelEntry) {
for asset_id in entry_asset_ids(entry) {
let Some(asset) = self.manifest.assets.get_mut(&asset_id) else {
continue;
};
if asset.ref_count > 0 {
asset.ref_count -= 1;
}
}
}
fn remove_orphaned_assets(&mut self) -> Vec<BrowserAssetRecord> {
remove_matching_values(&mut self.manifest.assets, |asset| asset.ref_count == 0)
}
fn rebalance_refs(
&mut self,
previous_refs: &[String],
next_refs: &[String],
) -> Result<(), ModelError> {
let (removed, added) = sorted_ref_deltas(previous_refs, next_refs);
self.decrement_refs(&removed)?;
self.increment_refs(&added)
}
fn bump_projector_index_revision(&mut self) -> Result<(), ModelError> {
bump_revision(&mut self.manifest.projector_index_revision)
}
fn emit(&mut self, event_type: BrowserObservabilityEventType, patch: SnapshotPatch) {
self.snapshot = apply_snapshot_patch(self.snapshot.clone(), patch);
let event = BrowserObservabilityEvent {
event_type,
snapshot: self.snapshot.clone(),
};
self.events.push_back(event);
}
}
#[derive(Debug, Clone, Default, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
struct SnapshotPatch {
#[serde(default)]
mode: Option<BrowserObservabilityMode>,
#[serde(default)]
state: Option<BrowserLifecycleState>,
#[serde(default)]
model: Option<Option<BrowserModelInfo>>,
#[serde(default)]
query: Option<Option<BrowserQueryObservation>>,
#[serde(default)]
runtime: Option<Option<Value>>,
#[serde(default)]
profile: Option<Option<Value>>,
}
pub fn success_response<T>(value: T) -> BrowserLifecycleEnvelope<T>
where
T: Serialize,
{
BrowserLifecycleEnvelope {
ok: true,
value: Some(value),
error: None,
}
}
pub fn error_response<T>(error: ModelError) -> BrowserLifecycleEnvelope<T>
where
T: Serialize,
{
let code = match error {
ModelError::InvalidModelSource(_) => CODE_INVALID_MODEL_SOURCE,
ModelError::InvalidModelPairing(_) => CODE_INVALID_MODEL_PAIRING,
ModelError::StorageUnavailable(_) => CODE_STORAGE_UNAVAILABLE,
ModelError::StorageCorrupt(_) => CODE_STORAGE_CORRUPT,
ModelError::AssetMissing(_) => CODE_MODEL_BROKEN,
ModelError::ModelNotFound(_) => CODE_MODEL_NOT_FOUND,
ModelError::RemoteUnavailable(_) => CODE_REMOTE_LOAD_FAILED,
ModelError::UnsupportedOperation { .. } => CODE_UNSUPPORTED_OPERATION,
ModelError::Runtime(_) => CODE_QUERY_FAILED,
ModelError::RegistryJson(_) => CODE_STORAGE_CORRUPT,
ModelError::Io(_) => CODE_STORAGE_UNAVAILABLE,
ModelError::UnsupportedGgufVersion(_)
| ModelError::InvalidGgufMetadata(_)
| ModelError::GgufMetadataTooLarge { .. } => CODE_INVALID_MODEL_SOURCE,
};
BrowserLifecycleEnvelope {
ok: false,
value: None,
error: Some(BrowserLifecycleError {
code,
message: error.to_string(),
}),
}
}
pub fn response_json<T>(response: BrowserLifecycleEnvelope<T>) -> String
where
T: Serialize,
{
serde_json::to_string(&response)
.unwrap_or_else(|_| LIFECYCLE_SERIALIZATION_FALLBACK.to_string())
}
fn migrate_manifest(
mut manifest: BrowserRegistryManifest,
) -> Result<BrowserRegistryManifest, ModelError> {
validate_registry_manifest_version(BROWSER_REGISTRY_MANIFEST_LABEL, manifest.version)?;
for (id, asset) in &mut manifest.assets {
if asset.id.is_empty() {
asset.id = id.clone();
}
if asset.created_at.trim().is_empty() {
asset.created_at = now_iso();
}
}
for (id, model) in &mut manifest.models {
if model.id.is_empty() {
model.id = id.clone();
}
if model.created_at.trim().is_empty() {
model.created_at = now_iso();
}
if model.updated_at.trim().is_empty() {
model.updated_at = model.created_at.clone();
}
}
Ok(manifest)
}
fn validate_manifest(manifest: &BrowserRegistryManifest) -> Result<(), ModelError> {
validate_registry_manifest_version(BROWSER_REGISTRY_MANIFEST_LABEL, manifest.version)?;
let mut expected_ref_counts = BTreeMap::<String, u32>::new();
for (id, asset) in &manifest.assets {
if id != &asset.id {
return Err(manifest_key_mismatch("asset", id, &asset.id));
}
validate_asset_record(asset)?;
}
for (id, model) in &manifest.models {
if id != &model.id {
return Err(manifest_key_mismatch("model", id, &model.id));
}
for asset_id in entry_asset_ids(model) {
if !manifest.assets.contains_key(&asset_id) {
return Err(model_missing_asset(id, &asset_id));
}
increment_expected_asset_refcount(&mut expected_ref_counts, &asset_id)?;
}
}
for (id, asset) in &manifest.assets {
let expected = expected_ref_counts.get(id).copied().unwrap_or(0);
if asset.ref_count != expected {
return Err(asset_refcount_mismatch(id, asset.ref_count, expected));
}
}
Ok(())
}
fn validate_asset_record(asset: &BrowserAssetRecord) -> Result<(), ModelError> {
if asset.id.trim().is_empty() {
return Err(empty_asset_id());
}
if asset.storage_path.trim().is_empty() {
return Err(invalid_asset_field(
&asset.id,
"storagePath must not be empty",
));
}
if asset.bytes == 0 {
return Err(invalid_asset_field(&asset.id, "byte size must be positive"));
}
let has_split_index = asset.source_part_index.is_some() || asset.source_part_count.is_some();
if has_split_index {
let index = asset
.source_part_index
.ok_or_else(|| invalid_asset_field(&asset.id, "split part index is missing"))?;
let count = asset
.source_part_count
.ok_or_else(|| invalid_asset_field(&asset.id, "split part count is missing"))?;
if count == 0 || index >= count {
return Err(invalid_asset_field(
&asset.id,
"split part index/count is invalid",
));
}
}
Ok(())
}
fn resolve_source_projector_asset_id(
classified: &[ClassifiedAsset],
explicit_projector_asset_id: Option<&str>,
) -> Option<String> {
if let Some(explicit) = explicit_projector_asset_id {
return Some(explicit.to_string());
}
let projectors: Vec<_> = classified
.iter()
.filter(|asset| asset.inspection.role == crate::lifecycle::AssetRole::Projector)
.collect();
(projectors.len() == 1).then(|| projectors[0].asset_id.clone())
}
fn planned_asset(record: &BrowserAssetRecord) -> BrowserPlannedAsset {
BrowserPlannedAsset {
asset_id: record.id.clone(),
kind: record.kind,
storage_path: record.storage_path.clone(),
mount_name: record.name.clone(),
bytes: record.bytes,
}
}
fn contains_projector_asset(assets: &[BrowserAssetRecord]) -> bool {
assets
.iter()
.any(|asset| asset.kind == ModelAssetKind::Projector)
}
fn browser_asset_summary<'asset>(
assets: impl Iterator<Item = &'asset BrowserAssetRecord>,
) -> AssetSummary {
asset_summary(assets.map(|asset| (asset.bytes, asset.source_url.is_some())))
}
fn current_loaded_model<'model>(
current: Option<&'model BrowserLoadedModelState>,
entry_id: &str,
) -> Option<&'model BrowserLoadedModelState> {
current.filter(|current| current.id == entry_id)
}
fn browser_media_marker(
current: Option<&BrowserLoadedModelState>,
modality: ModelModality,
) -> Option<String> {
current
.and_then(|current| current.media_marker.clone())
.or_else(|| media_marker_for_modality(modality))
}
fn entry_asset_ids(entry: &BrowserModelEntry) -> Vec<String> {
sorted_model_asset_ids(&entry.model_asset_ids, entry.projector_asset_id.as_ref())
}
fn failed_query_observation(message: Option<String>) -> BrowserQueryObservation {
BrowserQueryObservation {
context_key: None,
status: QUERY_STATUS_FAILED.to_string(),
wall_ms: None,
ttft_ms: None,
output_tokens: None,
error_code: Some(CODE_QUERY_FAILED.to_string()),
error_message: message,
}
}
fn normalize_projector_types(projector_types: &[String]) -> Vec<String> {
sorted_unique_strings(projector_types.to_vec())
}
fn compatible_projector_type_set(projector_types: &[String]) -> BTreeSet<&String> {
projector_types.iter().collect()
}
fn projector_type_matches(
inspection: Option<&AssetInspection>,
compatible_projector_types: &BTreeSet<&String>,
) -> bool {
inspection
.and_then(|inspection| inspection.provided_vision_projector_type.as_ref())
.is_some_and(|provided| compatible_projector_types.contains(provided))
}
fn browser_pairing(
state: CoreModelPairingState,
projector_index_revision: u64,
compatible_vision_projector_types: &[String],
reason_code: Option<ModelPairingReason>,
updated_at: &str,
) -> BrowserModelPairing {
BrowserModelPairing {
state,
checked_projector_index_revision: projector_index_revision,
compatible_vision_projector_types: normalize_projector_types(
compatible_vision_projector_types,
),
reason_code,
updated_at: updated_at.to_string(),
}
}
fn base_model_id(plan: &PairingPlan) -> String {
let hash = stable_json_hash(&json!({
"modelAssetIds": sorted_model_asset_ids(&plan.model_asset_ids, None),
}));
format!("model-{}", &hash[..BROWSER_MODEL_ID_HASH_CHARS])
}
fn asset_fingerprint(entry: &BrowserModelEntry) -> String {
stable_json_hash(&json!({
"modelAssetIds": sorted_model_asset_ids(&entry.model_asset_ids, None),
"projectorAssetId": entry.projector_asset_id,
}))
}
fn browser_load_id(model_id: &str, asset_fingerprint: &str, runtime_fingerprint: &str) -> String {
stable_json_hash(&json!({
"modelId": model_id,
"assetFingerprint": asset_fingerprint,
"runtimeFingerprint": runtime_fingerprint,
"nonce": now_iso(),
}))
}
fn browser_load_required(
current: Option<&BrowserLoadedModelState>,
model_id: &str,
asset_fingerprint: &str,
runtime_fingerprint: &str,
) -> bool {
match current {
Some(current) => {
current.id != model_id
|| current.asset_fingerprint != asset_fingerprint
|| current.runtime_fingerprint != runtime_fingerprint
}
None => true,
}
}
fn browser_backend_plan(options: &BrowserLoadOptions) -> Result<BackendPlan, ModelError> {
browser_backend_plan_with_capabilities(options, None)
}
fn browser_backend_plan_with_capabilities(
options: &BrowserLoadOptions,
capabilities: Option<&BackendCapabilities>,
) -> Result<BackendPlan, ModelError> {
let backend = match options.backend {
BrowserBackendPreference::Auto => BackendPreference::Auto,
BrowserBackendPreference::Cpu => BackendPreference::Cpu,
BrowserBackendPreference::WebGpu => BackendPreference::WebGpu,
};
let load_options = ModelLoadOptions {
backend,
stats: stats_mode(options.observability),
runtime: browser_runtime_config(&options.runtime)?,
};
if let Some(capabilities) = capabilities {
return BackendPolicy::select_with_capabilities(&load_options, capabilities);
}
let selected = match options.backend {
BrowserBackendPreference::Auto | BrowserBackendPreference::Cpu => {
BackendPreference::Cpu.as_str()
}
BrowserBackendPreference::WebGpu => BackendPreference::WebGpu.as_str(),
};
Ok(BackendPolicy::select_known(
&load_options,
selected,
vec![selected.to_string()],
Some(format!("browser selected {selected}")),
))
}
fn browser_runtime_config(runtime: &Value) -> Result<NativeRuntimeConfig, ModelError> {
let runtime = if runtime.is_null() {
json!({})
} else {
runtime.clone()
};
Ok(serde_json::from_value(runtime)?)
}
fn stats_mode(mode: BrowserObservabilityMode) -> StatsMode {
match mode {
BrowserObservabilityMode::Off => StatsMode::Off,
BrowserObservabilityMode::Runtime => StatsMode::Basic,
BrowserObservabilityMode::Profile => StatsMode::Profile,
}
}
fn runtime_fingerprint(runtime_config: &Value, backend: &BackendSelection) -> String {
stable_json_hash(&json!({
"backend": backend.selected,
"runtime": runtime_config,
}))
}
fn apply_snapshot_patch(
mut snapshot: BrowserObservabilitySnapshot,
patch: SnapshotPatch,
) -> BrowserObservabilitySnapshot {
if let Some(mode) = patch.mode {
snapshot.mode = mode;
}
if let Some(state) = patch.state {
snapshot.state = state;
}
if let Some(model) = patch.model {
snapshot.model = model;
}
if let Some(query) = patch.query {
snapshot.query = query;
}
if let Some(runtime) = patch.runtime {
snapshot.runtime = runtime;
}
if let Some(profile) = patch.profile {
snapshot.profile = profile;
}
snapshot.updated_at = now_iso();
snapshot
}
fn stable_json_hash(value: &Value) -> String {
sha256_hex(stable_json(value).as_bytes())
}
fn stable_json(value: &Value) -> String {
match value {
Value::Null => "null".to_string(),
Value::Bool(value) => value.to_string(),
Value::Number(value) => value.to_string(),
Value::String(value) => serde_json::to_string(value).expect("string serialization"),
Value::Array(values) => {
let inner = values.iter().map(stable_json).collect::<Vec<_>>().join(",");
format!("[{inner}]")
}
Value::Object(values) => {
let inner = values
.iter()
.map(|(key, value)| {
format!(
"{}:{}",
serde_json::to_string(key).expect("key serialization"),
stable_json(value)
)
})
.collect::<Vec<_>>()
.join(",");
format!("{{{inner}}}")
}
}
}
fn now_iso() -> String {
let ms = super::storage::now_unix_ms();
iso_from_unix_ms(ms)
}
fn iso_from_unix_ms(ms: u64) -> String {
let seconds = ms / MILLIS_PER_SECOND;
let millis = ms % MILLIS_PER_SECOND;
let days = (seconds / SECONDS_PER_DAY) as i64;
let seconds_of_day = seconds % SECONDS_PER_DAY;
let (year, month, day) = civil_from_days(days);
let hour = seconds_of_day / SECONDS_PER_HOUR;
let minute = (seconds_of_day % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE;
let second = seconds_of_day % SECONDS_PER_MINUTE;
format!("{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{millis:03}Z")
}
fn civil_from_days(days_since_unix_epoch: i64) -> (i64, u32, u32) {
let z = days_since_unix_epoch + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = z - era * 146_097;
let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
let mut year = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let day = doy - (153 * mp + 2) / 5 + 1;
let month = mp + if mp < 10 { 3 } else { -9 };
if month <= 2 {
year += 1;
}
(year, month as u32, day as u32)
}
#[cfg(test)]
#[path = "../tests/lifecycle/browser_tests.rs"]
mod browser_tests;