codegraph-engine 3.0.0__2-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 (130) hide show
  1. codegraph/__init__.py +37 -0
  2. codegraph/agent.py +26 -0
  3. codegraph/agent_brain.py +2212 -0
  4. codegraph/agent_capabilities.py +4058 -0
  5. codegraph/agent_rules.py +531 -0
  6. codegraph/api_drift.py +455 -0
  7. codegraph/architecture.py +371 -0
  8. codegraph/async_queue/__init__.py +50 -0
  9. codegraph/async_queue/detectors/__init__.py +48 -0
  10. codegraph/async_queue/detectors/celery.py +160 -0
  11. codegraph/async_queue/detectors/kafka.py +168 -0
  12. codegraph/async_queue/detectors/rabbitmq.py +116 -0
  13. codegraph/async_queue/detectors/redis.py +130 -0
  14. codegraph/async_queue/detectors/sqs.py +135 -0
  15. codegraph/async_queue/models.py +161 -0
  16. codegraph/async_queue/otel_models.py +74 -0
  17. codegraph/async_queue/otel_parser.py +337 -0
  18. codegraph/async_queue/reconciler.py +427 -0
  19. codegraph/async_queue/scanner.py +291 -0
  20. codegraph/async_queue/traversal.py +447 -0
  21. codegraph/audit.py +106 -0
  22. codegraph/binding_resolver.py +616 -0
  23. codegraph/branch_comparison.py +197 -0
  24. codegraph/cache.py +95 -0
  25. codegraph/change_impact.py +547 -0
  26. codegraph/cli.py +2436 -0
  27. codegraph/cli_output.py +205 -0
  28. codegraph/config.py +45 -0
  29. codegraph/constraints.py +238 -0
  30. codegraph/context.py +2182 -0
  31. codegraph/context_freshness.py +351 -0
  32. codegraph/database/__init__.py +72 -0
  33. codegraph/database/extractor.py +3651 -0
  34. codegraph/database/interrogation.py +1494 -0
  35. codegraph/database/models.py +310 -0
  36. codegraph/database/schema_drift.py +862 -0
  37. codegraph/epistemic.py +200 -0
  38. codegraph/errors.py +282 -0
  39. codegraph/evidence/__init__.py +15 -0
  40. codegraph/evidence/citations.py +418 -0
  41. codegraph/evidence_contract.py +486 -0
  42. codegraph/frameworks.py +960 -0
  43. codegraph/frameworks_nestjs.py +502 -0
  44. codegraph/frameworks_nextjs.py +337 -0
  45. codegraph/frameworks_react.py +457 -0
  46. codegraph/freshness.py +335 -0
  47. codegraph/frontend_html.py +109 -0
  48. codegraph/git.py +362 -0
  49. codegraph/git_state.py +545 -0
  50. codegraph/graph/__init__.py +46 -0
  51. codegraph/graph/models.py +56 -0
  52. codegraph/graph/traversal.py +1993 -0
  53. codegraph/indexing/__init__.py +24 -0
  54. codegraph/indexing/classifier.py +564 -0
  55. codegraph/indexing/indexer.py +2578 -0
  56. codegraph/indexing/models.py +427 -0
  57. codegraph/indexing/parser.py +3404 -0
  58. codegraph/indexing/pool.py +136 -0
  59. codegraph/indexing/scanner.py +211 -0
  60. codegraph/indexing/telemetry.py +326 -0
  61. codegraph/indexing/templates.py +418 -0
  62. codegraph/indexing/test_framework.py +116 -0
  63. codegraph/installer.py +1702 -0
  64. codegraph/interrogation.py +2355 -0
  65. codegraph/llm/__init__.py +3 -0
  66. codegraph/llm/base.py +15 -0
  67. codegraph/llm/context.py +20 -0
  68. codegraph/mcp/__init__.py +3 -0
  69. codegraph/mcp/server.py +1476 -0
  70. codegraph/mcp_diagnostics.py +882 -0
  71. codegraph/memory/__init__.py +3 -0
  72. codegraph/memory/store.py +46 -0
  73. codegraph/models.py +310 -0
  74. codegraph/monorepo.py +925 -0
  75. codegraph/observability.py +262 -0
  76. codegraph/optimizer.py +1014 -0
  77. codegraph/planner.py +417 -0
  78. codegraph/process_lifecycle.py +1153 -0
  79. codegraph/py.typed +1 -0
  80. codegraph/query_expansion.py +199 -0
  81. codegraph/ranking.py +408 -0
  82. codegraph/refactor/__init__.py +36 -0
  83. codegraph/refactor/models.py +114 -0
  84. codegraph/refactor/renamer.py +844 -0
  85. codegraph/refactor/transactions.py +414 -0
  86. codegraph/resolver.py +3968 -0
  87. codegraph/resources/__init__.py +46 -0
  88. codegraph/resources/cache.py +117 -0
  89. codegraph/resources/coalescer.py +83 -0
  90. codegraph/resources/debouncer.py +98 -0
  91. codegraph/resources/governor.py +219 -0
  92. codegraph/resources/monitor.py +102 -0
  93. codegraph/resources/policy.py +123 -0
  94. codegraph/retrieval_policy.py +320 -0
  95. codegraph/roblox.py +2174 -0
  96. codegraph/route_composer.py +363 -0
  97. codegraph/route_topology.py +172 -0
  98. codegraph/runtime/__init__.py +51 -0
  99. codegraph/runtime/collector.py +155 -0
  100. codegraph/runtime/db_watcher.py +191 -0
  101. codegraph/runtime/ingestor.py +692 -0
  102. codegraph/runtime/models.py +130 -0
  103. codegraph/runtime/port_inspector.py +304 -0
  104. codegraph/runtime/reconciliation.py +492 -0
  105. codegraph/runtime/runner.py +353 -0
  106. codegraph/runtime/trace_linker.py +175 -0
  107. codegraph/search/__init__.py +25 -0
  108. codegraph/search/hybrid.py +846 -0
  109. codegraph/search/semantic.py +28 -0
  110. codegraph/security/__init__.py +51 -0
  111. codegraph/security/paths.py +189 -0
  112. codegraph/security/redaction.py +633 -0
  113. codegraph/semantic_conflicts.py +270 -0
  114. codegraph/semantic_decorators.py +372 -0
  115. codegraph/structural_diff.py +542 -0
  116. codegraph/styles.py +144 -0
  117. codegraph/symbol_history.py +277 -0
  118. codegraph/target_resolver.py +348 -0
  119. codegraph/task.py +637 -0
  120. codegraph/tool_selection_eval.py +1158 -0
  121. codegraph/tsconfig.py +236 -0
  122. codegraph/ui/__init__.py +6 -0
  123. codegraph/ui/server.py +755 -0
  124. codegraph/watcher.py +323 -0
  125. codegraph_engine-3.0.0.dist-info/METADATA +868 -0
  126. codegraph_engine-3.0.0.dist-info/RECORD +130 -0
  127. codegraph_engine-3.0.0.dist-info/WHEEL +6 -0
  128. codegraph_engine-3.0.0.dist-info/entry_points.txt +2 -0
  129. codegraph_engine-3.0.0.dist-info/licenses/LICENSE +21 -0
  130. 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]