codegraph-engine 3.0.0__1-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.
- codegraph/__init__.py +37 -0
- codegraph/agent.py +26 -0
- codegraph/agent_brain.py +2212 -0
- codegraph/agent_capabilities.py +4058 -0
- codegraph/agent_rules.py +531 -0
- codegraph/api_drift.py +455 -0
- codegraph/architecture.py +371 -0
- codegraph/async_queue/__init__.py +50 -0
- codegraph/async_queue/detectors/__init__.py +48 -0
- codegraph/async_queue/detectors/celery.py +160 -0
- codegraph/async_queue/detectors/kafka.py +168 -0
- codegraph/async_queue/detectors/rabbitmq.py +116 -0
- codegraph/async_queue/detectors/redis.py +130 -0
- codegraph/async_queue/detectors/sqs.py +135 -0
- codegraph/async_queue/models.py +161 -0
- codegraph/async_queue/otel_models.py +74 -0
- codegraph/async_queue/otel_parser.py +337 -0
- codegraph/async_queue/reconciler.py +427 -0
- codegraph/async_queue/scanner.py +291 -0
- codegraph/async_queue/traversal.py +447 -0
- codegraph/audit.py +106 -0
- codegraph/binding_resolver.py +616 -0
- codegraph/branch_comparison.py +197 -0
- codegraph/cache.py +95 -0
- codegraph/change_impact.py +547 -0
- codegraph/cli.py +2436 -0
- codegraph/cli_output.py +205 -0
- codegraph/config.py +45 -0
- codegraph/constraints.py +238 -0
- codegraph/context.py +2182 -0
- codegraph/context_freshness.py +351 -0
- codegraph/database/__init__.py +72 -0
- codegraph/database/extractor.py +3651 -0
- codegraph/database/interrogation.py +1494 -0
- codegraph/database/models.py +310 -0
- codegraph/database/schema_drift.py +862 -0
- codegraph/epistemic.py +200 -0
- codegraph/errors.py +282 -0
- codegraph/evidence/__init__.py +15 -0
- codegraph/evidence/citations.py +418 -0
- codegraph/evidence_contract.py +486 -0
- codegraph/frameworks.py +960 -0
- codegraph/frameworks_nestjs.py +502 -0
- codegraph/frameworks_nextjs.py +337 -0
- codegraph/frameworks_react.py +457 -0
- codegraph/freshness.py +335 -0
- codegraph/frontend_html.py +109 -0
- codegraph/git.py +362 -0
- codegraph/git_state.py +545 -0
- codegraph/graph/__init__.py +46 -0
- codegraph/graph/models.py +56 -0
- codegraph/graph/traversal.py +1993 -0
- codegraph/indexing/__init__.py +24 -0
- codegraph/indexing/classifier.py +564 -0
- codegraph/indexing/indexer.py +2578 -0
- codegraph/indexing/models.py +427 -0
- codegraph/indexing/parser.py +3404 -0
- codegraph/indexing/pool.py +136 -0
- codegraph/indexing/scanner.py +211 -0
- codegraph/indexing/telemetry.py +326 -0
- codegraph/indexing/templates.py +418 -0
- codegraph/indexing/test_framework.py +116 -0
- codegraph/installer.py +1702 -0
- codegraph/interrogation.py +2355 -0
- codegraph/llm/__init__.py +3 -0
- codegraph/llm/base.py +15 -0
- codegraph/llm/context.py +20 -0
- codegraph/mcp/__init__.py +3 -0
- codegraph/mcp/server.py +1476 -0
- codegraph/mcp_diagnostics.py +882 -0
- codegraph/memory/__init__.py +3 -0
- codegraph/memory/store.py +46 -0
- codegraph/models.py +310 -0
- codegraph/monorepo.py +925 -0
- codegraph/observability.py +262 -0
- codegraph/optimizer.py +1014 -0
- codegraph/planner.py +417 -0
- codegraph/process_lifecycle.py +1153 -0
- codegraph/py.typed +1 -0
- codegraph/query_expansion.py +199 -0
- codegraph/ranking.py +408 -0
- codegraph/refactor/__init__.py +36 -0
- codegraph/refactor/models.py +114 -0
- codegraph/refactor/renamer.py +844 -0
- codegraph/refactor/transactions.py +414 -0
- codegraph/resolver.py +3968 -0
- codegraph/resources/__init__.py +46 -0
- codegraph/resources/cache.py +117 -0
- codegraph/resources/coalescer.py +83 -0
- codegraph/resources/debouncer.py +98 -0
- codegraph/resources/governor.py +219 -0
- codegraph/resources/monitor.py +102 -0
- codegraph/resources/policy.py +123 -0
- codegraph/retrieval_policy.py +320 -0
- codegraph/roblox.py +2174 -0
- codegraph/route_composer.py +363 -0
- codegraph/route_topology.py +172 -0
- codegraph/runtime/__init__.py +51 -0
- codegraph/runtime/collector.py +155 -0
- codegraph/runtime/db_watcher.py +191 -0
- codegraph/runtime/ingestor.py +692 -0
- codegraph/runtime/models.py +130 -0
- codegraph/runtime/port_inspector.py +304 -0
- codegraph/runtime/reconciliation.py +492 -0
- codegraph/runtime/runner.py +353 -0
- codegraph/runtime/trace_linker.py +175 -0
- codegraph/search/__init__.py +25 -0
- codegraph/search/hybrid.py +846 -0
- codegraph/search/semantic.py +28 -0
- codegraph/security/__init__.py +51 -0
- codegraph/security/paths.py +189 -0
- codegraph/security/redaction.py +633 -0
- codegraph/semantic_conflicts.py +270 -0
- codegraph/semantic_decorators.py +372 -0
- codegraph/structural_diff.py +542 -0
- codegraph/styles.py +144 -0
- codegraph/symbol_history.py +277 -0
- codegraph/target_resolver.py +348 -0
- codegraph/task.py +637 -0
- codegraph/tool_selection_eval.py +1158 -0
- codegraph/tsconfig.py +236 -0
- codegraph/ui/__init__.py +6 -0
- codegraph/ui/server.py +755 -0
- codegraph/watcher.py +323 -0
- codegraph_engine-3.0.0.dist-info/METADATA +859 -0
- codegraph_engine-3.0.0.dist-info/RECORD +130 -0
- codegraph_engine-3.0.0.dist-info/WHEEL +6 -0
- codegraph_engine-3.0.0.dist-info/entry_points.txt +2 -0
- codegraph_engine-3.0.0.dist-info/licenses/LICENSE +21 -0
- codegraph_engine-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""Production Observability and Structured Metrics.
|
|
2
|
+
|
|
3
|
+
Invariants:
|
|
4
|
+
- Never log raw repository source code.
|
|
5
|
+
- Never log secrets or credentials.
|
|
6
|
+
- Zero network dependencies or cloud uploads.
|
|
7
|
+
- Structured metrics buffer available locally for audits, benchmarks, and doctor diagnostics.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import time
|
|
12
|
+
import uuid
|
|
13
|
+
from dataclasses import asdict, dataclass, field
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ExecutionTiming:
|
|
19
|
+
planning_ms: float = 0.0
|
|
20
|
+
search_ms: float = 0.0
|
|
21
|
+
graph_ms: float = 0.0
|
|
22
|
+
framework_ms: float = 0.0
|
|
23
|
+
tests_ms: float = 0.0
|
|
24
|
+
git_ms: float = 0.0
|
|
25
|
+
evidence_ms: float = 0.0
|
|
26
|
+
ranking_ms: float = 0.0
|
|
27
|
+
compilation_ms: float = 0.0
|
|
28
|
+
serialization_ms: float = 0.0
|
|
29
|
+
total_ms: float = 0.0
|
|
30
|
+
|
|
31
|
+
def as_dict(self) -> dict[str, float]:
|
|
32
|
+
return {
|
|
33
|
+
"planning_ms": round(self.planning_ms, 2),
|
|
34
|
+
"search_ms": round(self.search_ms, 2),
|
|
35
|
+
"graph_ms": round(self.graph_ms, 2),
|
|
36
|
+
"framework_ms": round(self.framework_ms, 2),
|
|
37
|
+
"tests_ms": round(self.tests_ms, 2),
|
|
38
|
+
"git_ms": round(self.git_ms, 2),
|
|
39
|
+
"evidence_ms": round(self.evidence_ms, 2),
|
|
40
|
+
"ranking_ms": round(self.ranking_ms, 2),
|
|
41
|
+
"compilation_ms": round(self.compilation_ms, 2),
|
|
42
|
+
"serialization_ms": round(self.serialization_ms, 2),
|
|
43
|
+
"total_ms": round(self.total_ms, 2),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class ExecutionMetadata:
|
|
49
|
+
latency_ms: float = 0.0
|
|
50
|
+
cache_hit: bool = False
|
|
51
|
+
index_generation: int = 0
|
|
52
|
+
candidate_count: int = 0
|
|
53
|
+
selected_count: int = 0
|
|
54
|
+
mode: str = "BALANCED" # FAST | BALANCED | DEEP
|
|
55
|
+
timing: ExecutionTiming | None = None
|
|
56
|
+
|
|
57
|
+
def as_dict(self) -> dict[str, object]:
|
|
58
|
+
res: dict[str, object] = {
|
|
59
|
+
"latency_ms": round(self.latency_ms, 2),
|
|
60
|
+
"cache_hit": self.cache_hit,
|
|
61
|
+
"index_generation": self.index_generation,
|
|
62
|
+
"candidate_count": self.candidate_count,
|
|
63
|
+
"selected_count": self.selected_count,
|
|
64
|
+
"mode": self.mode,
|
|
65
|
+
}
|
|
66
|
+
if self.timing:
|
|
67
|
+
res["timing"] = self.timing.as_dict()
|
|
68
|
+
return res
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class OperationMetric:
|
|
73
|
+
request_id: str
|
|
74
|
+
tool_or_op: str
|
|
75
|
+
latency_ms: float
|
|
76
|
+
index_generation: int = 0
|
|
77
|
+
cache_hit: bool = False
|
|
78
|
+
candidate_count: int = 0
|
|
79
|
+
selected_count: int = 0
|
|
80
|
+
estimated_tokens: int = 0
|
|
81
|
+
task_intent: str = "UNDERSTAND"
|
|
82
|
+
ambiguity_state: str = "CLEAR"
|
|
83
|
+
freshness: str = "FRESH"
|
|
84
|
+
error_class: str | None = None
|
|
85
|
+
timestamp: float = field(default_factory=time.time)
|
|
86
|
+
|
|
87
|
+
def as_dict(self) -> dict[str, object]:
|
|
88
|
+
return asdict(self)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def compute_latency_percentiles(latencies: list[float]) -> dict[str, float]:
|
|
92
|
+
"""Compute deterministic p50, p95, p99 latency statistics."""
|
|
93
|
+
if not latencies:
|
|
94
|
+
return {
|
|
95
|
+
"count": 0,
|
|
96
|
+
"min_ms": 0.0,
|
|
97
|
+
"avg_ms": 0.0,
|
|
98
|
+
"p50_ms": 0.0,
|
|
99
|
+
"p95_ms": 0.0,
|
|
100
|
+
"p99_ms": 0.0,
|
|
101
|
+
"max_ms": 0.0,
|
|
102
|
+
}
|
|
103
|
+
s = sorted(latencies)
|
|
104
|
+
n = len(s)
|
|
105
|
+
|
|
106
|
+
def _pct(p: float) -> float:
|
|
107
|
+
idx = min(n - 1, max(0, int(round((n - 1) * p))))
|
|
108
|
+
return round(s[idx], 2)
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
"count": n,
|
|
112
|
+
"min_ms": round(s[0], 2),
|
|
113
|
+
"avg_ms": round(sum(s) / n, 2),
|
|
114
|
+
"p50_ms": _pct(0.50),
|
|
115
|
+
"p95_ms": _pct(0.95),
|
|
116
|
+
"p99_ms": _pct(0.99),
|
|
117
|
+
"max_ms": round(s[-1], 2),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass
|
|
122
|
+
class ToolUsageRecord:
|
|
123
|
+
"""Local diagnostic record for agent tool-selection and fallback behavior (Section 14)."""
|
|
124
|
+
|
|
125
|
+
request_classification: str
|
|
126
|
+
selected_codegraph_tool: str | None
|
|
127
|
+
codegraph_used: bool
|
|
128
|
+
codegraph_call_count: int = 0
|
|
129
|
+
direct_file_reads_after: int = 0
|
|
130
|
+
fallback_occurred: bool = False
|
|
131
|
+
fallback_reason: str | None = None
|
|
132
|
+
|
|
133
|
+
def as_dict(self) -> dict[str, object]:
|
|
134
|
+
return {
|
|
135
|
+
"request_classification": self.request_classification,
|
|
136
|
+
"selected_codegraph_tool": self.selected_codegraph_tool,
|
|
137
|
+
"codegraph_used": self.codegraph_used,
|
|
138
|
+
"codegraph_call_count": self.codegraph_call_count,
|
|
139
|
+
"direct_file_reads_after": self.direct_file_reads_after,
|
|
140
|
+
"fallback_occurred": self.fallback_occurred,
|
|
141
|
+
"fallback_reason": self.fallback_reason,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class MetricsRegistry:
|
|
146
|
+
"""Thread-safe, bounded in-memory metrics tracker."""
|
|
147
|
+
|
|
148
|
+
def __init__(self, max_records: int = 500) -> None:
|
|
149
|
+
self.max_records = max_records
|
|
150
|
+
self._records: list[OperationMetric] = []
|
|
151
|
+
self._tool_usage_records: list[ToolUsageRecord] = []
|
|
152
|
+
|
|
153
|
+
def record(self, metric: OperationMetric) -> None:
|
|
154
|
+
self._records.append(metric)
|
|
155
|
+
if len(self._records) > self.max_records:
|
|
156
|
+
self._records = self._records[-self.max_records :]
|
|
157
|
+
|
|
158
|
+
def record_tool_usage(self, usage: ToolUsageRecord) -> None:
|
|
159
|
+
self._tool_usage_records.append(usage)
|
|
160
|
+
if len(self._tool_usage_records) > self.max_records:
|
|
161
|
+
self._tool_usage_records = self._tool_usage_records[-self.max_records :]
|
|
162
|
+
|
|
163
|
+
def get_tool_usage_summary(self) -> dict[str, object]:
|
|
164
|
+
total = len(self._tool_usage_records)
|
|
165
|
+
if not total:
|
|
166
|
+
return {
|
|
167
|
+
"total_requests": 0,
|
|
168
|
+
"codegraph_used_count": 0,
|
|
169
|
+
"codegraph_invocation_rate": 0.0,
|
|
170
|
+
"total_codegraph_calls": 0,
|
|
171
|
+
"avg_codegraph_calls": 0.0,
|
|
172
|
+
"total_direct_file_reads_after": 0,
|
|
173
|
+
"fallback_count": 0,
|
|
174
|
+
"by_classification": {},
|
|
175
|
+
"records": [],
|
|
176
|
+
}
|
|
177
|
+
used_count = sum(1 for r in self._tool_usage_records if r.codegraph_used)
|
|
178
|
+
total_cg_calls = sum(r.codegraph_call_count for r in self._tool_usage_records)
|
|
179
|
+
total_reads = sum(r.direct_file_reads_after for r in self._tool_usage_records)
|
|
180
|
+
fallbacks = sum(1 for r in self._tool_usage_records if r.fallback_occurred)
|
|
181
|
+
by_cls: dict[str, int] = {}
|
|
182
|
+
for r in self._tool_usage_records:
|
|
183
|
+
by_cls[r.request_classification] = by_cls.get(r.request_classification, 0) + 1
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
"total_requests": total,
|
|
187
|
+
"codegraph_used_count": used_count,
|
|
188
|
+
"codegraph_invocation_rate": round(used_count / total, 4),
|
|
189
|
+
"total_codegraph_calls": total_cg_calls,
|
|
190
|
+
"avg_codegraph_calls": round(total_cg_calls / total, 2),
|
|
191
|
+
"total_direct_file_reads_after": total_reads,
|
|
192
|
+
"fallback_count": fallbacks,
|
|
193
|
+
"by_classification": {k: by_cls[k] for k in sorted(by_cls.keys())},
|
|
194
|
+
"records": [r.as_dict() for r in self._tool_usage_records[-25:]],
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
def get_summary(self) -> dict[str, object]:
|
|
198
|
+
total = len(self._records)
|
|
199
|
+
if not total:
|
|
200
|
+
return {
|
|
201
|
+
"total_operations": 0,
|
|
202
|
+
"avg_latency_ms": 0.0,
|
|
203
|
+
"first_request_latency_ms": 0.0,
|
|
204
|
+
"warm_request_latency_ms": 0.0,
|
|
205
|
+
"p50_ms": 0.0,
|
|
206
|
+
"p95_ms": 0.0,
|
|
207
|
+
"p99_ms": 0.0,
|
|
208
|
+
"cache_hit_rate": 0.0,
|
|
209
|
+
"errors": 0,
|
|
210
|
+
"tool_usage": self.get_tool_usage_summary(),
|
|
211
|
+
}
|
|
212
|
+
all_lats = [r.latency_ms for r in self._records]
|
|
213
|
+
pcts = compute_latency_percentiles(all_lats)
|
|
214
|
+
first_lat = round(self._records[0].latency_ms, 2)
|
|
215
|
+
warm_lats = [r.latency_ms for r in self._records[1:]] if total > 1 else all_lats
|
|
216
|
+
warm_avg = round(sum(warm_lats) / len(warm_lats), 2) if warm_lats else first_lat
|
|
217
|
+
cache_hits = sum(1 for r in self._records if r.cache_hit)
|
|
218
|
+
errors = sum(1 for r in self._records if r.error_class is not None)
|
|
219
|
+
return {
|
|
220
|
+
"total_operations": total,
|
|
221
|
+
"avg_latency_ms": pcts["avg_ms"],
|
|
222
|
+
"first_request_latency_ms": first_lat,
|
|
223
|
+
"warm_request_latency_ms": warm_avg,
|
|
224
|
+
"p50_ms": pcts["p50_ms"],
|
|
225
|
+
"p95_ms": pcts["p95_ms"],
|
|
226
|
+
"p99_ms": pcts["p99_ms"],
|
|
227
|
+
"cache_hit_rate": round(cache_hits / total, 3),
|
|
228
|
+
"errors": errors,
|
|
229
|
+
"recent_operations": [r.as_dict() for r in self._records[-10:]],
|
|
230
|
+
"tool_usage": self.get_tool_usage_summary(),
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
def clear(self) -> None:
|
|
234
|
+
self._records.clear()
|
|
235
|
+
self._tool_usage_records.clear()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# Global in-memory metrics registry for current process
|
|
239
|
+
_GLOBAL_METRICS = MetricsRegistry()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def get_global_metrics() -> MetricsRegistry:
|
|
243
|
+
return _GLOBAL_METRICS
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class Timer:
|
|
247
|
+
"""Context manager for measuring latency with high precision."""
|
|
248
|
+
|
|
249
|
+
def __init__(self) -> None:
|
|
250
|
+
self.start_time = 0.0
|
|
251
|
+
self.elapsed_ms = 0.0
|
|
252
|
+
|
|
253
|
+
def __enter__(self) -> Timer:
|
|
254
|
+
self.start_time = time.perf_counter()
|
|
255
|
+
return self
|
|
256
|
+
|
|
257
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
258
|
+
self.elapsed_ms = (time.perf_counter() - self.start_time) * 1000.0
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def create_request_id() -> str:
|
|
262
|
+
return uuid.uuid4().hex[:12]
|