Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 37 additions & 0 deletions app/electron/agent/engine/library.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,43 @@ try {
const large = await lowMemory.scan()
assert.equal(large.models.every((model) => !model.loadable), true)


// AppleDouble companions (._file) are resource forks, never checkpoint assets.
const adModels = path.join(root, 'AppleDouble Models')
const adSharded = path.join(adModels, 'sharded-checkpoint')
await fs.mkdir(adSharded, { recursive: true })
await fs.writeFile(path.join(adSharded, 'config.json'), JSON.stringify({ model_type: 'qwen3_5', quantization: { bits: 4, group_size: 64 } }))
await fs.writeFile(path.join(adSharded, 'tokenizer.json'), JSON.stringify({ model: { type: 'BPE', vocab: {} } }))
await fs.writeFile(path.join(adSharded, 'model-00001-of-00002.safetensors'), weights())
await fs.writeFile(path.join(adSharded, 'model-00002-of-00002.safetensors'), weights())
// Stray invalid companions beside the real shards: not counted as shards, weights, or metadata.
await fs.writeFile(path.join(adSharded, '._model-00001-of-00002.safetensors'), Buffer.alloc(4096, 0xff))
await fs.writeFile(path.join(adSharded, '._model-00002-of-00002.safetensors'), Buffer.alloc(4096, 0xff))
const adLib = library({ appHome: path.join(root, 'appledouble-app') })
const adFound = await adLib.addFolder(adModels)
const adModel = adFound.models.find((model) => model.path === adSharded)!
assert.equal(adModel.complete, true, adModel.reason ?? '')
assert.equal(adModel.loadable, true)
assert.equal(adModel.weightsBytes, 2 * weights().length, 'companions add nothing to the weight total')
assert.equal(adModel.memoryFloorBytes, 2 * weights().length + 2 * 1024 ** 3)
assert.equal((await adLib.resolve(adModel.id)).path, adSharded)

// A stray companion in the selected parent must not stop discovery of the nested checkpoint below it.
const nested = path.join(adModels, 'nested', 'real-checkpoint')
await fs.mkdir(nested, { recursive: true })
await checkpoint(nested)
await fs.writeFile(path.join(adModels, '._old.safetensors'), Buffer.alloc(4096, 0xff))
const rediscovered = await library({ appHome: path.join(root, 'appledouble-nested-app') }).addFolder(adModels)
assert.equal(rediscovered.models.find((model) => model.path === nested)?.loadable, true, 'a stray parent companion does not block nested discovery')
assert.equal(rediscovered.models.some((model) => model.path === adModels), false, 'the parent is not mistaken for a checkpoint')

// A real corrupt shard still fails the checkpoint.
const corruptShard = path.join(adModels, 'corrupt-shard')
await checkpoint(corruptShard)
await fs.truncate(path.join(corruptShard, 'model.safetensors'), 12)
const corruptShardFound = await library({ appHome: path.join(root, 'appledouble-corrupt-app') }).addFolder(adModels)
assert.match(corruptShardFound.models.find((model) => model.path === corruptShard)!.reason!, /safetensors/)

const validRegistry = await fs.readFile(lib.file, 'utf8')
await fs.writeFile(lib.file, '{')
await assert.rejects(() => library().snapshot(), /library is invalid/)
Expand Down
7 changes: 5 additions & 2 deletions app/electron/agent/engine/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ export class ModelLibrary {
} finally { await handle.close() }
}

private async inspect(root: Root, directory: string, names: string[], caps: EngineCapabilities, budget: ScanBudget, repo: string | null = null, revision: string | null = null, blobRoot: string | null = null): Promise<{ model: EngineLibraryModel; fingerprint: string }> {
private async inspect(root: Root, directory: string, rawNames: string[], caps: EngineCapabilities, budget: ScanBudget, repo: string | null = null, revision: string | null = null, blobRoot: string | null = null): Promise<{ model: EngineLibraryModel; fingerprint: string }> {
// AppleDouble companions (._file) hold resource forks, never checkpoint assets, so drop them before inspection.
const names = rawNames.filter((name) => !name.startsWith('._'))
const model: EngineLibraryModel = { id: `local/${digest(directory)}`, repo, title: repo ?? path.basename(directory), path: directory, source: blobRoot ? 'huggingface' : 'folder', folderId: root.id, revision, modelType: null, format: null, complete: false, compatible: false, loadable: false, reason: null, weightsBytes: null, memoryFloorBytes: null, fitsMemory: null }
let fingerprint = directory
try {
Expand Down Expand Up @@ -298,7 +300,8 @@ export class ModelLibrary {
visited.add(directory)
if (await fs.realpath(directory) !== directory || !(await fs.lstat(directory)).isDirectory()) throw new Error('A model search folder is missing or has been redirected; select it again.')
const entries = await this.list(directory, budget)
if (entries.some((entry) => entry.name === 'config.json' || /\.(safetensors|gguf|bin|pt|pth)$/i.test(entry.name))) { await collect(root, directory, entries); return }
const assets = entries.filter((entry) => !entry.name.startsWith('._'))
if (assets.some((entry) => entry.name === 'config.json' || /\.(safetensors|gguf|bin|pt|pth)$/i.test(entry.name))) { await collect(root, directory, assets); return }
for (const entry of entries) {
this.check(budget)
if (!entry.isDirectory() || entry.isSymbolicLink() || entry.name.startsWith('.') || ['blobs', 'node_modules', 'venv', '__pycache__'].includes(entry.name)) continue
Expand Down
Loading