Repository navigation
feat: local embedding offline benchmark runner (#128) #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1b9ed31
c2b1237
8c8f9c8
db9c018
3527d9d
4ba5b7c
1d805c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "models": { | ||
| "baseline": "pending_corpus", | ||
| "primary": "pending_corpus" | ||
| }, | ||
| "result": "pending_corpus", | ||
| "schemaVersion": "context-engine-embedding-benchmark-frozen-result-v1" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Offline evaluation tools and deterministic judge contracts.""" |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,60 @@ | ||||||
| { | ||||||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||||||
| "$id": "https://context-engine.invalid/eval/embedding-benchmark/input.schema.json", | ||||||
| "title": "ContextEngine offline embedding benchmark input v1", | ||||||
| "description": "M1 assumes one trusted local operator. The colocated digest detects accidental edits, truncation, or re-serialization performed without relocking; it is not a defense against deliberate forgery because an operator can recompute the digest. If callers or runners become remote or untrusted, signing requires its own ADR.", | ||||||
| "type": "object", | ||||||
| "additionalProperties": false, | ||||||
| "required": ["schemaVersion", "lock", "documents", "cases"], | ||||||
| "properties": { | ||||||
| "schemaVersion": {"const": "context-engine-embedding-benchmark-input-v1"}, | ||||||
| "lock": { | ||||||
| "type": "object", | ||||||
| "additionalProperties": false, | ||||||
| "required": ["profile", "contentDigest"], | ||||||
| "properties": { | ||||||
| "profile": { | ||||||
| "description": "Typed accidental-edit detector for one trusted local operator; not a tamper-proof or anti-forgery signature.", | ||||||
| "const": "sha256-rfc8785-accidental-edit-detection-v1" | ||||||
| }, | ||||||
| "contentDigest": {"type": "string", "pattern": "^[0-9a-f]{64}$"} | ||||||
| } | ||||||
| }, | ||||||
| "documents": { | ||||||
| "type": "array", | ||||||
| "minItems": 1, | ||||||
| "items": { | ||||||
| "type": "object", | ||||||
| "additionalProperties": false, | ||||||
| "required": ["documentRef", "text"], | ||||||
| "properties": { | ||||||
| "documentRef": {"$ref": "#/$defs/nonblank"}, | ||||||
| "text": {"$ref": "#/$defs/nonblank"} | ||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "cases": { | ||||||
| "type": "array", | ||||||
| "minItems": 1, | ||||||
| "items": { | ||||||
| "type": "object", | ||||||
| "additionalProperties": false, | ||||||
| "required": ["caseRef", "query", "expectedDocumentRefs", "slice"], | ||||||
| "properties": { | ||||||
| "caseRef": {"type": "string", "pattern": "^[a-z][a-z0-9_-]{0,127}$"}, | ||||||
| "query": {"$ref": "#/$defs/nonblank"}, | ||||||
| "expectedDocumentRefs": { | ||||||
| "type": "array", | ||||||
| "minItems": 1, | ||||||
| "uniqueItems": true, | ||||||
| "items": {"$ref": "#/$defs/nonblank"} | ||||||
| }, | ||||||
| "slice": {"$ref": "#/$defs/nonblank"} | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "$defs": { | ||||||
| "nonblank": {"type": "string", "minLength": 1, "pattern": ".*\\S.*"} | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🐛 Proposed fix- "nonblank": {"type": "string", "minLength": 1, "pattern": ".*\\S.*"}
+ "nonblank": {"type": "string", "minLength": 1, "pattern": "(?s).*\\S.*"}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| { | ||
| "schemaVersion": "context-engine-embedding-model-registry-v1", | ||
| "models": { | ||
| "primary": { | ||
| "backend": "sentence-transformers", | ||
| "artifacts": [ | ||
| {"path": "1_Pooling/config.json", "sha256": "37bf193fa101f19101bfad9c31d3eb0f786e247b7b1e5cb7f007d730eed1ddbd"}, | ||
| {"path": "config.json", "sha256": "b5bf1f51fc45be473a54718cef92448d90a1be001bf9b9a44b8c7f10a19feaa9"}, | ||
| {"path": "config_sentence_transformers.json", "sha256": "10667c72ddb772627bf1780cb7f86af8e2ae0032b8c243c731172064105c6961"}, | ||
| {"path": "generation_config.json", "sha256": "28396d421a2108acce96383f6a7de78008f7f1b17f807958f3c14c51dbfb65fb"}, | ||
| {"path": "merges.txt", "sha256": "8831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5"}, | ||
| {"path": "model.safetensors", "sha256": "0437e45c94563b09e13cb7a64478fc406947a93cb34a7e05870fc8dcd48e23fd"}, | ||
| {"path": "modules.json", "sha256": "84e40c8e006c9b1d6c122e02cba9b02458120b5fb0c87b746c41e0207cf642cf"}, | ||
| {"path": "tokenizer.json", "sha256": "def76fb086971c7867b829c23a26261e38d9d74e02139253b38aeb9df8b4b50a"}, | ||
| {"path": "tokenizer_config.json", "sha256": "253153d0738ceb4c668d2eff957714dd2bea0b56de772a9fdccd96cbf517e6a0"}, | ||
| {"path": "vocab.json", "sha256": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910"} | ||
| ], | ||
| "identity": { | ||
| "modelId": "Qwen/Qwen3-Embedding-0.6B", | ||
| "revision": "97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3", | ||
| "artifactDigest": "8cb25677d5be69ce6ac88ebbdfb5dad30980fee39c35c6324a583e325917eddc", | ||
| "dimension": 384, | ||
| "pooling": "last_token", | ||
| "queryPrefix": "Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:", | ||
| "documentPrefix": "", | ||
| "transformationPipeline": "l2 -> truncate 1024->384 -> l2", | ||
| "precision": "float32", | ||
| "batchSize": 8 | ||
| } | ||
| }, | ||
| "baseline": { | ||
| "backend": "sentence-transformers", | ||
| "artifacts": [ | ||
| {"path": "1_Pooling/config.json", "sha256": "987f7a67a38fa564c849bb5d277c52ab9088a84368fc0be31a354125aebb12a0"}, | ||
| {"path": "config.json", "sha256": "69137736cab8b8903a07fe8afaafdda25aac55415a12a55d1bffa9f581abf959"}, | ||
| {"path": "model.safetensors", "sha256": "1a55775f53449dac10a2bcbc312469fac40b96d53198c407081a831f81c98477"}, | ||
| {"path": "modules.json", "sha256": "c6e29747481e8b5dd2b58401966aeac910de39092f90cda9a704b1545f902b04"}, | ||
| {"path": "sentence_bert_config.json", "sha256": "948201d8329907aae938fa62f9ceeed53f5694dacc2b87b9f3b78b37ee986529"}, | ||
| {"path": "sentencepiece.bpe.model", "sha256": "cfc8146abe2a0488e9e2a0c56de7952f7c11ab059eca145a0a727afce0db2865"}, | ||
| {"path": "special_tokens_map.json", "sha256": "d05497f1da52c5e09554c0cd874037a083e1dc1b9cfd48034d1c717f1afc07a7"}, | ||
| {"path": "tokenizer.json", "sha256": "0b44a9d7b51c3c62626640cda0e2c2f70fdacdc25bbbd68038369d14ebdf4c39"}, | ||
| {"path": "tokenizer_config.json", "sha256": "a1d6bc8734a6f635dc158508bef000f8e2e5a759c7d92f984b2c86e5ff53425b"} | ||
| ], | ||
| "identity": { | ||
| "modelId": "intfloat/multilingual-e5-small", | ||
| "revision": "614241f622f53c4eeff9890bdc4f31cfecc418b3", | ||
| "artifactDigest": "961e0953328daf4df7916c1406751c5c63fe19e14a5c7e977d9f8b38964c5cf3", | ||
| "dimension": 384, | ||
| "pooling": "mean", | ||
| "queryPrefix": "query: ", | ||
| "documentPrefix": "passage: ", | ||
| "transformationPipeline": "l2 -> keep native 384 -> l2", | ||
| "precision": "float32", | ||
| "batchSize": 8 | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "$id": "https://context-engine.invalid/eval/embedding-benchmark/model-registry.schema.json", | ||
| "title": "ContextEngine offline embedding tracked model registry v1", | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["schemaVersion", "models"], | ||
| "properties": { | ||
| "schemaVersion": {"const": "context-engine-embedding-model-registry-v1"}, | ||
| "models": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["primary", "baseline"], | ||
| "properties": { | ||
| "primary": {"$ref": "#/$defs/model"}, | ||
| "baseline": {"$ref": "#/$defs/model"} | ||
| } | ||
| } | ||
| }, | ||
| "$defs": { | ||
| "digest": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, | ||
| "identity": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["modelId", "revision", "artifactDigest", "dimension", "transformationPipeline", "pooling", "queryPrefix", "documentPrefix", "precision", "batchSize"], | ||
| "properties": { | ||
| "modelId": {"type": "string", "minLength": 1}, | ||
| "revision": {"type": "string", "pattern": "^(?:[0-9a-f]{40}|[0-9a-f]{64})$"}, | ||
| "artifactDigest": {"$ref": "#/$defs/digest"}, | ||
| "dimension": {"const": 384}, | ||
| "transformationPipeline": { | ||
| "description": "Complete ordered vector transformation, including upstream normalization, dimensional reduction, and final normalization. Both supported pipelines emit unit-norm 384-dimensional vectors.", | ||
| "enum": ["l2 -> truncate 1024->384 -> l2", "l2 -> keep native 384 -> l2"] | ||
| }, | ||
| "pooling": {"type": "string", "minLength": 1}, | ||
| "queryPrefix": {"type": "string"}, | ||
| "documentPrefix": {"type": "string"}, | ||
| "precision": {"type": "string", "minLength": 1}, | ||
| "batchSize": {"type": "integer", "minimum": 1} | ||
| } | ||
| }, | ||
| "model": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["backend", "artifacts", "identity"], | ||
| "properties": { | ||
| "backend": {"const": "sentence-transformers"}, | ||
| "artifacts": { | ||
| "description": "The exact complete local snapshot consumed by SentenceTransformer. Every required config, tokenizer, pooling, and weight file is present and digest-bound; extra or missing files are refused.", | ||
| "type": "array", | ||
| "minItems": 1, | ||
| "items": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["path", "sha256"], | ||
| "properties": { | ||
| "path": {"type": "string", "minLength": 1}, | ||
| "sha256": {"$ref": "#/$defs/digest"} | ||
| } | ||
| } | ||
| }, | ||
| "identity": {"$ref": "#/$defs/identity"} | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Record the benchmark trust-boundary decisions in an ADR.
The local-operator threat model, digest-lock limitation, and fail-closed judge dependency need a decision record under
docs/decisions/, rather than only README prose. As per coding guidelines: “Record every non-obvious architectural decision as an ADR under docs/decisions/.”🤖 Prompt for AI Agents
Source: Coding guidelines