embedflow 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- embedflow/__init__.py +25 -0
- embedflow/__main__.py +3 -0
- embedflow/analysis.py +192 -0
- embedflow/cache/__init__.py +4 -0
- embedflow/cache/base.py +28 -0
- embedflow/cache/persistent_cache.py +198 -0
- embedflow/cli.py +1200 -0
- embedflow/compatibility/__init__.py +28 -0
- embedflow/compatibility/candidate_gap.py +105 -0
- embedflow/compatibility/containment.py +17 -0
- embedflow/compatibility/evaluate.py +319 -0
- embedflow/compatibility/metrics.py +75 -0
- embedflow/compatibility/migration_depth.py +67 -0
- embedflow/compatibility/probe.py +34 -0
- embedflow/compatibility/report.py +102 -0
- embedflow/compatibility/t2.py +64 -0
- embedflow/config.py +455 -0
- embedflow/data/__init__.py +1 -0
- embedflow/data/registry/__init__.py +1 -0
- embedflow/data/registry/benchmark_profiles.jsonl +3 -0
- embedflow/data/registry/checksums.sha256 +4 -0
- embedflow/data/registry/migrations.jsonl +15 -0
- embedflow/data/registry/registry_manifest.json +16 -0
- embedflow/data/registry/research_summaries.json +55 -0
- embedflow/data/registry/schema_version.json +5 -0
- embedflow/frozen/T2_V1_FROZEN_SPEC.md +71 -0
- embedflow/frozen/T2_V1_FROZEN_SPEC.sha256 +1 -0
- embedflow/indexes/__init__.py +5 -0
- embedflow/indexes/base.py +60 -0
- embedflow/indexes/faiss_backend.py +240 -0
- embedflow/indexes/qdrant_backend.py +225 -0
- embedflow/metrics/__init__.py +3 -0
- embedflow/metrics/latency.py +50 -0
- embedflow/migration/__init__.py +3 -0
- embedflow/migration/compatibility.py +156 -0
- embedflow/migration/facade.py +312 -0
- embedflow/migration/materializer.py +190 -0
- embedflow/migration/planner.py +78 -0
- embedflow/migration/state.py +81 -0
- embedflow/models/__init__.py +4 -0
- embedflow/models/base.py +31 -0
- embedflow/models/huggingface.py +226 -0
- embedflow/registry/__init__.py +47 -0
- embedflow/registry/loader.py +785 -0
- embedflow/registry/matcher.py +197 -0
- embedflow/registry/schema.py +266 -0
- embedflow/runtime.py +115 -0
- embedflow/serving/__init__.py +3 -0
- embedflow/serving/api.py +161 -0
- embedflow/serving/engine.py +222 -0
- embedflow/serving/factory.py +3 -0
- embedflow/serving/schemas.py +39 -0
- embedflow-0.1.0.dist-info/METADATA +210 -0
- embedflow-0.1.0.dist-info/RECORD +64 -0
- embedflow-0.1.0.dist-info/WHEEL +5 -0
- embedflow-0.1.0.dist-info/entry_points.txt +2 -0
- embedflow-0.1.0.dist-info/licenses/LICENSE +178 -0
- embedflow-0.1.0.dist-info/top_level.txt +2 -0
- src/__init__.py +1 -0
- src/embed.py +123 -0
- src/probe_features.py +24 -0
- src/storage.py +51 -0
- src/t2_v1.py +21 -0
- src/utils.py +53 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{"registry_version":"0.1.0","evidence_id":"bright_minilm_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"sentence-transformers/all-MiniLM-L6-v2","revision":"1110a243fdf4706b3f48f1d95db1a4f5529b4d41","dimension":384,"max_length":512,"pooling":"mean_tokens","padding_side":"right","truncation_side":"right","query_instruction":"","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"33e79641f1c0a16c2cc0c755560aa37c88166b1402016acb65e31c5fc69b793f"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"BRIGHT (frozen four-domain slice)","canonical_dataset_id":"bright_frozen_four_domain","canonical_construction":true,"corpus_size":413234,"query_count":522,"split":"external_confirmation","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[10,20,50,100,200,500],"candidate_gap":{"10":0.0586009717079398,"20":0.0453152589417813,"50":0.0366503509448972,"100":0.0241809969653622,"200":0.0136079748023061,"500":0.0118447328683605},"containment":{"10":0.2559386973180076,"20":0.356896551724138,"50":0.503256704980843,"100":0.5946360153256705,"200":0.6821839080459771,"500":0.7760536398467434},"native_target_quality":0.18252188442369427,"source_quality":0.11320806420310428,"restricted_target_quality":{"10":0.1239209127157544,"20":0.1372066254819129,"50":0.1458715334787971,"100":0.1583408874583322,"200":0.1689139096213881,"500":0.1706771515553337},"observed_migration_depth":null,"ci_certified_migration_depth":null,"t2":null,"ann":null,"provenance":{"artifact":"iclr_candidate_compatibility/paper_appendix_artifacts/data/bright_pooled_appendix_values.csv","artifact_sha256":"85993bc74823bb8a08a4906691fab882c955aaec11e49615646a3f7ddde6eab5","table":"bright_pooled_appendix_values.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
2
|
+
{"registry_version":"0.1.0","evidence_id":"bright_q06_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-0.6B","revision":"97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3","dimension":1024,"max_length":512,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: {text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"01022d70ba11a7e69c1516f9b1416e61dd753579a59d2e73f7173bf04318263c"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"BRIGHT (frozen four-domain slice)","canonical_dataset_id":"bright_frozen_four_domain","canonical_construction":true,"corpus_size":413234,"query_count":522,"split":"external_confirmation","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[10,20,50,100,200,500],"candidate_gap":{"10":0.040950869227321,"20":0.0275051598238951,"50":0.0146489675984517,"100":0.0106095765235362,"200":0.0041343855509737,"500":0.0029957540848326},"containment":{"10":0.442911877394636,"20":0.5936781609195402,"50":0.7590038314176246,"100":0.8417624521072796,"200":0.9024904214559388,"500":0.946360153256705},"native_target_quality":0.18252188442369427,"source_quality":0.13177793963430032,"restricted_target_quality":{"10":0.1415710151963732,"20":0.1550167245997991,"50":0.1678729168252425,"100":0.171912307900158,"200":0.1783874988727205,"500":0.1795261303388615},"observed_migration_depth":200,"ci_certified_migration_depth":500,"t2":null,"ann":null,"provenance":{"artifact":"iclr_candidate_compatibility/paper_appendix_artifacts/data/bright_pooled_appendix_values.csv","artifact_sha256":"85993bc74823bb8a08a4906691fab882c955aaec11e49615646a3f7ddde6eab5","table":"bright_pooled_appendix_values.csv","status":"retained_external","git_commit":null},"notes":["Historical Qwen3-0.6B source contract uses max length 512 and the retained trailing-space prompt behavior; parameter count alone is not a causal explanation of the observed hierarchy."]}
|
|
3
|
+
{"registry_version":"0.1.0","evidence_id":"bright_q4_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-4B","revision":"5cf2132abc99cad020ac570b19d031efec650f2b","dimension":2560,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"0607b87cc200f80bfc498e595891fb533f42677355b18279be42d7d988896c44"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"BRIGHT (frozen four-domain slice)","canonical_dataset_id":"bright_frozen_four_domain","canonical_construction":true,"corpus_size":413234,"query_count":522,"split":"external_confirmation","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[10,20,50,100,200,500],"candidate_gap":{"10":0.0143823299930196,"20":0.0068605322289811,"50":0.0034745366708826,"100":0.0028603276290983,"200":0.0025005971627645,"500":0.0011638933929886},"containment":{"10":0.5574712643678161,"20":0.7325670498084291,"50":0.8796934865900384,"100":0.935632183908046,"200":0.9657088122605364,"500":0.985823754789272},"native_target_quality":0.18252188442369427,"source_quality":0.1595777430744477,"restricted_target_quality":{"10":0.1681395544306745,"20":0.1756613521947131,"50":0.1790473477528116,"100":0.1796615567945959,"200":0.1800212872609297,"500":0.1813579910307056},"observed_migration_depth":20,"ci_certified_migration_depth":50,"t2":null,"ann":null,"provenance":{"artifact":"iclr_candidate_compatibility/paper_appendix_artifacts/data/bright_pooled_appendix_values.csv","artifact_sha256":"85993bc74823bb8a08a4906691fab882c955aaec11e49615646a3f7ddde6eab5","table":"bright_pooled_appendix_values.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
4
|
+
{"registry_version":"0.1.0","evidence_id":"nq_minilm_100000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"sentence-transformers/all-MiniLM-L6-v2","revision":"1110a243fdf4706b3f48f1d95db1a4f5529b4d41","dimension":384,"max_length":512,"pooling":"mean_tokens","padding_side":"right","truncation_side":"right","query_instruction":"","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"33e79641f1c0a16c2cc0c755560aa37c88166b1402016acb65e31c5fc69b793f"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 100K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_100000","canonical_construction":true,"corpus_size":100000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.04889172585889534,"50":0.027784233801564986,"100":0.02042199116199458,"200":0.019092155196911726,"500":0.012799078510286164,"1000":0.008366106137423975},"containment":{"20":0.5199,"50":0.651,"100":0.7275,"200":0.7955,"500":0.8647,"1000":0.908},"native_target_quality":0.8799917757827989,"source_quality":0.7458455073968056,"restricted_target_quality":{"20":0.8311000499239035,"50":0.8522075419812338,"100":0.8595697846208042,"200":0.8608996205858872,"500":0.8671926972725128,"1000":0.8716256696453749},"observed_migration_depth":1000,"ci_certified_migration_depth":1000,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.90731,"g_ann_at_50":0.045384613,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
5
|
+
{"registry_version":"0.1.0","evidence_id":"nq_minilm_250000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"sentence-transformers/all-MiniLM-L6-v2","revision":"1110a243fdf4706b3f48f1d95db1a4f5529b4d41","dimension":384,"max_length":512,"pooling":"mean_tokens","padding_side":"right","truncation_side":"right","query_instruction":"","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"33e79641f1c0a16c2cc0c755560aa37c88166b1402016acb65e31c5fc69b793f"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 250K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_250000","canonical_construction":true,"corpus_size":250000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.04882083477630582,"50":0.032690124097947235,"100":0.021525916271242097,"200":0.015116991962891593,"500":0.01315534405200144,"1000":0.009609753962908378},"containment":{"20":0.5347,"50":0.6604,"100":0.7447,"200":0.8037,"500":0.8666,"1000":0.9022},"native_target_quality":0.8144039524195975,"source_quality":0.6629857288877559,"restricted_target_quality":{"20":0.7655831176432918,"50":0.7817138283216504,"100":0.7928780361483555,"200":0.799286960456706,"500":0.8012486083675961,"1000":0.8047941984566892},"observed_migration_depth":1000,"ci_certified_migration_depth":1000,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.90731,"g_ann_at_50":0.045384613,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
6
|
+
{"registry_version":"0.1.0","evidence_id":"nq_minilm_500000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"sentence-transformers/all-MiniLM-L6-v2","revision":"1110a243fdf4706b3f48f1d95db1a4f5529b4d41","dimension":384,"max_length":512,"pooling":"mean_tokens","padding_side":"right","truncation_side":"right","query_instruction":"","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"33e79641f1c0a16c2cc0c755560aa37c88166b1402016acb65e31c5fc69b793f"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 500K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_500000","canonical_construction":true,"corpus_size":500000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.06208153300561271,"50":0.031456675398717965,"100":0.02393859758180647,"200":0.013226428113780943,"500":0.008236308012747688,"1000":0.007399852967423057},"containment":{"20":0.5468,"50":0.6833,"100":0.7579,"200":0.8186,"500":0.876,"1000":0.9072},"native_target_quality":0.7540575411662394,"source_quality":0.5956048958216327,"restricted_target_quality":{"20":0.6919760081606267,"50":0.7226008657675215,"100":0.730118943584433,"200":0.7408311130524585,"500":0.7458212331534917,"1000":0.7466576881988164},"observed_migration_depth":500,"ci_certified_migration_depth":1000,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.90731,"g_ann_at_50":0.045384613,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
7
|
+
{"registry_version":"0.1.0","evidence_id":"nq_minilm_1000000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"sentence-transformers/all-MiniLM-L6-v2","revision":"1110a243fdf4706b3f48f1d95db1a4f5529b4d41","dimension":384,"max_length":512,"pooling":"mean_tokens","padding_side":"right","truncation_side":"right","query_instruction":"","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"33e79641f1c0a16c2cc0c755560aa37c88166b1402016acb65e31c5fc69b793f"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 1M canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_1000000","canonical_construction":true,"corpus_size":1000000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.05709602795250202,"50":0.03255228101306185,"100":0.018659503743341875,"200":0.01523184556680107,"500":0.006899217552864197,"1000":0.0054396898748682075},"containment":{"20":0.5625,"50":0.7007,"100":0.7766,"200":0.8357,"500":0.8937,"1000":0.9227},"native_target_quality":0.6811742213916842,"source_quality":0.5231306308441888,"restricted_target_quality":{"20":0.6240781934391821,"50":0.6486219403786222,"100":0.6625147176483421,"200":0.665942375824883,"500":0.6742750038388199,"1000":0.6757345315168158},"observed_migration_depth":500,"ci_certified_migration_depth":1000,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.90731,"g_ann_at_50":0.045384613,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
8
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q06_100000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-0.6B","revision":"97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3","dimension":1024,"max_length":512,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: {text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"01022d70ba11a7e69c1516f9b1416e61dd753579a59d2e73f7173bf04318263c"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 100K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_100000","canonical_construction":true,"corpus_size":100000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.0268808783142928,"50":0.016870042625055953,"100":0.012352832751062828,"200":0.008651342390920566,"500":0.007597822917019358,"1000":0.005069268930032147},"containment":{"20":0.6406,"50":0.7765,"100":0.8506,"200":0.902,"500":0.9441,"1000":0.9701},"native_target_quality":0.8799917757827989,"source_quality":0.788932164523722,"restricted_target_quality":{"20":0.853110897468506,"50":0.863121733157743,"100":0.867638943031736,"200":0.8713404333918784,"500":0.8723939528657796,"1000":0.8749225068527666},"observed_migration_depth":200,"ci_certified_migration_depth":500,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.903104,"g_ann_at_50":0.028536035584226974,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Historical Qwen3-0.6B source contract uses max length 512 and the retained trailing-space prompt behavior; parameter count alone is not a causal explanation of the observed hierarchy."]}
|
|
9
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q06_250000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-0.6B","revision":"97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3","dimension":1024,"max_length":512,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: {text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"01022d70ba11a7e69c1516f9b1416e61dd753579a59d2e73f7173bf04318263c"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 250K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_250000","canonical_construction":true,"corpus_size":250000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.026841530732156702,"50":0.018609461034600008,"100":0.01380021142300151,"200":0.007969216414570318,"500":0.004852605795502614,"1000":0.005426061357756286},"containment":{"20":0.6535,"50":0.7844,"100":0.85,"200":0.8989,"500":0.9402,"1000":0.9621},"native_target_quality":0.8144039524195975,"source_quality":0.7180062600301215,"restricted_target_quality":{"20":0.787562421687441,"50":0.7957944913849976,"100":0.8006037409965961,"200":0.8064347360050271,"500":0.809551346624095,"1000":0.8089778910618413},"observed_migration_depth":200,"ci_certified_migration_depth":500,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.903104,"g_ann_at_50":0.028536035584226974,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Historical Qwen3-0.6B source contract uses max length 512 and the retained trailing-space prompt behavior; parameter count alone is not a causal explanation of the observed hierarchy."]}
|
|
10
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q06_500000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-0.6B","revision":"97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3","dimension":1024,"max_length":512,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: {text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"01022d70ba11a7e69c1516f9b1416e61dd753579a59d2e73f7173bf04318263c"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 500K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_500000","canonical_construction":true,"corpus_size":500000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.029639359910110356,"50":0.0179311276676135,"100":0.01550754409927157,"200":0.011482549923777778,"500":0.006699866016699463,"1000":0.0044958669929648105},"containment":{"20":0.6692,"50":0.7969,"100":0.8593,"200":0.9022,"500":0.9426,"1000":0.962},"native_target_quality":0.7540575411662394,"source_quality":0.6577692238969566,"restricted_target_quality":{"20":0.724418181256129,"50":0.7361264134986258,"100":0.7385499970669678,"200":0.7425749912424616,"500":0.74735767514954,"1000":0.7495616741732746},"observed_migration_depth":500,"ci_certified_migration_depth":500,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.903104,"g_ann_at_50":0.028536035584226974,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Historical Qwen3-0.6B source contract uses max length 512 and the retained trailing-space prompt behavior; parameter count alone is not a causal explanation of the observed hierarchy."]}
|
|
11
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q06_1000000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-0.6B","revision":"97b0c614be4d77ee51c0cef4e5f07c00f9eb65b3","dimension":1024,"max_length":512,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: {text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"01022d70ba11a7e69c1516f9b1416e61dd753579a59d2e73f7173bf04318263c"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 1M canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_1000000","canonical_construction":true,"corpus_size":1000000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.037852309778764644,"50":0.017367670711871723,"100":0.012664586586224942,"200":0.009126184378587228,"500":0.005909441565411069,"1000":0.003621957446104695},"containment":{"20":0.6878,"50":0.8154,"100":0.8709,"200":0.9114,"500":0.9489,"1000":0.9644},"native_target_quality":0.6811742213916842,"source_quality":0.5795521297196562,"restricted_target_quality":{"20":0.6433219116129194,"50":0.6638065506798124,"100":0.6685096348054592,"200":0.6720480370130969,"500":0.6752647798262731,"1000":0.6775522639455794},"observed_migration_depth":200,"ci_certified_migration_depth":500,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.903104,"g_ann_at_50":0.028536035584226974,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Historical Qwen3-0.6B source contract uses max length 512 and the retained trailing-space prompt behavior; parameter count alone is not a causal explanation of the observed hierarchy."]}
|
|
12
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q4_100000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-4B","revision":"5cf2132abc99cad020ac570b19d031efec650f2b","dimension":2560,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"0607b87cc200f80bfc498e595891fb533f42677355b18279be42d7d988896c44"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 100K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_100000","canonical_construction":true,"corpus_size":100000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.0009311483076909985,"50":0.0010609850811850834,"100":0.0017278129387014484,"200":0.0003065735963827292,"500":0,"1000":0},"containment":{"20":0.8366,"50":0.9407,"100":0.9758,"200":0.9886,"500":0.9962,"1000":0.9985},"native_target_quality":0.8799917757827989,"source_quality":0.8576143782397919,"restricted_target_quality":{"20":0.8790606274751079,"50":0.8789307907016137,"100":0.8782639628440975,"200":0.8796852021864162,"500":0.8799917757827989,"1000":0.8799917757827989},"observed_migration_depth":20,"ci_certified_migration_depth":20,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.92194,"g_ann_at_50":0.004046421654698915,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
13
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q4_250000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-4B","revision":"5cf2132abc99cad020ac570b19d031efec650f2b","dimension":2560,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"0607b87cc200f80bfc498e595891fb533f42677355b18279be42d7d988896c44"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 250K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_250000","canonical_construction":true,"corpus_size":250000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":-0.00003621660519000031,"50":-0.0005653266956973871,"100":0.0007348082164592,"200":0.001643874867006752,"500":0.0003065735963827292,"1000":0.0003065735963827292},"containment":{"20":0.8467,"50":0.9446,"100":0.9718,"200":0.9861,"500":0.9951,"1000":0.9978},"native_target_quality":0.8144039524195975,"source_quality":0.793923498749834,"restricted_target_quality":{"20":0.8144401690247897,"50":0.8149692791152949,"100":0.8136691442031383,"200":0.8127600775525908,"500":0.8140973788232148,"1000":0.8140973788232148},"observed_migration_depth":20,"ci_certified_migration_depth":20,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.92194,"g_ann_at_50":0.004046421654698915,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
14
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q4_500000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-4B","revision":"5cf2132abc99cad020ac570b19d031efec650f2b","dimension":2560,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"0607b87cc200f80bfc498e595891fb533f42677355b18279be42d7d988896c44"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 500K canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_500000","canonical_construction":true,"corpus_size":500000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":-0.001012840806237946,"50":0.00035532900637539266,"100":0.00016090064626588928,"200":0.0008190357190123196,"500":0.0012640681225725909,"1000":0.0002640681225725909},"containment":{"20":0.8572,"50":0.946,"100":0.973,"200":0.9867,"500":0.9955,"1000":0.9976},"native_target_quality":0.7540575411662394,"source_quality":0.7343172086242484,"restricted_target_quality":{"20":0.7550703819724773,"50":0.7537022121598641,"100":0.7538966405199735,"200":0.753238505447227,"500":0.7527934730436668,"1000":0.7537934730436668},"observed_migration_depth":20,"ci_certified_migration_depth":20,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.92194,"g_ann_at_50":0.004046421654698915,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
15
|
+
{"registry_version":"0.1.0","evidence_id":"nq_q4_1000000_qwen3_8b","evidence_type":"migration_curve","source":{"canonical_model_id":"Qwen/Qwen3-Embedding-4B","revision":"5cf2132abc99cad020ac570b19d031efec650f2b","dimension":2560,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"0607b87cc200f80bfc498e595891fb533f42677355b18279be42d7d988896c44"},"target":{"canonical_model_id":"Qwen/Qwen3-Embedding-8B","revision":"1d8ad4ca9b3dd8059ad90a75d4983776a23d44af","dimension":4096,"max_length":8192,"pooling":"last_non_padding_token","padding_side":"left","truncation_side":"right","query_instruction":"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:{text}","document_instruction":"","normalization":"l2","dtype":"float32","contract_fingerprint":"11d6d071a07a73b0bb3588a49447bb8bf58cb5426af732b534d374581ac4b018"},"dataset":{"name":"Natural Questions (BEIR) 1M canonical prefix","canonical_dataset_id":"beir_nq_1m_prefix_1000000","canonical_construction":true,"corpus_size":1000000,"query_count":1000,"split":"scale_study","fingerprint":null},"metric":"nDCG@10","epsilon":0.01,"candidate_depths":[20,50,100,200,500,1000],"candidate_gap":{"20":0.003291235284552691,"50":-0.00043030085527543164,"100":0.0001045284033624751,"200":0.0002040233121074877,"500":0.00028778163515584223,"1000":0.00043067655807339306},"containment":{"20":0.8692,"50":0.9539,"100":0.9755,"200":0.988,"500":0.9951,"1000":0.9979},"native_target_quality":0.6811742213916842,"source_quality":0.661377543285374,"restricted_target_quality":{"20":0.6778829861071315,"50":0.6816045222469596,"100":0.6810696929883215,"200":0.6809701980795767,"500":0.6808864397565283,"1000":0.6807435448336107},"observed_migration_depth":20,"ci_certified_migration_depth":20,"t2":null,"ann":{"status":"MEASURED_REFERENCE_COMPARISON","metric":"source Recall@500 against exact source top-500","index_type":"ivf_flat","nprobe":64,"recall_at_500":0.92194,"g_ann_at_50":0.004046421654698915,"scope":"NQ canonical-prefix scale study"},"provenance":{"artifact":"remote_scale_experiment/runs/20260824-194342/results/scale_curves.csv","artifact_sha256":"a8eea1b1c7ae0d3ac4d23ca5e806704c8e8f2bd932e5765002ba250377fc449c","table":"scale_curves.csv","status":"retained_external","git_commit":null},"notes":["Query-weighted measurements from retained research artifacts; not universal claims about this model pair."]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"registry_version": "0.1.0",
|
|
3
|
+
"schema_version": "1",
|
|
4
|
+
"record_count": 15,
|
|
5
|
+
"profile_count": 3,
|
|
6
|
+
"summary_count": 2,
|
|
7
|
+
"namespace": "core",
|
|
8
|
+
"trust": "verified retained research artifacts",
|
|
9
|
+
"files": {
|
|
10
|
+
"schema_version.json": {"sha256": "c3d851bcfe66d9cc1aabc4ec8b61512110d8802ac0bcc7af3239a9ac6cec14a9"},
|
|
11
|
+
"migrations.jsonl": {"sha256": "1471e7a937d87eeba58d19a801fb8c147f6982d2c1522b5da43f6986b92800d5"},
|
|
12
|
+
"research_summaries.json": {"sha256": "fcbf2fa0456fc3fc2e30d1b945b05e7d7dcfa11b7187910ced8a3fa0e69f6720"},
|
|
13
|
+
"benchmark_profiles.jsonl": {"sha256": "4ef2bdfc9f1803ef108400986be6a06915879d302e14a2757fc091c32b5b6e54"}
|
|
14
|
+
},
|
|
15
|
+
"provenance_policy": "Every core row names a retained artifact and SHA-256; external artifact paths are not required at install time."
|
|
16
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"registry_version": "0.1.0",
|
|
3
|
+
"summaries": [
|
|
4
|
+
{
|
|
5
|
+
"summary_id": "development_63_cell_audit",
|
|
6
|
+
"kind": "development_sweep_summary",
|
|
7
|
+
"cells": 63,
|
|
8
|
+
"point_estimate_g50_le_0_01": 42,
|
|
9
|
+
"ci_certified_cells": 26,
|
|
10
|
+
"excluding_nanoquora_and_nanomsmarco": {
|
|
11
|
+
"cells": 45,
|
|
12
|
+
"point_estimate_g50_le_0_01": 25,
|
|
13
|
+
"ci_certified_cells": 11
|
|
14
|
+
},
|
|
15
|
+
"provenance": {
|
|
16
|
+
"artifact": "iclr_candidate_compatibility/results/DEVELOPMENT_63_FINAL.csv",
|
|
17
|
+
"artifact_sha256": "af614e572c811c3905ad1071548bbb53d01e1079eff6232c5cf2ec9bdf294560",
|
|
18
|
+
"supporting_artifact": "iclr_candidate_compatibility/ICLR_Embedding_Vector_Migration_backup_20260823_precleanup/paper_appendix_artifacts/data/full_63_cell_appendix_table.csv",
|
|
19
|
+
"supporting_artifact_sha256": "005d5957f4827e8cb133e8d101e3a0a95ecdf6f753af7c77d97e7c5a86b3ddc5",
|
|
20
|
+
"table": "APPENDIX_BUILD_REPORT.md",
|
|
21
|
+
"status": "retained_external",
|
|
22
|
+
"git_commit": null
|
|
23
|
+
},
|
|
24
|
+
"notes": [
|
|
25
|
+
"Aggregate development evidence, not a model-pair guarantee.",
|
|
26
|
+
"The 63 cells include the retained development matrix; frozen holdout counts are reported separately."
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"summary_id": "t2_v1_frozen_holdout_audit",
|
|
31
|
+
"kind": "t2_holdout_summary",
|
|
32
|
+
"cells": 28,
|
|
33
|
+
"compatible_cells": 22,
|
|
34
|
+
"safe_predictions": 17,
|
|
35
|
+
"observed_false_safe_predictions": 0,
|
|
36
|
+
"groups": [
|
|
37
|
+
{"source": "Qwen3-Embedding-0.6B", "target": "Qwen3-Embedding-4B", "cells": 7, "compatible": 6, "safe": 4, "false_safe": 0},
|
|
38
|
+
{"source": "MiniLM-L6", "target": "Qwen3-Embedding-8B", "cells": 7, "compatible": 3, "safe": 2, "false_safe": 0},
|
|
39
|
+
{"source": "Qwen3-Embedding-0.6B", "target": "Qwen3-Embedding-8B", "cells": 7, "compatible": 6, "safe": 4, "false_safe": 0},
|
|
40
|
+
{"source": "Qwen3-Embedding-4B", "target": "Qwen3-Embedding-8B", "cells": 7, "compatible": 7, "safe": 7, "false_safe": 0}
|
|
41
|
+
],
|
|
42
|
+
"provenance": {
|
|
43
|
+
"artifact": "final_paper_audit/t2_holdout_ledger.csv",
|
|
44
|
+
"artifact_sha256": "dfeca01f6bd1891bb2e4ba04cd0db1988ff273419f91c617a72d863a9bd92a47",
|
|
45
|
+
"table": "T2_HOLDOUT_AUDIT.md",
|
|
46
|
+
"status": "retained_external",
|
|
47
|
+
"git_commit": null
|
|
48
|
+
},
|
|
49
|
+
"notes": [
|
|
50
|
+
"Frozen leakage-safe holdout reconstruction; SAFE is an empirical diagnostic, not a guarantee.",
|
|
51
|
+
"False-safe is an observed holdout count, not a universal error bound."
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# T2-v1 frozen specification for Qwen3-8B target-shift validation
|
|
2
|
+
|
|
3
|
+
**Frozen before any Qwen3-8B target-shift feature/result inspection:**
|
|
4
|
+
2026-08-23
|
|
5
|
+
|
|
6
|
+
This is an exact copy of the previously frozen representative T2 rule. It
|
|
7
|
+
must not be altered during this experiment.
|
|
8
|
+
|
|
9
|
+
## Probe-only inputs and normalization
|
|
10
|
+
|
|
11
|
+
The probe may use source-ranked candidate IDs, source ranks and scores, exact
|
|
12
|
+
target scores for documents in the finite source candidate pool, and stable
|
|
13
|
+
target rerankings of source prefixes. Embeddings are treated as the existing
|
|
14
|
+
normalized vectors and candidate scores use the audited target/source dot
|
|
15
|
+
product convention in the saved candidate tables. No cross-model score
|
|
16
|
+
calibration is performed inside the probe.
|
|
17
|
+
|
|
18
|
+
The finite reference is `R_500`: target reranking restricted to the source
|
|
19
|
+
top-500 candidates. `R_500` is not native target retrieval.
|
|
20
|
+
|
|
21
|
+
The probe must not use qrels, native full-corpus target rankings, target nDCG,
|
|
22
|
+
candidate gaps, confidence intervals, or any retrospective compatibility
|
|
23
|
+
label.
|
|
24
|
+
|
|
25
|
+
## Frozen K values
|
|
26
|
+
|
|
27
|
+
`K = {10, 20, 50, 100, 200, 500}`. T2-v1 is evaluated at K=50 and uses
|
|
28
|
+
the source-top-500 finite reference.
|
|
29
|
+
|
|
30
|
+
## Frozen feature definitions
|
|
31
|
+
|
|
32
|
+
For each query and source prefix:
|
|
33
|
+
|
|
34
|
+
* `probe_residual_tail(K) = 1 - |Top10(R_K) ∩ Top10(R_500)| / 10`.
|
|
35
|
+
* `stability_to_500(K) = |Top10(R_K) ∩ Top10(R_500)| / 10`.
|
|
36
|
+
* `deepest_source_rank_top10` is the maximum original source rank in
|
|
37
|
+
`Top10(R_500)`; `deepest_p90` is its query-level 90th percentile.
|
|
38
|
+
* For each adjacent source-shell expansion `K→K'`, `p_any_entrant` is the
|
|
39
|
+
fraction of queries in which at least one newly exposed document enters
|
|
40
|
+
`Top10(R_K')`.
|
|
41
|
+
* `late_tail_area` is the prior finite-shell weighted summary of entrant
|
|
42
|
+
probabilities over expansions 50→100, 100→200, and 200→500.
|
|
43
|
+
* `fraction_margin_nonpositive` and `last_shell_any_rate` are computed with
|
|
44
|
+
the prior implementation and are used only by the fixed high-risk guard.
|
|
45
|
+
|
|
46
|
+
## Exact T2-v1 decision logic
|
|
47
|
+
|
|
48
|
+
At K=50, output `SAFE` iff all of the following are true:
|
|
49
|
+
|
|
50
|
+
1. mean `probe_residual_tail(50) <= 0.05`;
|
|
51
|
+
2. `deepest_p90 <= 200`;
|
|
52
|
+
3. `late_tail_area <= 0.20`.
|
|
53
|
+
|
|
54
|
+
If SAFE is false, output `UNSAFE_OR_UNCERTAIN` iff at least one of these
|
|
55
|
+
fixed guards is true:
|
|
56
|
+
|
|
57
|
+
* mean `probe_residual_tail(50) > 0.10`;
|
|
58
|
+
* mean `stability_to_500(50) < 0.90`;
|
|
59
|
+
* `last_shell_any_rate > 0.25`;
|
|
60
|
+
* `fraction_margin_nonpositive(50) > 0.50`.
|
|
61
|
+
|
|
62
|
+
Otherwise output `EXPAND`.
|
|
63
|
+
|
|
64
|
+
## Evaluation-only labels
|
|
65
|
+
|
|
66
|
+
Only after blind predictions are persisted may the evaluation join the
|
|
67
|
+
authoritative `G(50) = native target nDCG@10 − exact target rerank nDCG@10
|
|
68
|
+
within source top-50` labels. Practical compatibility is `G(50) <= 0.01`
|
|
69
|
+
using unrounded values. CI-NI fields are secondary evaluation labels.
|
|
70
|
+
|
|
71
|
+
**T2-v1 was frozen before any Qwen3-8B target-shift results were inspected.**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6ea348373d5ff257ac0fa4ce08207a84c647fb567012f2d64591fb057428e09a
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class SearchHit:
|
|
12
|
+
document_id: str
|
|
13
|
+
score: float
|
|
14
|
+
source_rank: int
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class VectorIndex(ABC):
|
|
18
|
+
dimension: int
|
|
19
|
+
metric: str
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def search(self, query_vector: np.ndarray, k: int) -> list[SearchHit]:
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def fetch_documents(self, ids: list[str]) -> dict[str, Any]:
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def size(self) -> int:
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def metadata(self) -> dict[str, Any]:
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
def close(self) -> None:
|
|
38
|
+
"""Release backend resources.
|
|
39
|
+
|
|
40
|
+
In-memory/FAISS indexes do not currently own a live resource, so the
|
|
41
|
+
default is a no-op. Backends such as local Qdrant use this hook to
|
|
42
|
+
release file locks and network clients when a serving engine stops.
|
|
43
|
+
"""
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def validate_query_vector(vector: np.ndarray, dimension: int) -> np.ndarray:
|
|
48
|
+
value = np.asarray(vector, dtype="float32")
|
|
49
|
+
if value.ndim != 1 or value.shape[0] != int(dimension):
|
|
50
|
+
raise ValueError(f"query vector dimension {value.shape} does not match index dimension {dimension}")
|
|
51
|
+
if not np.isfinite(value).all():
|
|
52
|
+
raise ValueError("query vector contains non-finite values")
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def validate_k(k: int) -> int:
|
|
57
|
+
"""Validate a requested top-k before it reaches a backend API."""
|
|
58
|
+
if isinstance(k, bool) or int(k) != k or int(k) < 1:
|
|
59
|
+
raise ValueError("k must be a positive integer")
|
|
60
|
+
return int(k)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from .base import SearchHit, VectorIndex, validate_k, validate_query_vector
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NumpyIndex(VectorIndex):
|
|
13
|
+
"""Tiny deterministic fallback used by the offline demo when FAISS is absent."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, vectors: np.ndarray, ids: list[str], path: str | Path | None = None,
|
|
16
|
+
metric: str = "cosine", documents: dict[str, Any] | None = None, metadata: dict[str, Any] | None = None):
|
|
17
|
+
self.vectors = np.asarray(vectors, dtype="float32"); self.ids = [str(x) for x in ids]; self.path = Path(path) if path else None
|
|
18
|
+
self.metric, self.documents = str(metric).lower(), documents or {}; self._metadata = dict(metadata or {})
|
|
19
|
+
if self.vectors.ndim != 2 or self.vectors.shape[0] < 1 or self.vectors.shape[0] != len(self.ids) or self.vectors.shape[1] < 1:
|
|
20
|
+
raise ValueError("invalid numpy index vectors/IDs: expected a non-empty 2-D matrix aligned with IDs")
|
|
21
|
+
if len(set(self.ids)) != len(self.ids):
|
|
22
|
+
raise ValueError("duplicate document IDs are not allowed")
|
|
23
|
+
if self.metric not in {"cosine", "dot", "inner_product"}:
|
|
24
|
+
raise ValueError("metric must be cosine, dot, or inner_product")
|
|
25
|
+
if not np.isfinite(self.vectors).all():
|
|
26
|
+
raise ValueError("index vectors must be finite")
|
|
27
|
+
self.dimension = self.vectors.shape[1]
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def load(cls, path: str | Path, documents: dict[str, Any] | None = None, metric: str = "cosine") -> NumpyIndex:
|
|
31
|
+
try:
|
|
32
|
+
with Path(path).open("rb") as handle:
|
|
33
|
+
data = np.load(handle, allow_pickle=False)
|
|
34
|
+
vectors, ids = data["vectors"], data["ids"].tolist()
|
|
35
|
+
if not isinstance(ids, list):
|
|
36
|
+
raise ValueError("embedded IDs must be a one-dimensional array")
|
|
37
|
+
except (OSError, ValueError, KeyError, TypeError, UnicodeError, EOFError) as exc:
|
|
38
|
+
raise ValueError(f"invalid NumPy index artifact: {path}") from exc
|
|
39
|
+
meta_path = Path(path).with_suffix(Path(path).suffix + ".meta.json")
|
|
40
|
+
try:
|
|
41
|
+
meta = json.loads(meta_path.read_text()) if meta_path.exists() else {}
|
|
42
|
+
if not isinstance(meta, dict):
|
|
43
|
+
raise ValueError("metadata must be a JSON object")
|
|
44
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
|
45
|
+
raise ValueError(f"invalid NumPy index metadata: {meta_path}") from exc
|
|
46
|
+
if not isinstance(meta, dict):
|
|
47
|
+
raise ValueError(f"invalid NumPy index metadata: {meta_path}")
|
|
48
|
+
return cls(vectors, ids, path, metric, documents, meta)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def build(cls, vectors: np.ndarray, ids: list[str], path: str | Path | None = None,
|
|
52
|
+
metric: str = "cosine", documents: dict[str, Any] | None = None, metadata: dict[str, Any] | None = None,
|
|
53
|
+
ids_path: str | Path | None = None) -> NumpyIndex:
|
|
54
|
+
x = np.asarray(vectors, dtype="float32")
|
|
55
|
+
metric = str(metric).lower()
|
|
56
|
+
if metric not in {"cosine", "dot", "inner_product"}:
|
|
57
|
+
raise ValueError("metric must be cosine, dot, or inner_product")
|
|
58
|
+
if x.ndim != 2 or x.shape[1] < 1 or x.shape[0] == 0:
|
|
59
|
+
raise ValueError("vectors must be a non-empty 2-D matrix")
|
|
60
|
+
if metric == "cosine":
|
|
61
|
+
norms = np.linalg.norm(x, axis=1, keepdims=True)
|
|
62
|
+
if np.any(norms <= 1e-12): raise ValueError("cosine vectors must be non-zero")
|
|
63
|
+
x = x / norms
|
|
64
|
+
result = cls(x, ids, path, metric, documents=documents, metadata=metadata)
|
|
65
|
+
if path:
|
|
66
|
+
target = Path(path); target.parent.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
with target.open("wb") as handle: np.savez(handle, vectors=x, ids=np.asarray(ids, dtype="U"))
|
|
68
|
+
id_target = Path(ids_path) if ids_path else target.with_suffix(target.suffix + ".ids.json")
|
|
69
|
+
id_target.parent.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
id_target.write_text(json.dumps(ids))
|
|
71
|
+
if metadata:
|
|
72
|
+
target.with_suffix(target.suffix + ".meta.json").write_text(json.dumps(metadata, indent=2, sort_keys=True))
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
def search(self, query_vector: np.ndarray, k: int) -> list[SearchHit]:
|
|
76
|
+
k = validate_k(k)
|
|
77
|
+
q = validate_query_vector(query_vector, self.dimension)
|
|
78
|
+
if self.metric == "cosine":
|
|
79
|
+
norm = float(np.linalg.norm(q))
|
|
80
|
+
if norm <= 1e-12:
|
|
81
|
+
raise ValueError("cosine query vector must be non-zero")
|
|
82
|
+
q = q / norm
|
|
83
|
+
scores = self.vectors @ q; order = np.lexsort((np.arange(len(scores)), -scores))[:min(k, len(self.ids))]
|
|
84
|
+
return [SearchHit(self.ids[int(i)], float(scores[i]), rank) for rank, i in enumerate(order)]
|
|
85
|
+
|
|
86
|
+
def fetch_documents(self, ids: list[str]) -> dict[str, Any]: return {str(i): self.documents[str(i)] for i in ids if str(i) in self.documents}
|
|
87
|
+
def size(self) -> int: return len(self.ids)
|
|
88
|
+
def metadata(self) -> dict[str, Any]: return {"backend": "numpy_fallback", "dimension": self.dimension, "metric": self.metric, "size": self.size(), **self._metadata}
|
|
89
|
+
def close(self) -> None: return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class FaissIndex(VectorIndex):
|
|
93
|
+
"""FAISS wrapper supporting saved indexes and small in-process indexes."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, index: Any, ids: list[str], path: str | Path | None = None,
|
|
96
|
+
metric: str = "cosine", metadata: dict[str, Any] | None = None,
|
|
97
|
+
documents: dict[str, Any] | None = None):
|
|
98
|
+
self.index = index
|
|
99
|
+
self.ids = [str(x) for x in ids]
|
|
100
|
+
self.path = Path(path) if path else None
|
|
101
|
+
self.dimension = int(index.d)
|
|
102
|
+
self.metric = metric
|
|
103
|
+
self._metadata = dict(metadata or {})
|
|
104
|
+
self.documents = documents or {}
|
|
105
|
+
if int(index.ntotal) != len(self.ids):
|
|
106
|
+
raise ValueError(f"FAISS index contains {index.ntotal} rows but {len(self.ids)} IDs were supplied")
|
|
107
|
+
if len(set(self.ids)) != len(self.ids):
|
|
108
|
+
raise ValueError("duplicate document IDs are not allowed")
|
|
109
|
+
if self.dimension < 1:
|
|
110
|
+
raise ValueError("FAISS index dimension must be positive")
|
|
111
|
+
if str(metric).lower() not in {"cosine", "dot", "inner_product"}:
|
|
112
|
+
raise ValueError("metric must be cosine, dot, or inner_product")
|
|
113
|
+
self.metric = str(metric).lower()
|
|
114
|
+
self._metadata.update({"backend": "faiss", "dimension": self.dimension, "metric": self.metric,
|
|
115
|
+
"size": len(self.ids), "index_type": type(index).__name__})
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def load(cls, path: str | Path, ids_path: str | Path | None = None,
|
|
119
|
+
metric: str = "cosine", documents: dict[str, Any] | None = None,
|
|
120
|
+
nprobe: int | None = None) -> FaissIndex:
|
|
121
|
+
try:
|
|
122
|
+
import faiss
|
|
123
|
+
except ImportError as exc:
|
|
124
|
+
raise RuntimeError("FAISS backend requires faiss-cpu or faiss-gpu") from exc
|
|
125
|
+
path = Path(path)
|
|
126
|
+
if not path.exists():
|
|
127
|
+
raise FileNotFoundError(path)
|
|
128
|
+
try:
|
|
129
|
+
index = faiss.read_index(str(path))
|
|
130
|
+
except (OSError, RuntimeError, ValueError) as exc:
|
|
131
|
+
raise ValueError(f"invalid FAISS index artifact: {path}") from exc
|
|
132
|
+
candidates = [Path(ids_path)] if ids_path else []
|
|
133
|
+
candidates += [Path(str(path) + ".ids.json"), path.with_suffix(path.suffix + ".ids.json"), path.with_suffix(".ids.json")]
|
|
134
|
+
id_file = next((p for p in candidates if p.exists()), None)
|
|
135
|
+
if id_file is None:
|
|
136
|
+
raise FileNotFoundError(f"missing FAISS document ID sidecar; tried {[str(x) for x in candidates]}")
|
|
137
|
+
if nprobe is not None and (isinstance(nprobe, bool) or int(nprobe) != nprobe or int(nprobe) < 1):
|
|
138
|
+
raise ValueError("nprobe must be a positive integer")
|
|
139
|
+
if nprobe is not None and hasattr(index, "nprobe"):
|
|
140
|
+
index.nprobe = min(max(1, int(nprobe)), int(getattr(index, "nlist", nprobe)))
|
|
141
|
+
try:
|
|
142
|
+
raw = json.loads(id_file.read_text())
|
|
143
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
|
144
|
+
raise ValueError(f"invalid FAISS IDs sidecar: {id_file}") from exc
|
|
145
|
+
ids = raw.get("ids", raw) if isinstance(raw, dict) else raw
|
|
146
|
+
if not isinstance(ids, list):
|
|
147
|
+
raise ValueError(f"invalid FAISS IDs sidecar: {id_file}")
|
|
148
|
+
meta_file = path.with_suffix(path.suffix + ".meta.json")
|
|
149
|
+
try:
|
|
150
|
+
meta = json.loads(meta_file.read_text()) if meta_file.exists() else {}
|
|
151
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
|
152
|
+
raise ValueError(f"invalid FAISS metadata: {meta_file}") from exc
|
|
153
|
+
if not isinstance(meta, dict):
|
|
154
|
+
raise ValueError(f"invalid FAISS metadata: {meta_file}")
|
|
155
|
+
return cls(index, [str(x) for x in ids], path=path, metric=metric, metadata=meta, documents=documents)
|
|
156
|
+
|
|
157
|
+
@classmethod
|
|
158
|
+
def build(cls, vectors: np.ndarray, ids: list[str], path: str | Path | None = None,
|
|
159
|
+
metric: str = "cosine", nlist: int | None = None, nprobe: int = 64,
|
|
160
|
+
documents: dict[str, Any] | None = None, metadata: dict[str, Any] | None = None,
|
|
161
|
+
ids_path: str | Path | None = None) -> FaissIndex:
|
|
162
|
+
try:
|
|
163
|
+
import faiss
|
|
164
|
+
except ImportError as exc:
|
|
165
|
+
raise RuntimeError("FAISS backend requires faiss-cpu or faiss-gpu") from exc
|
|
166
|
+
x = np.asarray(vectors, dtype="float32")
|
|
167
|
+
metric = str(metric).lower()
|
|
168
|
+
if metric not in {"cosine", "dot", "inner_product"}:
|
|
169
|
+
raise ValueError("metric must be cosine, dot, or inner_product")
|
|
170
|
+
if isinstance(nprobe, bool) or int(nprobe) != nprobe or int(nprobe) < 1:
|
|
171
|
+
raise ValueError("nprobe must be a positive integer")
|
|
172
|
+
if nlist is not None and (isinstance(nlist, bool) or int(nlist) != nlist or int(nlist) < 1):
|
|
173
|
+
raise ValueError("nlist must be a positive integer when provided")
|
|
174
|
+
if x.ndim != 2 or x.shape[0] == 0 or x.shape[1] < 1 or x.shape[0] != len(ids) or not np.isfinite(x).all():
|
|
175
|
+
raise ValueError("vectors must be finite 2-D data aligned with unique IDs")
|
|
176
|
+
if len(set(map(str, ids))) != len(ids):
|
|
177
|
+
raise ValueError("duplicate document IDs are not allowed")
|
|
178
|
+
if metric.lower() == "cosine":
|
|
179
|
+
norms = np.linalg.norm(x, axis=1, keepdims=True)
|
|
180
|
+
if np.any(norms <= 1e-12): raise ValueError("cosine vectors must be non-zero")
|
|
181
|
+
x = x / norms
|
|
182
|
+
d = x.shape[1]
|
|
183
|
+
if nlist and len(x) >= max(39, nlist * 4):
|
|
184
|
+
quantizer = faiss.IndexFlatIP(d)
|
|
185
|
+
index = faiss.IndexIVFFlat(quantizer, d, min(int(nlist), len(x)), faiss.METRIC_INNER_PRODUCT)
|
|
186
|
+
index.train(x)
|
|
187
|
+
index.nprobe = min(int(nprobe), int(index.nlist))
|
|
188
|
+
else:
|
|
189
|
+
index = faiss.IndexFlatIP(d)
|
|
190
|
+
index.add(x)
|
|
191
|
+
result = cls(index, [str(x) for x in ids], path=path, metric=metric,
|
|
192
|
+
metadata={"nprobe": nprobe, "nlist": nlist, **(metadata or {})}, documents=documents)
|
|
193
|
+
if path:
|
|
194
|
+
result.save(path, ids_path=ids_path)
|
|
195
|
+
return result
|
|
196
|
+
|
|
197
|
+
def save(self, path: str | Path | None = None, ids_path: str | Path | None = None) -> None:
|
|
198
|
+
try:
|
|
199
|
+
import faiss
|
|
200
|
+
except ImportError as exc:
|
|
201
|
+
raise RuntimeError("FAISS backend requires faiss-cpu or faiss-gpu") from exc
|
|
202
|
+
target = Path(path or self.path or "legacy.index")
|
|
203
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
204
|
+
faiss.write_index(self.index, str(target))
|
|
205
|
+
id_target = Path(ids_path) if ids_path else target.with_suffix(target.suffix + ".ids.json")
|
|
206
|
+
id_target.parent.mkdir(parents=True, exist_ok=True)
|
|
207
|
+
id_target.write_text(json.dumps(self.ids, ensure_ascii=False))
|
|
208
|
+
target.with_suffix(target.suffix + ".meta.json").write_text(json.dumps(self.metadata(), indent=2, sort_keys=True))
|
|
209
|
+
self.path = target
|
|
210
|
+
|
|
211
|
+
def search(self, query_vector: np.ndarray, k: int) -> list[SearchHit]:
|
|
212
|
+
k = validate_k(k)
|
|
213
|
+
value = validate_query_vector(query_vector, self.dimension)
|
|
214
|
+
if self.metric == "cosine":
|
|
215
|
+
norm = float(np.linalg.norm(value))
|
|
216
|
+
if norm <= 1e-12:
|
|
217
|
+
raise ValueError("cosine query vector must be non-zero")
|
|
218
|
+
value = value / norm
|
|
219
|
+
value = value[None, :]
|
|
220
|
+
scores, indices = self.index.search(value, min(k, len(self.ids)))
|
|
221
|
+
return [SearchHit(self.ids[int(i)], float(score), rank) for rank, (score, i) in enumerate(zip(scores[0], indices[0])) if int(i) >= 0]
|
|
222
|
+
|
|
223
|
+
def fetch_documents(self, ids: list[str]) -> dict[str, Any]:
|
|
224
|
+
return {str(i): self.documents[str(i)] for i in ids if str(i) in self.documents}
|
|
225
|
+
|
|
226
|
+
def size(self) -> int:
|
|
227
|
+
return len(self.ids)
|
|
228
|
+
|
|
229
|
+
def metadata(self) -> dict[str, Any]:
|
|
230
|
+
return dict(self._metadata)
|
|
231
|
+
|
|
232
|
+
def close(self) -> None:
|
|
233
|
+
# FAISS index objects are in-memory Python wrappers; there is no live
|
|
234
|
+
# client to close. Keep the hook symmetric with other backends.
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def load_faiss_index(path: str | Path, documents: dict[str, Any] | None = None,
|
|
239
|
+
metric: str = "cosine", nprobe: int | None = None) -> FaissIndex:
|
|
240
|
+
return FaissIndex.load(path, metric=metric, documents=documents, nprobe=nprobe)
|