|
| 1 | +# Embedding Engine Phase 1 |
| 2 | + |
| 3 | +Status: Implemented locally |
| 4 | + |
| 5 | +## Decision |
| 6 | + |
| 7 | +Pressure: embedding inference was serialized around complete project calls, |
| 8 | +including lexical work, zvec writes, ranking, and state fsync. Bulk shared-memory |
| 9 | +and document transactions also submitted one zvec upsert call per record. Model |
| 10 | +selection remained a set of raw environment fields without a discoverable, |
| 11 | +daemon-owned profile catalog. |
| 12 | + |
| 13 | +Decision: keep llama.cpp inference globally serialized, but move the lock to the |
| 14 | +actual query/passage embedding calls. Batch zvec upserts and shared-memory |
| 15 | +commits without weakening the durable pending journal. Add an immutable catalog |
| 16 | +of stable, preview, and unsupported profiles, and expose profile listing plus a |
| 17 | +non-mutating switch preflight. |
| 18 | + |
| 19 | +Language form: Rust value types and exhaustive Protobuf enum additions for the |
| 20 | +closed profile-control methods; one shared TypeScript model-control facade for |
| 21 | +the OpenCode tools. |
| 22 | + |
| 23 | +Ownership: the daemon owns profile support decisions and preflight. Project |
| 24 | +actors remain the only storage writers. `MemoryEngine` owns its collection and |
| 25 | +embedder, while the daemon registry injects the shared inference lock. The |
| 26 | +plugin never constructs model repository configurations. |
| 27 | + |
| 28 | +Alternatives: changing model environment variables or rebuilding `zvec/` in |
| 29 | +place was rejected because incompatible vectors could mix or make an existing |
| 30 | +project unreadable. A public mutating switch was deferred because collection |
| 31 | +generations, an active pointer, a durable switch journal, mutation freeze, |
| 32 | +resume, cancellation, and rollback are not implemented yet. |
| 33 | + |
| 34 | +Costs: profile metadata is duplicated from reviewed upstream model information |
| 35 | +until an artifact-lock file is introduced. Preview profiles are visible but not |
| 36 | +selectable. Actual model cutover remains a later generation-migration phase. |
| 37 | + |
| 38 | +## Invariants |
| 39 | + |
| 40 | +1. At most one llama.cpp embedding call runs at a time across project actors. |
| 41 | +2. Lexical search, ranking, zvec flush, and state fsync do not hold inference |
| 42 | + capacity. |
| 43 | +3. One pending upsert batch produces one zvec upsert call and one flush. |
| 44 | +4. Shared-memory replacements are deleted only after successor writes commit. |
| 45 | +5. Profile listing and dry-run preflight do not initialize `MemoryEngine` or |
| 46 | + load a model. |
| 47 | +6. Only `qwen3-text-4b-q4` is selectable in phase 1. |
| 48 | +7. Qwen3-VL profiles remain unsupported until runtime, artifact, quality, |
| 49 | + portability, and memory gates pass. |
| 50 | +8. A non-dry-run switch is rejected before any project mutation. |
| 51 | + |
| 52 | +## Built-In Profiles |
| 53 | + |
| 54 | +| Profile | Support | Runtime | Phase-1 behavior | |
| 55 | +| ----------------------- | ----------- | ------------------------------ | -------------------------- | |
| 56 | +| `qwen3-text-4b-q4` | Stable | llama.cpp GGUF | Current/default profile | |
| 57 | +| `qwen3-text-0.6b-q8` | Preview | llama.cpp GGUF | Visible, preflight blocked | |
| 58 | +| `qwen3-text-8b-q4` | Preview | llama.cpp GGUF | Visible, preflight blocked | |
| 59 | +| `bge-m3` | Preview | Unvalidated | Visible, unsupported | |
| 60 | +| `nomic-embed-text-v1.5` | Preview | Unvalidated | Visible, unsupported | |
| 61 | +| `qwen3-vl-embedding-2b` | Unsupported | No packaged multimodal runtime | Visible, unsupported | |
| 62 | +| `qwen3-vl-embedding-8b` | Unsupported | No packaged multimodal runtime | Visible, unsupported | |
| 63 | + |
| 64 | +The Qwen GGUF presets include pinned repository revisions and LFS SHA-256 |
| 65 | +digests. They remain non-selectable until retrieval-quality gates and the |
| 66 | +generation migration are implemented. |
| 67 | + |
| 68 | +The next phase adds legacy-root generation adaptation, managed generation |
| 69 | +manifests, an atomic active pointer, and generation-aware pending journals |
| 70 | +before any mutating switch command is enabled. |
| 71 | + |
| 72 | +## Protobuf Boundary |
| 73 | + |
| 74 | +The live memory wire contract remains in |
| 75 | +`schema/opencode/memory/v1/memory.proto`; its released `Method`, `Request`, and |
| 76 | +`Response` tags are unchanged. Model control is in |
| 77 | +`schema/opencode/memory/model/v1/model.proto` with typed request/response |
| 78 | +`oneof`s. `daemon.proto` adds new model branches without changing the existing |
| 79 | +memory branch. Memory and model requests are validated as exactly one domain at |
| 80 | +daemon admission, and the domain schema generation is incremented to `2` so an |
| 81 | +older daemon cannot silently ignore a model branch. |
| 82 | + |
| 83 | +The split follows the Protocol Buffers best-practice rules: new model messages |
| 84 | +use fresh field numbers, old memory model-method numbers are reserved, enums |
| 85 | +start with an `UNSPECIFIED` zero value, model operations use `oneof` rather than |
| 86 | +an open method/payload pair, and optional fields preserve presence for nullable |
| 87 | +profile metadata. The two domain files are intentionally grouped API surfaces |
| 88 | +for this two-file packaging boundary; storage state remains represented by Rust |
| 89 | +domain types and is not coupled to the RPC messages. |
0 commit comments