victor-codegraph 0.9.0__tar.gz → 0.10.0__tar.gz
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.
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/PKG-INFO +32 -4
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/README.md +29 -3
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/pyproject.toml +5 -1
- victor_codegraph-0.10.0/tests/test_embedding_contract.py +145 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/__init__.py +17 -1
- victor_codegraph-0.10.0/victor_codegraph/embeddings.py +196 -0
- victor_codegraph-0.10.0/victor_codegraph/py.typed +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph.egg-info/PKG-INFO +32 -4
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph.egg-info/SOURCES.txt +3 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph.egg-info/requires.txt +3 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/setup.cfg +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_adapter.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_adapter_validation_v2.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_chunk_contract_v2.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_diagnostics_v2.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_identity_v2.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_line_base_contract.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_manifest.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_python_parser.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_relations_v2.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_repo.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_repository_delta_v2.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_sizing_and_chunk.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_sizing_overlap.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_symbol_oid_contract.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_symbol_oid_parity.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_treesitter_jsts.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_treesitter_languages.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/tests/test_treesitter_relations.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/adapter.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/config.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/javascript_repository.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/languages.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/manifest.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/model.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/parser.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/python_parser.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/repo.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/repository_index.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/resolution.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/rust_repository.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/sizing.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/treesitter_parser.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/treesitter_tables.py +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph.egg-info/dependency_links.txt +0 -0
- {victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: victor-codegraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Repository-to-Code-Semantic-Graph compiler with stable identity, bounded chunks, incremental deltas, and ProximaRecord projection.
|
|
5
5
|
Author-email: Vijaykumar Singh <vijay@anvaiops.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,6 +16,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries
|
|
17
17
|
Requires-Python: >=3.12
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
|
+
Provides-Extra: embeddings
|
|
20
|
+
Requires-Dist: sentence-transformers<6,>=3; extra == "embeddings"
|
|
19
21
|
Provides-Extra: treesitter
|
|
20
22
|
Requires-Dist: tree-sitter>=0.26.0; extra == "treesitter"
|
|
21
23
|
Requires-Dist: tree-sitter-language-pack>=1.21.3; extra == "treesitter"
|
|
@@ -54,7 +56,7 @@ gaps:
|
|
|
54
56
|
## Install
|
|
55
57
|
|
|
56
58
|
Published on PyPI as [`victor-codegraph`](https://pypi.org/project/victor-codegraph/).
|
|
57
|
-
This source tree is version **0.
|
|
59
|
+
This source tree is version **0.10.0**; consumers should use a compatible `<1.0` pin.
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
62
|
# from PyPI
|
|
@@ -126,7 +128,7 @@ records = to_proxima_records(repository, repo_graph_id="myrepo")
|
|
|
126
128
|
|
|
127
129
|
## Status
|
|
128
130
|
|
|
129
|
-
`0.
|
|
131
|
+
`0.10.0` source version. Python (`ast`) is the full, offline frontend. JS/TS, Go, Rust,
|
|
130
132
|
Java, C, and C++ have conformance fixtures; other mapped tree-sitter languages report
|
|
131
133
|
symbols-tier or fallback status rather than implying full fidelity.
|
|
132
134
|
|
|
@@ -143,4 +145,30 @@ workspace-inherited dependencies, re-export chains, macros and unknown receiver
|
|
|
143
145
|
types retain unresolved references. Glob imports retain a module dependency but
|
|
144
146
|
do not guess call targets. This is a conservative static graph, not rustc name
|
|
145
147
|
resolution or evidence that a call executes. Publish this package before a Victor
|
|
146
|
-
release that requires its
|
|
148
|
+
release that requires its exact repository graph contract.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### Embedding conformance (0.10.0)
|
|
152
|
+
|
|
153
|
+
The accepted FEP-0040 supplies store-neutral
|
|
154
|
+
`EmbeddingProfile`, `SymbolEmbedding`, `embed_symbols` and an optional
|
|
155
|
+
`SentenceTransformerEncoder`. The base compiler stays dependency-free. The
|
|
156
|
+
`embeddings` extra loads sentence-transformers only when explicitly selected;
|
|
157
|
+
private source is not sent to a remote model by default.
|
|
158
|
+
|
|
159
|
+
The contract uses canonical compiler IDs and `symbol-summary-v1` (name,
|
|
160
|
+
signature and documentation), with an immutable model revision, dimensions,
|
|
161
|
+
token limit, normalization, query prefix and separate input/source hashes.
|
|
162
|
+
Inference errors propagate; hash placeholders, zero vectors and malformed batches
|
|
163
|
+
are not semantic evidence. File/container symbols marked `embedding_excluded`
|
|
164
|
+
remain graph evidence only.
|
|
165
|
+
|
|
166
|
+
These APIs are introduced in 0.10.0 and are unavailable in 0.9.0. Install the
|
|
167
|
+
exact published `victor-codegraph[treesitter,embeddings]==0.10.0` when selecting
|
|
168
|
+
local model execution; the base package has no mandatory model dependency.
|
|
169
|
+
Current evidence covers
|
|
170
|
+
synthetic document-vector persistence parity and local LanceDB collection/query
|
|
171
|
+
profile enforcement. Labeled relevance and hosted lifecycle remain separate
|
|
172
|
+
acceptance gates. Victor binds a new LanceDB table to the complete profile in its
|
|
173
|
+
Arrow schema and rejects incompatible query encoders and mixed/orphan row markers.
|
|
174
|
+
Legacy tables require a fresh destination; they are never silently relabeled.
|
|
@@ -25,7 +25,7 @@ gaps:
|
|
|
25
25
|
## Install
|
|
26
26
|
|
|
27
27
|
Published on PyPI as [`victor-codegraph`](https://pypi.org/project/victor-codegraph/).
|
|
28
|
-
This source tree is version **0.
|
|
28
|
+
This source tree is version **0.10.0**; consumers should use a compatible `<1.0` pin.
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
# from PyPI
|
|
@@ -97,7 +97,7 @@ records = to_proxima_records(repository, repo_graph_id="myrepo")
|
|
|
97
97
|
|
|
98
98
|
## Status
|
|
99
99
|
|
|
100
|
-
`0.
|
|
100
|
+
`0.10.0` source version. Python (`ast`) is the full, offline frontend. JS/TS, Go, Rust,
|
|
101
101
|
Java, C, and C++ have conformance fixtures; other mapped tree-sitter languages report
|
|
102
102
|
symbols-tier or fallback status rather than implying full fidelity.
|
|
103
103
|
|
|
@@ -114,4 +114,30 @@ workspace-inherited dependencies, re-export chains, macros and unknown receiver
|
|
|
114
114
|
types retain unresolved references. Glob imports retain a module dependency but
|
|
115
115
|
do not guess call targets. This is a conservative static graph, not rustc name
|
|
116
116
|
resolution or evidence that a call executes. Publish this package before a Victor
|
|
117
|
-
release that requires its
|
|
117
|
+
release that requires its exact repository graph contract.
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
### Embedding conformance (0.10.0)
|
|
121
|
+
|
|
122
|
+
The accepted FEP-0040 supplies store-neutral
|
|
123
|
+
`EmbeddingProfile`, `SymbolEmbedding`, `embed_symbols` and an optional
|
|
124
|
+
`SentenceTransformerEncoder`. The base compiler stays dependency-free. The
|
|
125
|
+
`embeddings` extra loads sentence-transformers only when explicitly selected;
|
|
126
|
+
private source is not sent to a remote model by default.
|
|
127
|
+
|
|
128
|
+
The contract uses canonical compiler IDs and `symbol-summary-v1` (name,
|
|
129
|
+
signature and documentation), with an immutable model revision, dimensions,
|
|
130
|
+
token limit, normalization, query prefix and separate input/source hashes.
|
|
131
|
+
Inference errors propagate; hash placeholders, zero vectors and malformed batches
|
|
132
|
+
are not semantic evidence. File/container symbols marked `embedding_excluded`
|
|
133
|
+
remain graph evidence only.
|
|
134
|
+
|
|
135
|
+
These APIs are introduced in 0.10.0 and are unavailable in 0.9.0. Install the
|
|
136
|
+
exact published `victor-codegraph[treesitter,embeddings]==0.10.0` when selecting
|
|
137
|
+
local model execution; the base package has no mandatory model dependency.
|
|
138
|
+
Current evidence covers
|
|
139
|
+
synthetic document-vector persistence parity and local LanceDB collection/query
|
|
140
|
+
profile enforcement. Labeled relevance and hosted lifecycle remain separate
|
|
141
|
+
acceptance gates. Victor binds a new LanceDB table to the complete profile in its
|
|
142
|
+
Arrow schema and rejects incompatible query encoders and mixed/orphan row markers.
|
|
143
|
+
Legacy tables require a fresh destination; they are never silently relabeled.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "victor-codegraph"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.10.0"
|
|
8
8
|
description = "Repository-to-Code-Semantic-Graph compiler with stable identity, bounded chunks, incremental deltas, and ProximaRecord projection."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -40,6 +40,7 @@ classifiers = [
|
|
|
40
40
|
dependencies = []
|
|
41
41
|
|
|
42
42
|
[project.optional-dependencies]
|
|
43
|
+
embeddings = ["sentence-transformers>=3,<6"]
|
|
43
44
|
treesitter = [
|
|
44
45
|
"tree-sitter>=0.26.0",
|
|
45
46
|
"tree-sitter-language-pack>=1.21.3",
|
|
@@ -77,3 +78,6 @@ select = ["E4", "E7", "E9", "F"]
|
|
|
77
78
|
[tool.pytest.ini_options]
|
|
78
79
|
testpaths = ["tests"]
|
|
79
80
|
addopts = ""
|
|
81
|
+
|
|
82
|
+
[tool.setuptools.package-data]
|
|
83
|
+
victor_codegraph = ["py.typed"]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""One semantic input/provenance contract for local and hosted consumers."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import replace
|
|
4
|
+
import pytest
|
|
5
|
+
from victor_codegraph import parse
|
|
6
|
+
from victor_codegraph.embeddings import (
|
|
7
|
+
BGE_SMALL_PROFILE,
|
|
8
|
+
embedding_text,
|
|
9
|
+
embed_symbols,
|
|
10
|
+
validate_vectors,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_same_symbols_produce_store_independent_embedding_evidence():
|
|
15
|
+
symbols = parse(
|
|
16
|
+
'def pay(x):\n """Charge a customer."""\n return x\n', file_path="billing.py"
|
|
17
|
+
).symbols
|
|
18
|
+
seen = []
|
|
19
|
+
|
|
20
|
+
def encode(texts):
|
|
21
|
+
seen.extend(texts)
|
|
22
|
+
return [[1.0] + [0.0] * 383 for _ in texts]
|
|
23
|
+
|
|
24
|
+
documents = embed_symbols(symbols, encode, BGE_SMALL_PROFILE)
|
|
25
|
+
assert seen == [embedding_text(s.simple_name, s.signature, s.documentation) for s in symbols]
|
|
26
|
+
assert [d.symbol_id for d in documents] == [s.id for s in symbols]
|
|
27
|
+
assert all(d.profile_id == BGE_SMALL_PROFILE.identity for d in documents)
|
|
28
|
+
assert all(len(d.input_hash) == 64 and len(d.source_hash) == 64 for d in documents)
|
|
29
|
+
assert all(len(d.values) == 384 for d in documents)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize(
|
|
33
|
+
"vectors",
|
|
34
|
+
[[], [[0.0, 0.0]], [[True, 1.0]], [[float("nan"), 1.0]], [[1.0]], [[float("inf"), 1.0]]],
|
|
35
|
+
)
|
|
36
|
+
def test_invalid_or_missing_vectors_fail_without_substitution(vectors):
|
|
37
|
+
with pytest.raises(ValueError):
|
|
38
|
+
validate_vectors(vectors, count=1, dimension=2)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_model_preprocessing_changes_invalidate_profile_identity():
|
|
42
|
+
p = BGE_SMALL_PROFILE
|
|
43
|
+
assert (
|
|
44
|
+
len(
|
|
45
|
+
{
|
|
46
|
+
p.identity,
|
|
47
|
+
replace(p, revision="a" * 40).identity,
|
|
48
|
+
replace(p, normalize=True).identity,
|
|
49
|
+
replace(p, query_prefix="query: ").identity,
|
|
50
|
+
replace(p, max_tokens=256).identity,
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
== 5
|
|
54
|
+
)
|
|
55
|
+
with pytest.raises(ValueError, match="revision"):
|
|
56
|
+
replace(p, revision="main")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_failed_encoder_propagates_and_duplicate_symbol_ids_are_rejected():
|
|
60
|
+
symbols = parse("def f(): pass", file_path="m.py").symbols
|
|
61
|
+
|
|
62
|
+
def fail(texts):
|
|
63
|
+
raise RuntimeError("model unavailable")
|
|
64
|
+
|
|
65
|
+
with pytest.raises(RuntimeError, match="model unavailable"):
|
|
66
|
+
embed_symbols(symbols, fail, BGE_SMALL_PROFILE)
|
|
67
|
+
with pytest.raises(ValueError, match="duplicate"):
|
|
68
|
+
embed_symbols(symbols * 2, fail, BGE_SMALL_PROFILE)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_unbounded_encoder_outputs_are_rejected_after_one_overflow_item():
|
|
72
|
+
from itertools import repeat
|
|
73
|
+
|
|
74
|
+
with pytest.raises(ValueError, match="dimension"):
|
|
75
|
+
validate_vectors([repeat(1.0)], count=1, dimension=2)
|
|
76
|
+
with pytest.raises(ValueError, match="exactly one"):
|
|
77
|
+
validate_vectors(repeat([1.0, 2.0]), count=1, dimension=2)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_optional_encoder_owns_one_pinned_model_and_query_preprocessing(monkeypatch):
|
|
81
|
+
import asyncio
|
|
82
|
+
import sys
|
|
83
|
+
from types import SimpleNamespace
|
|
84
|
+
from victor_codegraph.embeddings import SentenceTransformerEncoder
|
|
85
|
+
|
|
86
|
+
calls = []
|
|
87
|
+
|
|
88
|
+
class Model:
|
|
89
|
+
max_seq_length = 0
|
|
90
|
+
|
|
91
|
+
def __init__(self, model_id, **kwargs):
|
|
92
|
+
calls.append((model_id, kwargs))
|
|
93
|
+
|
|
94
|
+
def get_sentence_embedding_dimension(self):
|
|
95
|
+
return 3
|
|
96
|
+
|
|
97
|
+
def encode(self, texts, **kwargs):
|
|
98
|
+
calls.append((texts, kwargs, self.max_seq_length))
|
|
99
|
+
return [[1.0, 2.0, 3.0] for _ in texts]
|
|
100
|
+
|
|
101
|
+
monkeypatch.setitem(
|
|
102
|
+
sys.modules, "sentence_transformers", SimpleNamespace(SentenceTransformer=Model)
|
|
103
|
+
)
|
|
104
|
+
profile = replace(BGE_SMALL_PROFILE, dimension=3, query_prefix="query: ", max_tokens=256)
|
|
105
|
+
encoder = SentenceTransformerEncoder(profile)
|
|
106
|
+
assert encoder.encode([]) == [] and calls == []
|
|
107
|
+
assert encoder.encode_query("permissions") == [1.0, 2.0, 3.0]
|
|
108
|
+
assert asyncio.run(encoder.embed_text("symbol")) == [1.0, 2.0, 3.0]
|
|
109
|
+
assert len(calls) == 3 # one model load, two bounded inference calls
|
|
110
|
+
assert calls[0] == (
|
|
111
|
+
profile.model_id,
|
|
112
|
+
{"revision": profile.revision, "device": "cpu", "trust_remote_code": False},
|
|
113
|
+
)
|
|
114
|
+
assert calls[1][0] == ["query: permissions"]
|
|
115
|
+
assert calls[1][1]["normalize_embeddings"] is False
|
|
116
|
+
assert calls[1][2] == 256
|
|
117
|
+
with pytest.raises(ValueError, match="512"):
|
|
118
|
+
encoder.encode(["x"] * 513)
|
|
119
|
+
with pytest.raises(ValueError, match="dimension"):
|
|
120
|
+
SentenceTransformerEncoder(BGE_SMALL_PROFILE).encode(["x"])
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_optional_encoder_missing_dependency_is_explicit(monkeypatch):
|
|
124
|
+
import sys
|
|
125
|
+
from victor_codegraph.embeddings import SentenceTransformerEncoder
|
|
126
|
+
|
|
127
|
+
monkeypatch.setitem(sys.modules, "sentence_transformers", None)
|
|
128
|
+
with pytest.raises(ImportError):
|
|
129
|
+
SentenceTransformerEncoder().encode(["symbol"])
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.mark.parametrize(
|
|
133
|
+
"changes",
|
|
134
|
+
[
|
|
135
|
+
{"model_id": ""},
|
|
136
|
+
{"revision": "main"},
|
|
137
|
+
{"dimension": True},
|
|
138
|
+
{"max_tokens": 0},
|
|
139
|
+
{"text_contract": "body-v1"},
|
|
140
|
+
{"normalize": 1},
|
|
141
|
+
],
|
|
142
|
+
)
|
|
143
|
+
def test_invalid_profile_cannot_claim_semantic_compatibility(changes):
|
|
144
|
+
with pytest.raises(ValueError):
|
|
145
|
+
replace(BGE_SMALL_PROFILE, **changes)
|
|
@@ -14,6 +14,15 @@ from __future__ import annotations
|
|
|
14
14
|
|
|
15
15
|
from .adapter import relation_to_record, symbol_to_record, to_proxima_records
|
|
16
16
|
from .config import ChunkConfig
|
|
17
|
+
from .embeddings import (
|
|
18
|
+
BGE_SMALL_PROFILE,
|
|
19
|
+
EmbeddingProfile,
|
|
20
|
+
SymbolEmbedding,
|
|
21
|
+
SentenceTransformerEncoder,
|
|
22
|
+
embedding_text,
|
|
23
|
+
embed_symbols,
|
|
24
|
+
validate_vectors,
|
|
25
|
+
)
|
|
17
26
|
from .languages import detect_language
|
|
18
27
|
from .model import (
|
|
19
28
|
CapabilityTier,
|
|
@@ -45,10 +54,17 @@ from .repository_index import (
|
|
|
45
54
|
relation_key,
|
|
46
55
|
)
|
|
47
56
|
|
|
48
|
-
__version__ = "0.
|
|
57
|
+
__version__ = "0.10.0"
|
|
49
58
|
|
|
50
59
|
__all__ = [
|
|
51
60
|
"__version__",
|
|
61
|
+
"BGE_SMALL_PROFILE",
|
|
62
|
+
"EmbeddingProfile",
|
|
63
|
+
"SymbolEmbedding",
|
|
64
|
+
"SentenceTransformerEncoder",
|
|
65
|
+
"embedding_text",
|
|
66
|
+
"embed_symbols",
|
|
67
|
+
"validate_vectors",
|
|
52
68
|
"chunk",
|
|
53
69
|
"parse",
|
|
54
70
|
"chunk_repo",
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Store-neutral semantic embedding evidence; model dependencies are optional.
|
|
2
|
+
|
|
3
|
+
Inference and publication failures propagate. Graph identity is supplied by the
|
|
4
|
+
compiler; this module neither derives replacement IDs nor writes to a database.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import asdict, dataclass
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
from itertools import islice
|
|
13
|
+
import math
|
|
14
|
+
from numbers import Real
|
|
15
|
+
import re
|
|
16
|
+
from typing import Any, Callable, Iterable, Sequence
|
|
17
|
+
|
|
18
|
+
from .model import CodeSymbol
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def embedding_text(name: str, signature: str | None, documentation: str | None) -> str:
|
|
22
|
+
"""The symbol-summary-v1 input, identical to Victor's existing node text."""
|
|
23
|
+
return " ".join(filter(None, [name, signature or "", documentation or ""]))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class EmbeddingProfile:
|
|
28
|
+
"""Complete compatibility key for document and query vectors."""
|
|
29
|
+
|
|
30
|
+
model_id: str
|
|
31
|
+
revision: str
|
|
32
|
+
dimension: int
|
|
33
|
+
text_contract: str = "symbol-summary-v1"
|
|
34
|
+
max_tokens: int = 512
|
|
35
|
+
normalize: bool = False
|
|
36
|
+
query_prefix: str = ""
|
|
37
|
+
|
|
38
|
+
def __post_init__(self) -> None:
|
|
39
|
+
if not isinstance(self.model_id, str) or not self.model_id.strip():
|
|
40
|
+
raise ValueError("model_id is required")
|
|
41
|
+
if not isinstance(self.revision, str) or not re.fullmatch(r"[0-9a-f]{40}", self.revision):
|
|
42
|
+
raise ValueError("model revision must be an immutable 40-hex commit")
|
|
43
|
+
if type(self.dimension) is not int or not 1 <= self.dimension <= 65536:
|
|
44
|
+
raise ValueError("invalid embedding dimension")
|
|
45
|
+
if type(self.max_tokens) is not int or not 1 <= self.max_tokens <= 32768:
|
|
46
|
+
raise ValueError("invalid embedding token limit")
|
|
47
|
+
if self.text_contract != "symbol-summary-v1":
|
|
48
|
+
raise ValueError("unsupported embedding text contract")
|
|
49
|
+
if type(self.normalize) is not bool or not isinstance(self.query_prefix, str):
|
|
50
|
+
raise ValueError("invalid embedding preprocessing")
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def identity(self) -> str:
|
|
54
|
+
return _digest(json.dumps(asdict(self), sort_keys=True, separators=(",", ":")))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
BGE_SMALL_PROFILE = EmbeddingProfile(
|
|
58
|
+
model_id="BAAI/bge-small-en-v1.5",
|
|
59
|
+
revision="5c38ec7c405ec4b44b94cc5a9bb96e735b38267a",
|
|
60
|
+
dimension=384,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class SymbolEmbedding:
|
|
66
|
+
symbol_id: str
|
|
67
|
+
profile_id: str
|
|
68
|
+
input_hash: str
|
|
69
|
+
source_hash: str
|
|
70
|
+
values: tuple[float, ...]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _digest(text: str) -> str:
|
|
74
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def validate_vectors(
|
|
78
|
+
vectors: Iterable[Iterable[float]], *, count: int, dimension: int
|
|
79
|
+
) -> list[list[float]]:
|
|
80
|
+
"""Reject failed, truncated or malformed inference before any publication."""
|
|
81
|
+
if type(count) is not int or not 0 <= count <= 512:
|
|
82
|
+
raise ValueError("embedding batch must contain at most 512 inputs")
|
|
83
|
+
if type(dimension) is not int or not 1 <= dimension <= 65536:
|
|
84
|
+
raise ValueError("invalid embedding dimension")
|
|
85
|
+
rows = [list(islice(row, dimension + 1)) for row in islice(vectors, count + 1)]
|
|
86
|
+
if len(rows) != count:
|
|
87
|
+
raise ValueError("embedding batch must return exactly one vector per input")
|
|
88
|
+
for row in rows:
|
|
89
|
+
if len(row) != dimension:
|
|
90
|
+
raise ValueError("embedding dimension mismatch")
|
|
91
|
+
if any(
|
|
92
|
+
isinstance(v, bool) or not isinstance(v, Real) or not math.isfinite(float(v))
|
|
93
|
+
for v in row
|
|
94
|
+
):
|
|
95
|
+
raise ValueError("embedding values must be finite numeric values")
|
|
96
|
+
if not any(float(v) != 0.0 for v in row):
|
|
97
|
+
raise ValueError("zero vector is not a semantic embedding")
|
|
98
|
+
return [[float(v) for v in row] for row in rows]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def embed_symbols(
|
|
102
|
+
symbols: Sequence[CodeSymbol],
|
|
103
|
+
encode: Callable[[list[str]], Iterable[Iterable[float]]],
|
|
104
|
+
profile: EmbeddingProfile,
|
|
105
|
+
*,
|
|
106
|
+
batch_size: int = 32,
|
|
107
|
+
) -> list[SymbolEmbedding]:
|
|
108
|
+
"""Prepare, batch and validate compiler symbols without a store dependency.
|
|
109
|
+
|
|
110
|
+
``encode`` is trusted configuration: it must implement the supplied profile.
|
|
111
|
+
Backend identity and real-model quality require separate deployment acceptance.
|
|
112
|
+
"""
|
|
113
|
+
if type(batch_size) is not int or not 1 <= batch_size <= 512:
|
|
114
|
+
raise ValueError("embedding batch size must be between 1 and 512")
|
|
115
|
+
ids = [s.id for s in symbols]
|
|
116
|
+
if len(set(ids)) != len(ids) or any(not sid for sid in ids):
|
|
117
|
+
raise ValueError("empty or duplicate canonical symbol identity")
|
|
118
|
+
eligible = [s for s in symbols if not s.metadata.get("embedding_excluded")]
|
|
119
|
+
result = []
|
|
120
|
+
for start in range(0, len(eligible), batch_size):
|
|
121
|
+
batch = eligible[start : start + batch_size]
|
|
122
|
+
texts = [embedding_text(s.simple_name, s.signature, s.documentation) for s in batch]
|
|
123
|
+
vectors = validate_vectors(encode(texts), count=len(texts), dimension=profile.dimension)
|
|
124
|
+
for symbol, text, vector in zip(batch, texts, vectors, strict=True):
|
|
125
|
+
result.append(
|
|
126
|
+
SymbolEmbedding(
|
|
127
|
+
symbol.id,
|
|
128
|
+
profile.identity,
|
|
129
|
+
_digest(text),
|
|
130
|
+
_digest(symbol.source_code),
|
|
131
|
+
tuple(vector),
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
return result
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class SentenceTransformerEncoder:
|
|
138
|
+
"""Optional local inference for an explicit immutable profile.
|
|
139
|
+
|
|
140
|
+
Uses sentence-transformers itself; no alternative model implementation or
|
|
141
|
+
remote-code execution. Own one encoder per configured worker/model lifetime.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
semantic = True
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self, profile: EmbeddingProfile = BGE_SMALL_PROFILE, *, device: str = "cpu"
|
|
148
|
+
) -> None:
|
|
149
|
+
import threading
|
|
150
|
+
|
|
151
|
+
self.profile = profile
|
|
152
|
+
self.device = device
|
|
153
|
+
self._model: Any = None
|
|
154
|
+
self._lock = threading.Lock()
|
|
155
|
+
|
|
156
|
+
def encode(self, texts: list[str]) -> list[list[float]]:
|
|
157
|
+
"""Encode one bounded document batch; propagate model/installation errors."""
|
|
158
|
+
if len(texts) > 512:
|
|
159
|
+
raise ValueError("embedding batch exceeds 512 inputs")
|
|
160
|
+
if not texts:
|
|
161
|
+
return []
|
|
162
|
+
with self._lock:
|
|
163
|
+
if self._model is None:
|
|
164
|
+
from sentence_transformers import SentenceTransformer
|
|
165
|
+
|
|
166
|
+
model = SentenceTransformer(
|
|
167
|
+
self.profile.model_id,
|
|
168
|
+
revision=self.profile.revision,
|
|
169
|
+
device=self.device,
|
|
170
|
+
trust_remote_code=False,
|
|
171
|
+
)
|
|
172
|
+
if model.get_sentence_embedding_dimension() != self.profile.dimension:
|
|
173
|
+
raise ValueError("model/profile embedding dimension mismatch")
|
|
174
|
+
model.max_seq_length = self.profile.max_tokens
|
|
175
|
+
self._model = model
|
|
176
|
+
values = self._model.encode(
|
|
177
|
+
texts,
|
|
178
|
+
batch_size=32,
|
|
179
|
+
normalize_embeddings=self.profile.normalize,
|
|
180
|
+
convert_to_numpy=True,
|
|
181
|
+
show_progress_bar=False,
|
|
182
|
+
)
|
|
183
|
+
return validate_vectors(values, count=len(texts), dimension=self.profile.dimension)
|
|
184
|
+
|
|
185
|
+
def encode_query(self, query: str) -> list[float]:
|
|
186
|
+
return self.encode([self.profile.query_prefix + query])[0]
|
|
187
|
+
|
|
188
|
+
async def embed_batch(self, texts: list[str]) -> list[list[float]]:
|
|
189
|
+
import asyncio
|
|
190
|
+
|
|
191
|
+
return await asyncio.to_thread(self.encode, texts)
|
|
192
|
+
|
|
193
|
+
async def embed_text(self, text: str, use_cache: bool = True) -> list[float]:
|
|
194
|
+
# Compatibility with Victor's injected embedding-service seam. Cache
|
|
195
|
+
# ownership remains outside this encoder; the flag never changes semantics.
|
|
196
|
+
return (await self.embed_batch([text]))[0]
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: victor-codegraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Repository-to-Code-Semantic-Graph compiler with stable identity, bounded chunks, incremental deltas, and ProximaRecord projection.
|
|
5
5
|
Author-email: Vijaykumar Singh <vijay@anvaiops.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,6 +16,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries
|
|
17
17
|
Requires-Python: >=3.12
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
|
+
Provides-Extra: embeddings
|
|
20
|
+
Requires-Dist: sentence-transformers<6,>=3; extra == "embeddings"
|
|
19
21
|
Provides-Extra: treesitter
|
|
20
22
|
Requires-Dist: tree-sitter>=0.26.0; extra == "treesitter"
|
|
21
23
|
Requires-Dist: tree-sitter-language-pack>=1.21.3; extra == "treesitter"
|
|
@@ -54,7 +56,7 @@ gaps:
|
|
|
54
56
|
## Install
|
|
55
57
|
|
|
56
58
|
Published on PyPI as [`victor-codegraph`](https://pypi.org/project/victor-codegraph/).
|
|
57
|
-
This source tree is version **0.
|
|
59
|
+
This source tree is version **0.10.0**; consumers should use a compatible `<1.0` pin.
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
62
|
# from PyPI
|
|
@@ -126,7 +128,7 @@ records = to_proxima_records(repository, repo_graph_id="myrepo")
|
|
|
126
128
|
|
|
127
129
|
## Status
|
|
128
130
|
|
|
129
|
-
`0.
|
|
131
|
+
`0.10.0` source version. Python (`ast`) is the full, offline frontend. JS/TS, Go, Rust,
|
|
130
132
|
Java, C, and C++ have conformance fixtures; other mapped tree-sitter languages report
|
|
131
133
|
symbols-tier or fallback status rather than implying full fidelity.
|
|
132
134
|
|
|
@@ -143,4 +145,30 @@ workspace-inherited dependencies, re-export chains, macros and unknown receiver
|
|
|
143
145
|
types retain unresolved references. Glob imports retain a module dependency but
|
|
144
146
|
do not guess call targets. This is a conservative static graph, not rustc name
|
|
145
147
|
resolution or evidence that a call executes. Publish this package before a Victor
|
|
146
|
-
release that requires its
|
|
148
|
+
release that requires its exact repository graph contract.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### Embedding conformance (0.10.0)
|
|
152
|
+
|
|
153
|
+
The accepted FEP-0040 supplies store-neutral
|
|
154
|
+
`EmbeddingProfile`, `SymbolEmbedding`, `embed_symbols` and an optional
|
|
155
|
+
`SentenceTransformerEncoder`. The base compiler stays dependency-free. The
|
|
156
|
+
`embeddings` extra loads sentence-transformers only when explicitly selected;
|
|
157
|
+
private source is not sent to a remote model by default.
|
|
158
|
+
|
|
159
|
+
The contract uses canonical compiler IDs and `symbol-summary-v1` (name,
|
|
160
|
+
signature and documentation), with an immutable model revision, dimensions,
|
|
161
|
+
token limit, normalization, query prefix and separate input/source hashes.
|
|
162
|
+
Inference errors propagate; hash placeholders, zero vectors and malformed batches
|
|
163
|
+
are not semantic evidence. File/container symbols marked `embedding_excluded`
|
|
164
|
+
remain graph evidence only.
|
|
165
|
+
|
|
166
|
+
These APIs are introduced in 0.10.0 and are unavailable in 0.9.0. Install the
|
|
167
|
+
exact published `victor-codegraph[treesitter,embeddings]==0.10.0` when selecting
|
|
168
|
+
local model execution; the base package has no mandatory model dependency.
|
|
169
|
+
Current evidence covers
|
|
170
|
+
synthetic document-vector persistence parity and local LanceDB collection/query
|
|
171
|
+
profile enforcement. Labeled relevance and hosted lifecycle remain separate
|
|
172
|
+
acceptance gates. Victor binds a new LanceDB table to the complete profile in its
|
|
173
|
+
Arrow schema and rejects incompatible query encoders and mixed/orphan row markers.
|
|
174
|
+
Legacy tables require a fresh destination; they are never silently relabeled.
|
|
@@ -4,6 +4,7 @@ tests/test_adapter.py
|
|
|
4
4
|
tests/test_adapter_validation_v2.py
|
|
5
5
|
tests/test_chunk_contract_v2.py
|
|
6
6
|
tests/test_diagnostics_v2.py
|
|
7
|
+
tests/test_embedding_contract.py
|
|
7
8
|
tests/test_identity_v2.py
|
|
8
9
|
tests/test_line_base_contract.py
|
|
9
10
|
tests/test_manifest.py
|
|
@@ -21,11 +22,13 @@ tests/test_treesitter_relations.py
|
|
|
21
22
|
victor_codegraph/__init__.py
|
|
22
23
|
victor_codegraph/adapter.py
|
|
23
24
|
victor_codegraph/config.py
|
|
25
|
+
victor_codegraph/embeddings.py
|
|
24
26
|
victor_codegraph/javascript_repository.py
|
|
25
27
|
victor_codegraph/languages.py
|
|
26
28
|
victor_codegraph/manifest.py
|
|
27
29
|
victor_codegraph/model.py
|
|
28
30
|
victor_codegraph/parser.py
|
|
31
|
+
victor_codegraph/py.typed
|
|
29
32
|
victor_codegraph/python_parser.py
|
|
30
33
|
victor_codegraph/repo.py
|
|
31
34
|
victor_codegraph/repository_index.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph/javascript_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{victor_codegraph-0.9.0 → victor_codegraph-0.10.0}/victor_codegraph.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|