seam-code 0.3.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.
Files changed (109) hide show
  1. seam/__init__.py +3 -0
  2. seam/_data/schema.sql +225 -0
  3. seam/_web/assets/index-BL_tqprR.js +216 -0
  4. seam/_web/assets/index-GTKUhVyD.css +1 -0
  5. seam/_web/index.html +13 -0
  6. seam/analysis/__init__.py +14 -0
  7. seam/analysis/affected.py +254 -0
  8. seam/analysis/builtins.py +966 -0
  9. seam/analysis/byte_budget.py +217 -0
  10. seam/analysis/changes.py +709 -0
  11. seam/analysis/cluster_naming.py +260 -0
  12. seam/analysis/clustering.py +216 -0
  13. seam/analysis/confidence.py +699 -0
  14. seam/analysis/embeddings.py +195 -0
  15. seam/analysis/flows.py +708 -0
  16. seam/analysis/impact.py +444 -0
  17. seam/analysis/imports.py +994 -0
  18. seam/analysis/imports_ext.py +780 -0
  19. seam/analysis/imports_resolve.py +176 -0
  20. seam/analysis/processes.py +453 -0
  21. seam/analysis/relevance.py +155 -0
  22. seam/analysis/rwr.py +129 -0
  23. seam/analysis/staleness.py +328 -0
  24. seam/analysis/steer.py +282 -0
  25. seam/analysis/synthesis.py +253 -0
  26. seam/analysis/synthesis_channels.py +433 -0
  27. seam/analysis/testpaths.py +103 -0
  28. seam/analysis/traversal.py +470 -0
  29. seam/cli/__init__.py +0 -0
  30. seam/cli/install.py +232 -0
  31. seam/cli/main.py +2602 -0
  32. seam/cli/output.py +137 -0
  33. seam/cli/read.py +244 -0
  34. seam/cli/serve.py +145 -0
  35. seam/config.py +551 -0
  36. seam/indexer/__init__.py +0 -0
  37. seam/indexer/cluster_index.py +425 -0
  38. seam/indexer/db.py +496 -0
  39. seam/indexer/embedding_index.py +183 -0
  40. seam/indexer/field_access.py +536 -0
  41. seam/indexer/field_access_c_cpp.py +643 -0
  42. seam/indexer/field_access_ext.py +708 -0
  43. seam/indexer/field_access_ext2.py +408 -0
  44. seam/indexer/field_access_go_rust.py +737 -0
  45. seam/indexer/field_access_php_swift.py +888 -0
  46. seam/indexer/field_access_ts.py +626 -0
  47. seam/indexer/graph.py +321 -0
  48. seam/indexer/graph_c.py +562 -0
  49. seam/indexer/graph_c_cpp.py +39 -0
  50. seam/indexer/graph_common.py +644 -0
  51. seam/indexer/graph_cpp.py +615 -0
  52. seam/indexer/graph_csharp.py +651 -0
  53. seam/indexer/graph_go.py +723 -0
  54. seam/indexer/graph_go_rust.py +39 -0
  55. seam/indexer/graph_java.py +689 -0
  56. seam/indexer/graph_java_csharp.py +38 -0
  57. seam/indexer/graph_php.py +914 -0
  58. seam/indexer/graph_python.py +628 -0
  59. seam/indexer/graph_ruby.py +748 -0
  60. seam/indexer/graph_rust.py +653 -0
  61. seam/indexer/graph_scope_infer.py +902 -0
  62. seam/indexer/graph_scope_infer_ext.py +723 -0
  63. seam/indexer/graph_scope_infer_ext2.py +992 -0
  64. seam/indexer/graph_swift.py +1014 -0
  65. seam/indexer/graph_swift_infer.py +515 -0
  66. seam/indexer/graph_typescript.py +663 -0
  67. seam/indexer/migrations.py +816 -0
  68. seam/indexer/parser.py +204 -0
  69. seam/indexer/pipeline.py +197 -0
  70. seam/indexer/signatures.py +634 -0
  71. seam/indexer/signatures_ext.py +780 -0
  72. seam/indexer/sync.py +287 -0
  73. seam/indexer/synthesis_index.py +291 -0
  74. seam/indexer/tokenize.py +79 -0
  75. seam/installer/__init__.py +67 -0
  76. seam/installer/claude.py +97 -0
  77. seam/installer/codex.py +94 -0
  78. seam/installer/core.py +127 -0
  79. seam/installer/cursor.py +61 -0
  80. seam/installer/guide.py +110 -0
  81. seam/installer/jsonfile.py +85 -0
  82. seam/installer/markdownfile.py +146 -0
  83. seam/installer/tomlfile.py +72 -0
  84. seam/query/__init__.py +0 -0
  85. seam/query/clusters.py +206 -0
  86. seam/query/comments.py +217 -0
  87. seam/query/context.py +293 -0
  88. seam/query/engine.py +940 -0
  89. seam/query/fts.py +328 -0
  90. seam/query/names.py +470 -0
  91. seam/query/pack.py +433 -0
  92. seam/query/semantic.py +339 -0
  93. seam/query/structure.py +727 -0
  94. seam/server/__init__.py +0 -0
  95. seam/server/graph_api.py +437 -0
  96. seam/server/handler_common.py +323 -0
  97. seam/server/impact_handler.py +615 -0
  98. seam/server/mcp.py +556 -0
  99. seam/server/tools.py +697 -0
  100. seam/server/trace_handler.py +184 -0
  101. seam/server/web.py +922 -0
  102. seam/watcher/__init__.py +0 -0
  103. seam/watcher/__main__.py +56 -0
  104. seam/watcher/daemon.py +237 -0
  105. seam_code-0.3.0.dist-info/METADATA +318 -0
  106. seam_code-0.3.0.dist-info/RECORD +109 -0
  107. seam_code-0.3.0.dist-info/WHEEL +4 -0
  108. seam_code-0.3.0.dist-info/entry_points.txt +2 -0
  109. seam_code-0.3.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,195 @@
1
+ """Local embedding wrapper for semantic search (T3).
2
+
3
+ Leaf module — imports ONLY stdlib + fastembed (lazy).
4
+ numpy is NEVER imported here; we only call .tobytes() on numpy arrays returned by fastembed.
5
+ NO server/cli/query imports.
6
+
7
+ Design decisions:
8
+ - fastembed is an OPTIONAL extra ([semantic]). This module degrades gracefully
9
+ when it is absent: is_available() → False, embed_texts() → [], embed_query() → b''.
10
+ - WHY no numpy import here: numpy is a fastembed transitive dep. Importing it at module
11
+ scope would make this module fail to import in environments where only the base package is
12
+ installed (no [semantic] extra). We call .tobytes() on the array objects fastembed returns
13
+ — that is safe because if fastembed is importable, numpy is also present.
14
+ - TextEmbedding is lazily loaded and cached per model name via _MODEL_CACHE.
15
+ WHY cached: FastEmbed model loading takes ~200ms; reloading on every call would make the
16
+ read path unacceptably slow. FastEmbed models are read-safe; the dict is not locked
17
+ (worst case: two concurrent callers each create an instance; one is discarded — harmless).
18
+ - Never raises: any error in the real path is logged as a warning and the function
19
+ returns the safe default ([] or b'').
20
+
21
+ Public API:
22
+ is_available() -> bool
23
+ symbol_text(name, signature, docstring) -> str
24
+ embed_texts(texts, model) -> list[bytes]
25
+ embed_query(text, model) -> bytes
26
+ _get_model(model) <- internal, exposed for monkeypatching in tests
27
+ """
28
+
29
+ import logging
30
+ from typing import Any
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+ # ── Availability cache ────────────────────────────────────────────────────────
35
+ # None = not yet checked; True/False = cached result.
36
+ # Reset to None in tests that need to simulate absence.
37
+ _fastembed_available: bool | None = None
38
+
39
+
40
+ def is_available() -> bool:
41
+ """Return True if the fastembed package is importable.
42
+
43
+ The result is cached after the first call — subsequent calls are O(1).
44
+ Never raises: any import error returns False gracefully.
45
+
46
+ WHY cached: this function is called once per query in the read path;
47
+ caching avoids repeated sys.modules lookups in hot code.
48
+ """
49
+ global _fastembed_available # noqa: PLW0603
50
+ if _fastembed_available is None:
51
+ try:
52
+ import fastembed # noqa: F401
53
+
54
+ _fastembed_available = True
55
+ except Exception: # noqa: BLE001
56
+ _fastembed_available = False
57
+ return bool(_fastembed_available)
58
+
59
+
60
+ # ── Model cache ───────────────────────────────────────────────────────────────
61
+ # Maps model_name → TextEmbedding instance. Single-process; not thread-locked
62
+ # (fastembed models are read-safe; worst case we create two and discard one).
63
+ _MODEL_CACHE: dict[str, Any] = {}
64
+
65
+
66
+ def _get_model(model: str) -> Any:
67
+ """Lazy-load and cache a fastembed TextEmbedding instance.
68
+
69
+ WHY: FastEmbed model loading (~200ms) must not happen at import time.
70
+ The cache avoids reloading across multiple calls within one process.
71
+
72
+ Raises: any exception from fastembed (caller is responsible for catching).
73
+ """
74
+ if model not in _MODEL_CACHE:
75
+ from fastembed import TextEmbedding # type: ignore[import-untyped]
76
+
77
+ _MODEL_CACHE[model] = TextEmbedding(model_name=model)
78
+ return _MODEL_CACHE[model]
79
+
80
+
81
+ # ── Public API ────────────────────────────────────────────────────────────────
82
+
83
+
84
+ def symbol_text(
85
+ name: str,
86
+ signature: str | None,
87
+ docstring: str | None,
88
+ ) -> str:
89
+ """Build the canonical text string to embed for a symbol.
90
+
91
+ Combines name + signature + docstring into a single string. Fields that are
92
+ None or empty are omitted (no trailing whitespace, no 'None' literals).
93
+
94
+ WHY this order: the symbol name is the highest-signal token; signature provides
95
+ parameter types and return type (shape of the function); docstring provides
96
+ intent and usage context. Together they cover both syntactic and semantic search.
97
+
98
+ Args:
99
+ name: Symbol name (always included).
100
+ signature: Function/class signature, or None.
101
+ docstring: Docstring / documentation text, or None.
102
+
103
+ Returns:
104
+ A single str suitable for embedding. Never raises.
105
+ """
106
+ parts: list[str] = [name]
107
+ if signature:
108
+ parts.append(signature)
109
+ if docstring:
110
+ parts.append(docstring)
111
+ return "\n".join(parts)
112
+
113
+
114
+ def embed_texts(texts: list[str], model: str) -> list[bytes]:
115
+ """Embed a list of texts using the local fastembed model.
116
+
117
+ Returns one float32 blob (numpy.array.tobytes()) per input text.
118
+ Returns [] when:
119
+ - fastembed is not available (graceful degradation)
120
+ - the texts list is empty (no-op fast path)
121
+ - any exception occurs (logs a warning, returns [])
122
+
123
+ WHY: The caller (embedding_index.py) batches its own inputs; this function
124
+ processes them all in one fastembed call. Returning [] on error mirrors the
125
+ index_clusters pattern: a single failure logs a warning and the index is left
126
+ unpopulated rather than crashing.
127
+
128
+ Args:
129
+ texts: List of strings to embed. May be empty.
130
+ model: FastEmbed model name (e.g. "BAAI/bge-small-en-v1.5").
131
+
132
+ Returns:
133
+ list[bytes] of length len(texts), each a float32 numpy array serialised
134
+ with .tobytes(). Returns [] on any failure.
135
+ """
136
+ if not is_available():
137
+ return []
138
+ if not texts:
139
+ return []
140
+ try:
141
+ emb_model = _get_model(model)
142
+ # fastembed TextEmbedding.embed() returns a Generator of numpy float32 arrays.
143
+ vectors = list(emb_model.embed(texts))
144
+ return [v.tobytes() for v in vectors]
145
+ except Exception as exc: # noqa: BLE001
146
+ logger.warning(
147
+ "embeddings.embed_texts: failed to embed %d texts with model %r (%s: %s); "
148
+ "returning [] — run 'seam init --semantic' again or check fastembed install.",
149
+ len(texts),
150
+ model,
151
+ type(exc).__name__,
152
+ exc,
153
+ )
154
+ return []
155
+
156
+
157
+ def embed_query(text: str, model: str) -> bytes:
158
+ """Embed a single query string using the local fastembed model.
159
+
160
+ Returns float32 bytes (numpy.array.tobytes()) for the query vector.
161
+ Returns b'' when:
162
+ - fastembed is not available (graceful degradation)
163
+ - any exception occurs (logs a warning, returns b'')
164
+
165
+ WHY query_embed vs embed: FastEmbed offers a separate query_embed() path that
166
+ prepends the BGE query prefix ("Represent this sentence for searching relevant
167
+ passages:"). Using it ensures the query vector lives in the same space as the
168
+ passage embeddings built with embed().
169
+
170
+ Args:
171
+ text: The search query string.
172
+ model: FastEmbed model name.
173
+
174
+ Returns:
175
+ bytes (float32 numpy array serialised with .tobytes()), or b'' on failure.
176
+ """
177
+ if not is_available():
178
+ return b""
179
+ try:
180
+ emb_model = _get_model(model)
181
+ # query_embed returns a Generator; we consume the first (and only) element.
182
+ vectors = list(emb_model.query_embed([text]))
183
+ if not vectors:
184
+ return b""
185
+ return vectors[0].tobytes()
186
+ except Exception as exc: # noqa: BLE001
187
+ logger.warning(
188
+ "embeddings.embed_query: failed to embed query %r with model %r (%s: %s); "
189
+ "returning b'' — semantic search will return no results.",
190
+ text,
191
+ model,
192
+ type(exc).__name__,
193
+ exc,
194
+ )
195
+ return b""