Skip to content

feat(localembed): pin local embedding models to a resolved revision, load offline - #7

Open
eshanclio wants to merge 12 commits into
ieshan:mainfrom
eshanclio:feat/pinned-model-revision
Open

eshanclio wants to merge 12 commits into
ieshan:mainfrom
eshanclio:feat/pinned-model-revision

Conversation

@eshanclio

@eshanclio eshanclio commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a pin file (codamigo-pin.json) recording the resolved upstream
commit for a downloaded local embedding model, and a loopback info shim
so a pinned model loads without any network access — go-huggingface has
no offline mode and otherwise makes one mandatory revision-lookup call per
load.

  • download-model resolves and pins the exact commit, verifying every
    file against a per-file checksum for built-in models.
  • Loading a pinned model answers go-huggingface's revision lookup from the
    pin file via a 127.0.0.1 shim, alive only for the span of the load.
  • A registry model's compiled-in revision is authoritative: a pin file can
    never redirect it, and a mismatch is reported rather than silently
    resolved.
  • Model directories predating the pin file still load offline by deriving
    the revision from go-huggingface's own cached info, no re-download.
  • doctor and download-model report the resolved revision, real
    embedding dimensions, and superseded snapshot sizes (never deleted
    automatically).
  • An unpinned repository id (e.g. google/embeddinggemma-300m) now
    discovers its own extra module files instead of silently missing them.

    standardManifest only ever covered the plain Transformer+Pooling pair,
    so a model that adds a Dense projection or Normalize module downloaded
    "successfully" while still missing a file transformer.LoadModel needed
    — surfacing later as a confusing shim 404 ("model not downloaded" for a
    model whose declared manifest was fully present). expandManifest now
    reads the model's own modules.json — fetched ahead of the main loop in
    Download, read from the resolved snapshot with no network access in
    ResolvePin — and folds in whatever extra files it declares, using the
    sizes/hashes the repository info already reports. Registry models are
    untouched; only a raw repository id, which has no hand-verified manifest
    to begin with, gets this treatment.
  • Dead-code cleanup in infoshim.go (an unreachable ErrServerClosed
    filter and a conditional whose body was _ = err) and an idiom fix in
    pin.go (errors.New instead of fmt.Errorf with no verbs).
  • .gitignore: also ignore cmd/codamigo/codamigo, the binary left by a
    bare go build ./cmd/codamigo/ run from inside that directory.

Testing

  • make fmt, make vet, golangci-lint run ./... (0 issues), gosec,
    nilaway all clean (pre-existing nilaway finding in
    infoshim_test.go is unrelated and present on the base branch too).
  • make test and make test-race pass across all packages.
  • make build succeeds.
  • New coverage: TestExpandManifest_* (unit), TestDownload_DiscoversExtraModuleFiles
    (download-time, fake HF server), TestResolvePin_DiscoversDenseModuleFromDisk
    (load-time, offline).

…check

SnapshotDir now requires a concrete revision. Download was still handing it
opts.Model unresolved for unpinned repositories, which made existingFile
silently treat every file as missing on a second run.
…ME polish

- infoshim.go: remove the unreachable ErrServerClosed filter in Close and
  the dead conditional body in the Serve goroutine (both provably
  unreachable per net/http's Server.Close semantics); drop the now-unused
  "errors" import.
- pin.go: use errors.New instead of fmt.Errorf with no format verbs, to
  match the identical guard in download.go and cache.go.
- .gitignore: also ignore cmd/codamigo/codamigo (a `go build ./cmd/codamigo/`
  run from inside that directory leaves the binary there, outside the
  root-anchored /codamigo pattern that already covers `make build`'s
  output). Anchored deliberately so it cannot also match the cmd/codamigo
  source directory.
- README.md: minor doc updates for the pinned-revision behavior.
…modules.json declares

standardManifest only ever fetched the plain Transformer+Pooling pair
(7 files). A raw repository id such as google/embeddinggemma-300m adds
a Dense projection module ("2_Dense") and a Normalize step, each with
its own files, that standardManifest has no idea exist.

Download would therefore report success while transformer.LoadModel
still needed a file it never fetched, and MissingFiles would report
the model ready when it was not — the failure only ever surfaced late,
as a loopback-shim 404 during New, i.e. "model not downloaded" for a
model whose declared manifest was in fact fully present on disk.

expandManifest (localembed/model.go) closes the gap for an unpinned
repository id (registry models keep their hand-verified manifest
untouched):

- Download fetches modules.json ahead of the main loop, parses it, and
  folds in any extra module's files using the sizes/hashes already
  present in the repository info, before the loop decides what counts
  as "downloaded".
- ResolvePin does the load-time equivalent with no network access,
  reading modules.json off the resolved snapshot directory, so
  MissingFiles catches a genuinely incomplete download before New ever
  starts talking to go-huggingface.

Also updates the design doc's "Known limitations" entry, which called
this out as deliberately deferred.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant