You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a reproducible benchmark suite for measuring embedding model quality for Codira.
Current embedding campaigns measure latency, memory, backend overhead, and indexing cost well. They do not yet measure whether a given embedding model retrieves better context for Codira-specific development tasks.
This issue adds a versioned benchmark for retrieval quality, so Codira can decide whether larger or more expensive embedding models are actually worth their runtime and memory cost.
The benchmark should answer:
Given the same repository, commit, chunking configuration, backend, and query set, does model A retrieve more useful Codira context than model B?
It should not answer the vague question:
Is this embedding model better in general?
Codira should select embedding models based on measured retrieval quality under Codira workloads, not on vector dimensionality, model reputation, or generic leaderboard scores alone.
Motivation
Recent performance campaigns show that smaller models are materially cheaper than larger ones.
For example, the current measured default candidate remains:
```text
bge-small-en-v1.5-onnx + sqlite
```
because it has strong interactive ctx / emb performance, modest memory use, and avoids the cost of larger 768-dimensional models.
However, there are indications that some larger models may produce “better” semantic retrieval. That claim is currently not measured in a Codira-specific, reproducible way.
This benchmark should make the tradeoff explicit:
```text
quality gain vs latency cost vs memory cost vs indexing cost
```
A larger model should only become a default, or a recommended quality profile, if it produces a measurable retrieval-quality improvement large enough to justify its cost.
The first benchmark should isolate embedding model retrieval quality as much as possible.
Background terminology
Codira does not need to expose all IR terminology to users, but the benchmark implementation should be compatible with standard ranked-retrieval metrics.
Useful concepts:
Hit@K: whether at least one relevant result appears in the top K.
Recall@K: how many known relevant results appear in the top K.
MRR@K: how early the first relevant result appears.
nDCG@K: ranking quality when relevance has graded scores, for example 3, 2, 1, 0.
MAP: mean average precision; standard IR metric, but not required for the first implementation.
MTEB: a broad embedding benchmark suite; useful context, but not a Codira-specific decision mechanism.
BEIR: a retrieval benchmark suite; closer in spirit to Codira, but still not a substitute for Codira-specific evaluation.
TREC-style pooling: collect top results from multiple systems/models, deduplicate them, and manually judge the pooled unique results once.
For the first implementation, Codira should focus on:
```text
Hit@1
Recall@5
MRR@5 or MRR@10
optional nDCG@10
```
Core design principle
Benchmark Codira retrieval tasks, not raw model quality.
A benchmark case is not just a query string. It is:
Different repositories should have different query text, but the same benchmark taxonomy.
Bad approach:
```text
Run the exact same query strings against every repository.
```
Good approach:
```text
Run the same categories of queries against every repository,
with repo-specific query text and repo-specific gold labels.
```
Example:
Intent
codira query
chatops query
symbol_lookup
DeclarationArtifact
ChatConversation
architecture_lookup
how does the backend plugin lifecycle work?
how does the processing pipeline work?
task_lookup
where would I change embedding vector persistence?
where would I change duplicate detection?
docs_lookup
how are analyzers configured?
how are ADRs created?
The benchmark should compare models on equivalent task types, not on identical query strings.
Initial benchmark scope
Use a small but structured first suite.
Recommended first version:
```text
4 repositories
5 query categories
5 queries per category per repository
= 100 benchmark cases
```
Suggested initial repositories:
```text
codira
chatops
fontshow
sanikey
```
The exact list should come from the existing benchmark repository manifest where possible.
The benchmark should use fixed commits. If a repository changes during a campaign, the report must flag it and either exclude it from before/after conclusions or report it separately.
These cases can often be generated semi-automatically from the symbol table.
2. architecture_lookup
Natural-language queries about where an architectural concept is implemented or documented.
Examples:
```text
how does codira select the analyzer for a file?
how does the backend plugin lifecycle work?
how does the processing pipeline work?
where is stable conversation fingerprinting defined?
```
3. task_lookup
Queries phrased as development tasks.
Examples:
```text
where would I change how pending embedding rows are flushed?
what code would I touch to add markdown frontmatter support?
where would I change duplicate detection?
where is keyboard layout data generated?
```
4. docs_lookup
Queries where the relevant answer may be in documentation rather than code.
Examples:
```text
how are analyzers configured?
how are ADRs created?
how is the embedding backend configured?
what is the documented storage backend policy?
```
5. error_or_trace_lookup
Queries based on error messages, failure symptoms, or stack trace fragments.
Examples:
```text
PRIMARY KEY or UNIQUE constraint violation duplicate key
unrecognized arguments benchmarks/bk-cpp.local.json
EXPORT_SCHEMA_ERROR missing role
DuckDB executemany semgrep finding
```
```text
current default candidate
best 384-dimensional ONNX candidate
one or more 768-dimensional ONNX candidates
sentence-transformers baseline if still relevant
```
The benchmark should report model metadata:
```text
model name
engine
dimension
backend
index latency
query latency
memory/RSS
quality metrics
```
Metrics
Compute at least:
```text
Hit@1
Recall@5
MRR@5
MRR@10
```
Optionally:
```text
Recall@10
nDCG@5
nDCG@10
MAP
```
The first report should not depend on MAP.
MAP can be added later if useful.
Aggregation strategy
Do not report only one global number.
Report:
1. Per-repository metrics
Example:
Model
Repo
Hit@1
Recall@5
MRR@10
nDCG@10
bge-small-en-v1.5-onnx
codira
...
...
...
...
bge-small-en-v1.5-onnx
chatops
...
...
...
...
This shows whether a model only wins on one repository.
2. Per-intent metrics
Example:
Model
Intent
Hit@1
Recall@5
MRR@10
nDCG@10
bge-small-en-v1.5-onnx
symbol_lookup
...
...
...
...
bge-small-en-v1.5-onnx
architecture_lookup
...
...
...
...
bge-small-en-v1.5-onnx
task_lookup
...
...
...
...
bge-small-en-v1.5-onnx
docs_lookup
...
...
...
...
This is likely more important than the global score.
3. Macro average
Each repository gets equal weight.
```text
macro_avg = mean(score_per_repo)
```
This prevents a larger repository or a repository with more cases from dominating the final decision.
4. Micro average
Each query gets equal weight.
```text
micro_avg = total successful results / total evaluated cases
```
This is useful as a secondary number.
The report should clearly label macro and micro averages.
Decision gates
A larger or slower model should only be recommended if it shows a material quality gain.
Initial suggested gate:
```text
A more expensive model may become the default only if:
Recall@5 improves by at least +5 percentage points, or
MRR@10 improves by at least +0.05 absolute, or
nDCG@10 improves by at least +0.05 absolute,
while:
ctx latency does not regress beyond 1.25x,
emb latency does not regress beyond 1.25x,
max RSS remains within the configured memory budget,
full-index cost remains acceptable for the target profile.
```
If a larger model improves quality but violates default latency/memory constraints, it may be classified as:
```text
quality profile
```
instead of:
```text
default profile
```
Possible decisions:
Outcome
Meaning
default
Best balance of retrieval quality, latency, memory, and index cost.
quality_profile
Better retrieval quality, but too expensive for default use.
interactive_profile
Best for ctx / emb latency, acceptable quality.
rejected
Cost increase is not justified by retrieval improvement.
needs_more_data
Difference is too small or inconsistent across repos/intents.
```text
case_id
repo
intent
model
rank
result kind
result path/symbol
score
grade
is_relevant
```
pooled-results.jsonl
Deduplicated results requiring or containing manual labels.
grading-sheet.csv
A human-editable spreadsheet-style file for manual grading.
regressions.md
Highlights cases where a model:
lost a previously relevant top result;
moved the first relevant result lower;
failed an easy symbol lookup;
performed poorly on a specific repo or intent.
Reproducibility requirements
Each campaign must record:
```text
Codira version
git commit of Codira
repo manifest path
model manifest path
query set path
query set checksum
backend
chunking configuration
embedding configuration
K values
timestamp
host platform
Python version
ONNX / sentence-transformers versions where available
repository commits
```
Feat: Add Codira Embedding Retrieval Quality Benchmark
Summary
Add a reproducible benchmark suite for measuring embedding model quality for Codira.
Current embedding campaigns measure latency, memory, backend overhead, and indexing cost well. They do not yet measure whether a given embedding model retrieves better context for Codira-specific development tasks.
This issue adds a versioned benchmark for retrieval quality, so Codira can decide whether larger or more expensive embedding models are actually worth their runtime and memory cost.
The benchmark should answer:
It should not answer the vague question:
Codira should select embedding models based on measured retrieval quality under Codira workloads, not on vector dimensionality, model reputation, or generic leaderboard scores alone.
Motivation
Recent performance campaigns show that smaller models are materially cheaper than larger ones.
For example, the current measured default candidate remains:
```text
bge-small-en-v1.5-onnx + sqlite
```
because it has strong interactive
ctx/embperformance, modest memory use, and avoids the cost of larger 768-dimensional models.However, there are indications that some larger models may produce “better” semantic retrieval. That claim is currently not measured in a Codira-specific, reproducible way.
This benchmark should make the tradeoff explicit:
```text
quality gain vs latency cost vs memory cost vs indexing cost
```
A larger model should only become a default, or a recommended
qualityprofile, if it produces a measurable retrieval-quality improvement large enough to justify its cost.Non-goals
This issue should not initially attempt to:
Those can be separate follow-up issues.
The first benchmark should isolate embedding model retrieval quality as much as possible.
Background terminology
Codira does not need to expose all IR terminology to users, but the benchmark implementation should be compatible with standard ranked-retrieval metrics.
Useful concepts:
K.K.3,2,1,0.For the first implementation, Codira should focus on:
```text
Hit@1
Recall@5
MRR@5 or MRR@10
optional nDCG@10
```
Core design principle
Benchmark Codira retrieval tasks, not raw model quality.
A benchmark case is not just a query string. It is:
```text
repo + query + intent + expected relevant results
```
Different repositories should have different query text, but the same benchmark taxonomy.
Bad approach:
```text
Run the exact same query strings against every repository.
```
Good approach:
```text
Run the same categories of queries against every repository,
with repo-specific query text and repo-specific gold labels.
```
Example:
codiraquerychatopsquerysymbol_lookupDeclarationArtifactChatConversationarchitecture_lookuphow does the backend plugin lifecycle work?how does the processing pipeline work?task_lookupwhere would I change embedding vector persistence?where would I change duplicate detection?docs_lookuphow are analyzers configured?how are ADRs created?The benchmark should compare models on equivalent task types, not on identical query strings.
Initial benchmark scope
Use a small but structured first suite.
Recommended first version:
```text
4 repositories
5 query categories
5 queries per category per repository
= 100 benchmark cases
```
Suggested initial repositories:
```text
codira
chatops
fontshow
sanikey
```
The exact list should come from the existing benchmark repository manifest where possible.
The benchmark should use fixed commits. If a repository changes during a campaign, the report must flag it and either exclude it from before/after conclusions or report it separately.
Query taxonomy
Each benchmark case should have an
intent.Initial intent categories:
```text
symbol_lookup
architecture_lookup
task_lookup
docs_lookup
error_or_trace_lookup
```
1.
symbol_lookupQueries that should retrieve a specific class, function, method, module, or declaration.
Examples:
```text
DeclarationArtifact
ChatConversation
EmbeddingModelConfig
DuckDBVectorStore
```
These cases can often be generated semi-automatically from the symbol table.
2.
architecture_lookupNatural-language queries about where an architectural concept is implemented or documented.
Examples:
```text
how does codira select the analyzer for a file?
how does the backend plugin lifecycle work?
how does the processing pipeline work?
where is stable conversation fingerprinting defined?
```
3.
task_lookupQueries phrased as development tasks.
Examples:
```text
where would I change how pending embedding rows are flushed?
what code would I touch to add markdown frontmatter support?
where would I change duplicate detection?
where is keyboard layout data generated?
```
4.
docs_lookupQueries where the relevant answer may be in documentation rather than code.
Examples:
```text
how are analyzers configured?
how are ADRs created?
how is the embedding backend configured?
what is the documented storage backend policy?
```
5.
error_or_trace_lookupQueries based on error messages, failure symptoms, or stack trace fragments.
Examples:
```text
PRIMARY KEY or UNIQUE constraint violation duplicate key
unrecognized arguments benchmarks/bk-cpp.local.json
EXPORT_SCHEMA_ERROR missing role
DuckDB executemany semgrep finding
```
Query distribution
For the first version:
codirachatopsfontshowsanikeyA later mature version can expand to:
```text
10 queries × 5 categories × 4 repositories = 200 cases
```
or eventually:
```text
300+ cases
```
but the first version should not require 300 hand-graded queries.
Manual grading strategy
Do not manually grade every model result independently.
Use TREC-style pooling:
Kresults from all models.Example:
```text
Query: "where is embedding flush implemented?"
Model A top 5:
A, B, C, D, E
Model B top 5:
A, C, F, G, H
Model C top 5:
B, F, I, J, K
Pooled unique results:
A, B, C, D, E, F, G, H, I, J, K
```
The grader judges 11 unique results, not 15 model-specific rows.
This keeps the benchmark feasible.
Grading scale
The first implementation should support both binary and graded relevance.
Binary mode
Recommended for the first suite:
```text
1 = useful
0 = not useful
```
This is enough for:
```text
Hit@1
Recall@5
MRR@5
MRR@10
```
Graded mode
Optional, for later refinement:
```text
3 = ideal / central result
2 = useful result
1 = marginally useful result
0 = irrelevant result
```
This enables:
```text
nDCG@5
nDCG@10
```
The benchmark data format should allow graded labels from the start, even if the first dataset uses only
0and1.Benchmark case format
Add a JSONL file such as:
```text
benchmarks/embedding-quality/cases.jsonl
```
Example record:
```json
{
"id": "codira-task-embeddings-001",
"repo": "codira",
"intent": "task_lookup",
"language": "python",
"channel": "code",
"difficulty": "medium",
"query": "where would I change how pending embedding rows are flushed?",
"relevant": [
{
"kind": "symbol",
"id": "embeddings.flush_pending_rows",
"grade": 3
},
{
"kind": "file",
"path": "packages/codira-backend-duckdb/src/codira_backend_duckdb/...",
"grade": 2
}
]
}
```
Fields:
idrepointentlanguagechannelcode,docs,code+docs,tests,config, etc.difficultyeasy,medium,hard.queryrelevantRelevant target kinds should support at least:
```text
file
symbol
doc_section
test
config
```
Gold-label format
The first implementation can keep labels directly in
cases.jsonl.A later implementation may split them:
```text
benchmarks/embedding-quality/cases.jsonl
benchmarks/embedding-quality/labels.jsonl
benchmarks/embedding-quality/pools.jsonl
```
Possible pooled-label record:
```json
{
"case_id": "codira-task-embeddings-001",
"result_id": "file:packages/codira-backend-duckdb/src/...",
"grade": 2,
"graded_by": "manual",
"notes": "Useful implementation file, but not the main flush function."
}
```
Campaign command
Add a script or CLI command similar to:
```bash
uv run python -m scripts.run_embedding_quality_campaign
--manifest benchmarks/uv-backed-repos.local.json
--model-manifest benchmarks/embedding-model-candidates.json
--queries benchmarks/embedding-quality/cases.jsonl
--backend sqlite
--k 1,3,5,10,20
--output .artifacts/embedding-quality-campaign/$(date -u +%Y%m%dT%H%M%SZ)
```
The first version should use:
```text
backend: sqlite
```
Rationale:
Candidate models
The benchmark should use the existing embedding model manifest where possible:
```text
benchmarks/embedding-model-candidates.json
```
It should compare at least:
```text
current default candidate
best 384-dimensional ONNX candidate
one or more 768-dimensional ONNX candidates
sentence-transformers baseline if still relevant
```
The benchmark should report model metadata:
```text
model name
engine
dimension
backend
index latency
query latency
memory/RSS
quality metrics
```
Metrics
Compute at least:
```text
Hit@1
Recall@5
MRR@5
MRR@10
```
Optionally:
```text
Recall@10
nDCG@5
nDCG@10
MAP
```
The first report should not depend on MAP.
MAP can be added later if useful.
Aggregation strategy
Do not report only one global number.
Report:
1. Per-repository metrics
Example:
bge-small-en-v1.5-onnxcodirabge-small-en-v1.5-onnxchatopsThis shows whether a model only wins on one repository.
2. Per-intent metrics
Example:
bge-small-en-v1.5-onnxsymbol_lookupbge-small-en-v1.5-onnxarchitecture_lookupbge-small-en-v1.5-onnxtask_lookupbge-small-en-v1.5-onnxdocs_lookupThis is likely more important than the global score.
3. Macro average
Each repository gets equal weight.
```text
macro_avg = mean(score_per_repo)
```
This prevents a larger repository or a repository with more cases from dominating the final decision.
4. Micro average
Each query gets equal weight.
```text
micro_avg = total successful results / total evaluated cases
```
This is useful as a secondary number.
The report should clearly label macro and micro averages.
Decision gates
A larger or slower model should only be recommended if it shows a material quality gain.
Initial suggested gate:
```text
A more expensive model may become the default only if:
while:
```
If a larger model improves quality but violates default latency/memory constraints, it may be classified as:
```text
quality profile
```
instead of:
```text
default profile
```
Possible decisions:
defaultquality_profileinteractive_profilectx/emblatency, acceptable quality.rejectedneeds_more_dataOutput artifacts
The campaign should write:
```text
quality-summary.md
quality-summary.json
per-query-results.jsonl
per-model-ranking.csv
per-repo-summary.csv
per-intent-summary.csv
pooled-results.jsonl
grading-sheet.csv
regressions.md
```
quality-summary.mdHuman-readable report with:
quality-summary.jsonMachine-readable summary for later comparison.
per-query-results.jsonlOne row per model/query/result.
Should include:
```text
case_id
repo
intent
model
rank
result kind
result path/symbol
score
grade
is_relevant
```
pooled-results.jsonlDeduplicated results requiring or containing manual labels.
grading-sheet.csvA human-editable spreadsheet-style file for manual grading.
regressions.mdHighlights cases where a model:
Reproducibility requirements
Each campaign must record:
```text
Codira version
git commit of Codira
repo manifest path
model manifest path
query set path
query set checksum
backend
chunking configuration
embedding configuration
K values
timestamp
host platform
Python version
ONNX / sentence-transformers versions where available
repository commits
```
The report must flag:
Implementation sketch
Phase 1: Data format and validation
Add:
```text
benchmarks/embedding-quality/cases.jsonl
benchmarks/embedding-quality/README.md
benchmarks/embedding-quality/grading-guidelines.md
```
Add validation for:
Phase 2: Campaign runner
Add:
```text
scripts/run_embedding_quality_campaign.py
```
Responsibilities:
Phase 3: Metric engine
Add a small metric module supporting:
```text
Hit@K
Recall@K
MRR@K
nDCG@K
macro average
micro average
per-repo aggregation
per-intent aggregation
```
Keep this module independent from the campaign runner where practical.
Phase 4: Manual grading workflow
Generate:
```text
grading-sheet.csv
```
with columns such as:
```text
case_id
repo
intent
query
result_id
ranked_by_models
result_kind
result_path
result_symbol
grade
notes
```
Then allow re-running the metric computation after the grading sheet has been filled.
Possible command:
```bash
uv run python -m scripts.score_embedding_quality_campaign
--results .artifacts/embedding-quality-campaign//per-query-results.jsonl
--labels benchmarks/embedding-quality/labels.jsonl
--output .artifacts/embedding-quality-campaign//scored
```
Phase 5: Reporting
Generate Markdown and JSON summaries.
The Markdown report should include the decision table:
bge-small-en-v1.5-onnxdefaultjina-embeddings-v2-base-code-onnxquality_profilenomic-embed-text-v1.5-onnxrejectedSuggested first benchmark workflow
Create 100 benchmark cases:
Run candidate models with
top_k = 10.Pool results across models.
Manually grade pooled unique results using binary relevance:
```text
1 = useful
0 = not useful
```
```text
Hit@1
Recall@5
MRR@5
MRR@10
```
Compare models using:
Only expand to 200+ cases if the first 100 cases show a meaningful difference.
Testing requirements
Add tests for:
Hit@K;Recall@K;MRR@K;nDCG@Kif implemented;Use small fixtures, not real large repositories, for unit tests.
Documentation requirements
Add documentation explaining:
```text
docs/benchmarks/embedding-quality.md
```
The document should cover:
Also update any relevant benchmark/process index pages.
Acceptance criteria
This issue is complete when:
cases.jsonlfile exists.Kvalues.Hit@1is computed.Recall@5is computed.MRR@5orMRR@10is computed.Future extensions
Possible follow-up issues:
code,docs,tests,config.Design note
Codira should not select embedding models based on vector dimension or generic benchmark reputation.
Codira should select embedding models based on:
```text
retrieval quality on versioned Codira benchmark cases
under explicit latency, memory, and indexing-cost constraints
```
A 768-dimensional model is only better for Codira if it retrieves better Codira context by enough margin to justify its cost.