tensorfs 0.3.44__tar.gz → 0.3.45__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tensorfs-0.3.44 → tensorfs-0.3.45}/Cargo.lock +2 -2
- {tensorfs-0.3.44 → tensorfs-0.3.45}/Cargo.toml +1 -1
- {tensorfs-0.3.44 → tensorfs-0.3.45}/PKG-INFO +1 -1
- tensorfs-0.3.45/crates/tensorfs-core/src/asset_update.rs +293 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/lib.rs +1 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-py/src/store.rs +37 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/pyproject.toml +1 -1
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/_ext.pyi +7 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/catalog.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/checkpoint_root.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/derived.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/source.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/meta.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/repo_cache.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/store.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/http.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/transport/tests.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/checkpoint_roots.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-py/src/lib.rs +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/__init__.py +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/_derived_channel.py +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/_source_channel.py +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/_tensors.py +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/derived.py +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.44 → tensorfs-0.3.45}/python/tensorfs/py.typed +0 -0
|
@@ -421,7 +421,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
421
421
|
|
|
422
422
|
[[package]]
|
|
423
423
|
name = "tensorfs-core"
|
|
424
|
-
version = "0.3.
|
|
424
|
+
version = "0.3.45"
|
|
425
425
|
dependencies = [
|
|
426
426
|
"fs2",
|
|
427
427
|
"libc",
|
|
@@ -435,7 +435,7 @@ dependencies = [
|
|
|
435
435
|
|
|
436
436
|
[[package]]
|
|
437
437
|
name = "tensorfs-py"
|
|
438
|
-
version = "0.3.
|
|
438
|
+
version = "0.3.45"
|
|
439
439
|
dependencies = [
|
|
440
440
|
"pyo3",
|
|
441
441
|
"tensorfs-core",
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
//! Metadata-only model-asset updates.
|
|
2
|
+
//!
|
|
3
|
+
//! An asset update rewrites only the CozyTensors header and manifest. Tensor part
|
|
4
|
+
//! ObjectRefs are copied verbatim; asset source objects are selected from an existing
|
|
5
|
+
//! manifest and are never opened or rehashed by this operation. Payload verification
|
|
6
|
+
//! remains the normal `read_asset` responsibility (or the repository admission gate).
|
|
7
|
+
|
|
8
|
+
use std::collections::BTreeMap;
|
|
9
|
+
|
|
10
|
+
use crate::checkpoint;
|
|
11
|
+
use crate::err::{refuse, Code, Refusal, Result};
|
|
12
|
+
use crate::header::Asset;
|
|
13
|
+
use crate::ids::ObjectRef;
|
|
14
|
+
use crate::manifest::{Entry, Manifest};
|
|
15
|
+
use crate::store::Store;
|
|
16
|
+
|
|
17
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
18
|
+
pub enum Edit {
|
|
19
|
+
/// Add or replace one named asset from an exact ordinary-file entry in the source
|
|
20
|
+
/// manifest. The source ObjectRef becomes the sole segment; no payload is read.
|
|
21
|
+
Copy {
|
|
22
|
+
name: String,
|
|
23
|
+
source_path: String,
|
|
24
|
+
media_type: String,
|
|
25
|
+
},
|
|
26
|
+
/// Remove an asset by its exact header name. Missing names are harmless, making
|
|
27
|
+
/// retries deterministic.
|
|
28
|
+
Drop { name: String },
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
32
|
+
pub struct ResultSnapshot {
|
|
33
|
+
pub header: ObjectRef,
|
|
34
|
+
pub manifest: ObjectRef,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn source_object(manifest: &Manifest, path: &str) -> Result<ObjectRef> {
|
|
38
|
+
manifest
|
|
39
|
+
.entries()
|
|
40
|
+
.iter()
|
|
41
|
+
.find_map(|(candidate, entry)| {
|
|
42
|
+
(candidate == path).then(|| match entry {
|
|
43
|
+
Entry::File(object) => Ok(object.clone()),
|
|
44
|
+
Entry::CozyTensors(_) => Err(Refusal {
|
|
45
|
+
code: Code::WRONG_TYPE,
|
|
46
|
+
detail: format!("asset source {path:?} is a CozyTensors header, not a file"),
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
.unwrap_or_else(|| {
|
|
51
|
+
Err(Refusal {
|
|
52
|
+
code: Code::OBJECT_ABSENT,
|
|
53
|
+
detail: format!("asset source {path:?} is absent from the source manifest"),
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Apply add/replace/drop edits to one existing checkpoint without reading tensor payloads.
|
|
59
|
+
///
|
|
60
|
+
/// The source manifest and header are read, and each selected source object is checked only
|
|
61
|
+
/// through its existing verification record and length. The new header and manifest are the
|
|
62
|
+
/// only bytes written. Callers that need a payload guarantee should subsequently use
|
|
63
|
+
/// [`crate::checkpoint::read_asset`], which hashes the reassembled asset once.
|
|
64
|
+
pub fn update(
|
|
65
|
+
store: &Store,
|
|
66
|
+
source_manifest: &ObjectRef,
|
|
67
|
+
edits: &[Edit],
|
|
68
|
+
) -> Result<ResultSnapshot> {
|
|
69
|
+
let source = checkpoint::load_manifest(store, source_manifest)?;
|
|
70
|
+
let header_ref = source.header().ok_or_else(|| Refusal {
|
|
71
|
+
code: Code::ATTACHMENT_CARDINALITY,
|
|
72
|
+
detail: "asset update source has no CozyTensors header".into(),
|
|
73
|
+
})?;
|
|
74
|
+
let mut header = checkpoint::load_header(store, header_ref)?;
|
|
75
|
+
let closure = checkpoint::load_closure(store, &header)?;
|
|
76
|
+
|
|
77
|
+
let mut assets: BTreeMap<String, Asset> = header.assets.drain(..).collect();
|
|
78
|
+
for edit in edits {
|
|
79
|
+
match edit {
|
|
80
|
+
Edit::Drop { name } => {
|
|
81
|
+
assets.remove(name);
|
|
82
|
+
}
|
|
83
|
+
Edit::Copy {
|
|
84
|
+
name,
|
|
85
|
+
source_path,
|
|
86
|
+
media_type,
|
|
87
|
+
} => {
|
|
88
|
+
if name.is_empty() || media_type.is_empty() {
|
|
89
|
+
return refuse(
|
|
90
|
+
Code::MISSING_FIELD,
|
|
91
|
+
"asset name and media_type are required",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
let object = source_object(&source, source_path)?;
|
|
95
|
+
// This consults only the durable record's inode/length facts. It does not
|
|
96
|
+
// open or hash the payload, which is the defining property of this operation.
|
|
97
|
+
let record = store
|
|
98
|
+
.record_valid(&object.sha256)
|
|
99
|
+
.map_err(|detail| Refusal {
|
|
100
|
+
code: Code::OBJECT_CORRUPT,
|
|
101
|
+
detail: format!("asset source {} is not verified: {detail}", object.id()),
|
|
102
|
+
})?;
|
|
103
|
+
if record.length != object.length {
|
|
104
|
+
return refuse(
|
|
105
|
+
Code::LENGTH_MISMATCH,
|
|
106
|
+
format!("asset source {} record length differs", object.id()),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
assets.insert(
|
|
110
|
+
name.clone(),
|
|
111
|
+
Asset {
|
|
112
|
+
logical_sha256: object.sha256.clone(),
|
|
113
|
+
logical_length: object.length,
|
|
114
|
+
media_type: media_type.clone(),
|
|
115
|
+
segments: vec![object],
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
header.assets = assets.into_iter().collect();
|
|
122
|
+
header.validate(&closure)?;
|
|
123
|
+
let header_object = checkpoint::put_doc(store, &header)?;
|
|
124
|
+
let manifest = checkpoint::build_manifest(&header, &header_object, &closure)?;
|
|
125
|
+
let manifest_object = store.put_manifest(&manifest)?.obj;
|
|
126
|
+
Ok(ResultSnapshot {
|
|
127
|
+
header: header_object,
|
|
128
|
+
manifest: manifest_object,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#[cfg(test)]
|
|
133
|
+
mod tests {
|
|
134
|
+
use super::*;
|
|
135
|
+
use crate::checkpoint;
|
|
136
|
+
use crate::header::{Body, Header, Part, Tensor};
|
|
137
|
+
use crate::manifest::Draft;
|
|
138
|
+
use crate::store::Fault;
|
|
139
|
+
use crate::{dtype::Dtype, registry};
|
|
140
|
+
|
|
141
|
+
fn temp() -> std::path::PathBuf {
|
|
142
|
+
std::env::temp_dir().join(format!(
|
|
143
|
+
"tensorfs-asset-update-{}",
|
|
144
|
+
crate::meta::now_nanos_unique()
|
|
145
|
+
))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn put(store: &Store, bytes: &[u8]) -> ObjectRef {
|
|
149
|
+
let expected = ObjectRef::of(bytes);
|
|
150
|
+
let mut input = bytes;
|
|
151
|
+
store
|
|
152
|
+
.put_stream(&mut input, Some(&expected), &Fault::default())
|
|
153
|
+
.unwrap()
|
|
154
|
+
.obj
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[test]
|
|
158
|
+
fn copies_asset_refs_without_changing_tensor_refs_and_read_asset_verifies_bytes() {
|
|
159
|
+
let root = temp();
|
|
160
|
+
let store = Store::init(&root).unwrap();
|
|
161
|
+
let spec = registry::seeds()
|
|
162
|
+
.into_iter()
|
|
163
|
+
.find(|seed| seed.alias == "plain/1")
|
|
164
|
+
.unwrap()
|
|
165
|
+
.spec;
|
|
166
|
+
let header = Header {
|
|
167
|
+
configs: vec![],
|
|
168
|
+
assets: vec![],
|
|
169
|
+
encodings: vec![spec.clone()],
|
|
170
|
+
components: vec![(
|
|
171
|
+
"model".into(),
|
|
172
|
+
vec![(
|
|
173
|
+
"weight".into(),
|
|
174
|
+
Tensor {
|
|
175
|
+
dtype: Dtype::U8,
|
|
176
|
+
shape: vec![3],
|
|
177
|
+
encoding: spec.object_id(),
|
|
178
|
+
parts: vec![(
|
|
179
|
+
"value".into(),
|
|
180
|
+
Part {
|
|
181
|
+
dtype: Dtype::U8,
|
|
182
|
+
shape: vec![3],
|
|
183
|
+
body: Body::Inline(vec![1, 2, 3]),
|
|
184
|
+
},
|
|
185
|
+
)],
|
|
186
|
+
},
|
|
187
|
+
)],
|
|
188
|
+
)],
|
|
189
|
+
};
|
|
190
|
+
let header_ref = put(&store, &header.canonical_bytes().unwrap());
|
|
191
|
+
let asset_bytes = b"tokenizer bytes";
|
|
192
|
+
let asset_ref = put(&store, asset_bytes);
|
|
193
|
+
let source = Draft {
|
|
194
|
+
entries: vec![
|
|
195
|
+
("model.cozytensors".into(), Entry::CozyTensors(header_ref)),
|
|
196
|
+
("tokenizer/vocab.txt".into(), Entry::File(asset_ref.clone())),
|
|
197
|
+
],
|
|
198
|
+
}
|
|
199
|
+
.seal()
|
|
200
|
+
.unwrap();
|
|
201
|
+
let source_ref = store.put_manifest(&source).unwrap().obj;
|
|
202
|
+
|
|
203
|
+
let result = update(
|
|
204
|
+
&store,
|
|
205
|
+
&source_ref,
|
|
206
|
+
&[Edit::Copy {
|
|
207
|
+
name: "tokenizer/vocab.txt".into(),
|
|
208
|
+
source_path: "tokenizer/vocab.txt".into(),
|
|
209
|
+
media_type: "text/plain".into(),
|
|
210
|
+
}],
|
|
211
|
+
)
|
|
212
|
+
.unwrap();
|
|
213
|
+
let output = checkpoint::load_header(&store, &result.header).unwrap();
|
|
214
|
+
assert_eq!(output.tensors().count(), 1);
|
|
215
|
+
assert_eq!(
|
|
216
|
+
output.tensors().next().unwrap().2.parts[0].1.body,
|
|
217
|
+
Body::Inline(vec![1, 2, 3])
|
|
218
|
+
);
|
|
219
|
+
let asset = &output.assets[0].1;
|
|
220
|
+
assert_eq!(asset.segments, vec![asset_ref]);
|
|
221
|
+
assert_eq!(asset.logical_length, asset_bytes.len() as u64);
|
|
222
|
+
let mut verified = Vec::new();
|
|
223
|
+
checkpoint::read_asset(&store, "tokenizer/vocab.txt", asset, &mut verified).unwrap();
|
|
224
|
+
assert_eq!(verified, asset_bytes);
|
|
225
|
+
std::fs::remove_dir_all(root).unwrap();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#[test]
|
|
229
|
+
fn drops_are_deterministic_and_do_not_touch_payloads() {
|
|
230
|
+
let root = temp();
|
|
231
|
+
let store = Store::init(&root).unwrap();
|
|
232
|
+
let spec = registry::seeds().into_iter().next().unwrap().spec;
|
|
233
|
+
let payload = put(&store, b"asset");
|
|
234
|
+
let header = Header {
|
|
235
|
+
configs: vec![],
|
|
236
|
+
assets: vec![(
|
|
237
|
+
"old".into(),
|
|
238
|
+
Asset {
|
|
239
|
+
logical_sha256: payload.sha256.clone(),
|
|
240
|
+
logical_length: payload.length,
|
|
241
|
+
media_type: "text/plain".into(),
|
|
242
|
+
segments: vec![payload.clone()],
|
|
243
|
+
},
|
|
244
|
+
)],
|
|
245
|
+
encodings: vec![spec.clone()],
|
|
246
|
+
components: vec![(
|
|
247
|
+
"model".into(),
|
|
248
|
+
vec![(
|
|
249
|
+
"w".into(),
|
|
250
|
+
Tensor {
|
|
251
|
+
dtype: Dtype::U8,
|
|
252
|
+
shape: vec![1],
|
|
253
|
+
encoding: spec.object_id(),
|
|
254
|
+
parts: vec![(
|
|
255
|
+
"value".into(),
|
|
256
|
+
Part {
|
|
257
|
+
dtype: Dtype::U8,
|
|
258
|
+
shape: vec![1],
|
|
259
|
+
body: Body::Inline(vec![0]),
|
|
260
|
+
},
|
|
261
|
+
)],
|
|
262
|
+
},
|
|
263
|
+
)],
|
|
264
|
+
)],
|
|
265
|
+
};
|
|
266
|
+
let href = put(&store, &header.canonical_bytes().unwrap());
|
|
267
|
+
let source = Draft {
|
|
268
|
+
entries: vec![("model.cozytensors".into(), Entry::CozyTensors(href))],
|
|
269
|
+
}
|
|
270
|
+
.seal()
|
|
271
|
+
.unwrap();
|
|
272
|
+
let source_ref = store.put_manifest(&source).unwrap().obj;
|
|
273
|
+
let one = update(
|
|
274
|
+
&store,
|
|
275
|
+
&source_ref,
|
|
276
|
+
&[Edit::Drop {
|
|
277
|
+
name: "missing".into(),
|
|
278
|
+
}],
|
|
279
|
+
)
|
|
280
|
+
.unwrap();
|
|
281
|
+
let two = update(
|
|
282
|
+
&store,
|
|
283
|
+
&source_ref,
|
|
284
|
+
&[Edit::Drop {
|
|
285
|
+
name: "missing".into(),
|
|
286
|
+
}],
|
|
287
|
+
)
|
|
288
|
+
.unwrap();
|
|
289
|
+
assert_eq!(one.header, two.header);
|
|
290
|
+
assert_eq!(one.manifest, two.manifest);
|
|
291
|
+
std::fs::remove_dir_all(root).unwrap();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
@@ -591,6 +591,43 @@ impl PyStore {
|
|
|
591
591
|
tensorfs_core::IMPLEMENTATION_SHA256
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
+
/// Rewrite only a CozyTensors header/manifest to add, replace, or drop model assets.
|
|
595
|
+
/// `copies` are `(name, source_path, media_type)` rows selected from the exact source
|
|
596
|
+
/// manifest; TensorFS reuses their ObjectRefs without reading payload bytes.
|
|
597
|
+
#[pyo3(signature = (manifest_id, manifest_length, copies=Vec::new(), drops=Vec::new()))]
|
|
598
|
+
fn update_model_assets<'p>(
|
|
599
|
+
&self,
|
|
600
|
+
py: Python<'p>,
|
|
601
|
+
manifest_id: &str,
|
|
602
|
+
manifest_length: u64,
|
|
603
|
+
copies: Vec<(String, String, String)>,
|
|
604
|
+
drops: Vec<String>,
|
|
605
|
+
) -> PyResult<Bound<'p, PyDict>> {
|
|
606
|
+
let source = obj_ref(py, manifest_id, manifest_length)?;
|
|
607
|
+
let mut edits = Vec::with_capacity(copies.len() + drops.len());
|
|
608
|
+
edits.extend(copies.into_iter().map(|(name, source_path, media_type)| {
|
|
609
|
+
tensorfs_core::asset_update::Edit::Copy {
|
|
610
|
+
name,
|
|
611
|
+
source_path,
|
|
612
|
+
media_type,
|
|
613
|
+
}
|
|
614
|
+
}));
|
|
615
|
+
edits.extend(
|
|
616
|
+
drops
|
|
617
|
+
.into_iter()
|
|
618
|
+
.map(|name| tensorfs_core::asset_update::Edit::Drop { name }),
|
|
619
|
+
);
|
|
620
|
+
let result = py
|
|
621
|
+
.detach(|| tensorfs_core::asset_update::update(&self.store, &source, &edits))
|
|
622
|
+
.or_refuse(py)?;
|
|
623
|
+
let out = PyDict::new(py);
|
|
624
|
+
out.set_item("header_id", result.header.id())?;
|
|
625
|
+
out.set_item("header_length", result.header.length)?;
|
|
626
|
+
out.set_item("manifest_id", result.manifest.id())?;
|
|
627
|
+
out.set_item("manifest_length", result.manifest.length)?;
|
|
628
|
+
Ok(out)
|
|
629
|
+
}
|
|
630
|
+
|
|
594
631
|
fn verify_checkpoint_source(
|
|
595
632
|
&self,
|
|
596
633
|
py: Python<'_>,
|
|
@@ -484,6 +484,13 @@ class Store:
|
|
|
484
484
|
def prepare_readers(self) -> None: ...
|
|
485
485
|
def complete_cozytensors_manifests(self) -> list[str]: ...
|
|
486
486
|
def contains(self, hex: str) -> bool: ...
|
|
487
|
+
def update_model_assets(
|
|
488
|
+
self,
|
|
489
|
+
manifest_id: str,
|
|
490
|
+
manifest_length: int,
|
|
491
|
+
copies: Sequence[tuple[str, str, str]] = ...,
|
|
492
|
+
drops: Sequence[str] = ...,
|
|
493
|
+
) -> dict[str, str | int]: ...
|
|
487
494
|
def objects(self) -> list[str]: ...
|
|
488
495
|
def reap_holds(self) -> list[str]: ...
|
|
489
496
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/examples/optional-cache-backfill.md
RENAMED
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/examples/resident_conversion_profile.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor
RENAMED
|
File without changes
|
{tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json
RENAMED
|
File without changes
|
{tensorfs-0.3.44 → tensorfs-0.3.45}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|