codecortex-context-engine 0.1.0a1__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 (90) hide show
  1. codecortex/__init__.py +3 -0
  2. codecortex/architecture/__init__.py +23 -0
  3. codecortex/architecture/drift.py +182 -0
  4. codecortex/architecture/inference.py +160 -0
  5. codecortex/backends/__init__.py +35 -0
  6. codecortex/backends/base.py +38 -0
  7. codecortex/backends/context.py +118 -0
  8. codecortex/backends/contracts.py +65 -0
  9. codecortex/backends/factory.py +60 -0
  10. codecortex/backends/graph.py +107 -0
  11. codecortex/backends/manager.py +303 -0
  12. codecortex/backends/mcp_client.py +196 -0
  13. codecortex/backends/pool.py +128 -0
  14. codecortex/backends/spec.py +83 -0
  15. codecortex/backends/symbols.py +189 -0
  16. codecortex/benchmark.py +211 -0
  17. codecortex/cli.py +409 -0
  18. codecortex/config.py +27 -0
  19. codecortex/context/__init__.py +13 -0
  20. codecortex/context/budget.py +62 -0
  21. codecortex/context/integrated.py +60 -0
  22. codecortex/context/pipeline.py +223 -0
  23. codecortex/core/__init__.py +1 -0
  24. codecortex/core/contracts.py +45 -0
  25. codecortex/core/errors.py +17 -0
  26. codecortex/core/models.py +69 -0
  27. codecortex/dashboard.py +259 -0
  28. codecortex/editing.py +41 -0
  29. codecortex/engines/__init__.py +5 -0
  30. codecortex/engines/builtin/__init__.py +5 -0
  31. codecortex/engines/builtin/factory.py +26 -0
  32. codecortex/engines/builtin/memory.py +35 -0
  33. codecortex/engines/builtin/repository.py +73 -0
  34. codecortex/engines/builtin/symbols.py +89 -0
  35. codecortex/engines/builtin/validation.py +54 -0
  36. codecortex/engines/registry.py +26 -0
  37. codecortex/entrypoint.py +266 -0
  38. codecortex/evaluation/__init__.py +55 -0
  39. codecortex/evaluation/external.py +265 -0
  40. codecortex/evaluation/production.py +670 -0
  41. codecortex/evaluation/regression.py +188 -0
  42. codecortex/gateway.py +38 -0
  43. codecortex/git_intelligence.py +252 -0
  44. codecortex/indexing/__init__.py +6 -0
  45. codecortex/indexing/graph.py +78 -0
  46. codecortex/indexing/impact.py +127 -0
  47. codecortex/indexing/incremental.py +163 -0
  48. codecortex/indexing/incremental_graph.py +189 -0
  49. codecortex/indexing/indexer.py +172 -0
  50. codecortex/indexing/relationships.py +179 -0
  51. codecortex/indexing/resolution.py +88 -0
  52. codecortex/integrations/__init__.py +5 -0
  53. codecortex/integrations/agents.py +235 -0
  54. codecortex/interfaces/__init__.py +1 -0
  55. codecortex/interfaces/mcp_bridge.py +66 -0
  56. codecortex/languages/__init__.py +5 -0
  57. codecortex/languages/native.py +166 -0
  58. codecortex/languages/registry.py +232 -0
  59. codecortex/mcp/__init__.py +5 -0
  60. codecortex/mcp/extended.py +114 -0
  61. codecortex/mcp/server.py +473 -0
  62. codecortex/memory/__init__.py +11 -0
  63. codecortex/memory/json_store.py +52 -0
  64. codecortex/memory/knowledge.py +193 -0
  65. codecortex/memory/team_store.py +193 -0
  66. codecortex/orchestrator.py +153 -0
  67. codecortex/pr_intelligence.py +214 -0
  68. codecortex/retrieval/__init__.py +16 -0
  69. codecortex/retrieval/hybrid.py +67 -0
  70. codecortex/retrieval/index.py +135 -0
  71. codecortex/retrieval/providers.py +67 -0
  72. codecortex/retrieval/repository.py +94 -0
  73. codecortex/router/__init__.py +5 -0
  74. codecortex/router/router.py +79 -0
  75. codecortex/runtime.py +69 -0
  76. codecortex/setup.py +100 -0
  77. codecortex/symbols/__init__.py +5 -0
  78. codecortex/symbols/providers.py +192 -0
  79. codecortex/telemetry/__init__.py +5 -0
  80. codecortex/telemetry/collector.py +43 -0
  81. codecortex/tracing/__init__.py +9 -0
  82. codecortex/tracing/task_trace.py +235 -0
  83. codecortex/workspace/__init__.py +9 -0
  84. codecortex/workspace/federation.py +173 -0
  85. codecortex_context_engine-0.1.0a1.dist-info/METADATA +381 -0
  86. codecortex_context_engine-0.1.0a1.dist-info/RECORD +90 -0
  87. codecortex_context_engine-0.1.0a1.dist-info/WHEEL +4 -0
  88. codecortex_context_engine-0.1.0a1.dist-info/entry_points.txt +3 -0
  89. codecortex_context_engine-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  90. codecortex_context_engine-0.1.0a1.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,265 @@
1
+ """Reproducible external evaluation suites for coding-agent workflows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ import time
9
+ import uuid
10
+ from dataclasses import asdict, dataclass, field
11
+ from datetime import UTC, datetime
12
+ from pathlib import Path
13
+ from typing import Any, Protocol
14
+
15
+
16
+ @dataclass(frozen=True, slots=True)
17
+ class EvaluationExpectation:
18
+ required_strings: tuple[str, ...] = ()
19
+ forbidden_strings: tuple[str, ...] = ()
20
+ required_paths: tuple[str, ...] = ()
21
+ max_tokens: int | None = None
22
+ max_tool_calls: int | None = None
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class EvaluationCase:
27
+ id: str
28
+ prompt: str
29
+ expectation: EvaluationExpectation = EvaluationExpectation()
30
+ metadata: dict[str, str] = field(default_factory=dict)
31
+
32
+
33
+ @dataclass(frozen=True, slots=True)
34
+ class EvaluationOutput:
35
+ answer: str
36
+ files_touched: tuple[str, ...] = ()
37
+ tokens: int = 0
38
+ tool_calls: int = 0
39
+ metadata: dict[str, Any] = field(default_factory=dict)
40
+
41
+
42
+ @dataclass(frozen=True, slots=True)
43
+ class Grade:
44
+ passed: bool
45
+ score: float
46
+ checks: tuple[str, ...]
47
+ failures: tuple[str, ...]
48
+
49
+
50
+ @dataclass(frozen=True, slots=True)
51
+ class EvaluationResult:
52
+ case_id: str
53
+ target: str
54
+ duration_ms: float
55
+ output: EvaluationOutput
56
+ grade: Grade
57
+ error: str | None = None
58
+
59
+
60
+ @dataclass(frozen=True, slots=True)
61
+ class EvaluationReport:
62
+ run_id: str
63
+ suite_name: str
64
+ suite_version: int
65
+ target: str
66
+ created_at: str
67
+ results: tuple[EvaluationResult, ...]
68
+
69
+ def summary(self) -> dict[str, float]:
70
+ count = max(1, len(self.results))
71
+ successful = [item for item in self.results if item.error is None]
72
+ return {
73
+ "cases": float(len(self.results)),
74
+ "success_rate": sum(item.grade.passed for item in self.results) / count,
75
+ "avg_score": sum(item.grade.score for item in self.results) / count,
76
+ "execution_success_rate": len(successful) / count,
77
+ "avg_duration_ms": sum(item.duration_ms for item in self.results) / count,
78
+ "avg_tokens": sum(item.output.tokens for item in self.results) / count,
79
+ "avg_tool_calls": sum(item.output.tool_calls for item in self.results) / count,
80
+ }
81
+
82
+ def save(self, path: Path) -> None:
83
+ path.parent.mkdir(parents=True, exist_ok=True)
84
+ payload = {**asdict(self), "summary": self.summary()}
85
+ temp = path.with_suffix(path.suffix + ".tmp")
86
+ temp.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
87
+ temp.replace(path)
88
+
89
+
90
+ class EvaluationTarget(Protocol):
91
+ name: str
92
+
93
+ async def run(self, case: EvaluationCase) -> EvaluationOutput: ...
94
+
95
+
96
+ class DeterministicGrader:
97
+ def grade(self, case: EvaluationCase, output: EvaluationOutput) -> Grade:
98
+ checks: list[str] = []
99
+ failures: list[str] = []
100
+ expectation = case.expectation
101
+ lowered = output.answer.lower()
102
+ for value in expectation.required_strings:
103
+ label = f"required-string:{value}"
104
+ checks.append(label)
105
+ if value.lower() not in lowered:
106
+ failures.append(label)
107
+ for value in expectation.forbidden_strings:
108
+ label = f"forbidden-string:{value}"
109
+ checks.append(label)
110
+ if value.lower() in lowered:
111
+ failures.append(label)
112
+ paths = {path.replace("\\", "/") for path in output.files_touched}
113
+ for path in expectation.required_paths:
114
+ label = f"required-path:{path}"
115
+ checks.append(label)
116
+ normalized = path.replace("\\", "/")
117
+ if normalized not in paths:
118
+ failures.append(label)
119
+ if expectation.max_tokens is not None:
120
+ label = f"max-tokens:{expectation.max_tokens}"
121
+ checks.append(label)
122
+ if output.tokens > expectation.max_tokens:
123
+ failures.append(label)
124
+ if expectation.max_tool_calls is not None:
125
+ label = f"max-tool-calls:{expectation.max_tool_calls}"
126
+ checks.append(label)
127
+ if output.tool_calls > expectation.max_tool_calls:
128
+ failures.append(label)
129
+ total = max(1, len(checks))
130
+ score = (len(checks) - len(failures)) / total if checks else 1.0
131
+ return Grade(not failures, score, tuple(checks), tuple(failures))
132
+
133
+
134
+ class SubprocessEvaluationTarget:
135
+ """External target adapter using explicit argv and JSON over stdin/stdout.
136
+
137
+ No shell is used. The child must return a JSON object compatible with
138
+ EvaluationOutput. This keeps the harness agent-agnostic and scriptable.
139
+ """
140
+
141
+ def __init__(
142
+ self,
143
+ name: str,
144
+ argv: tuple[str, ...],
145
+ *,
146
+ cwd: Path | None = None,
147
+ timeout_seconds: float = 300.0,
148
+ env: dict[str, str] | None = None,
149
+ ) -> None:
150
+ if not argv:
151
+ raise ValueError("argv cannot be empty")
152
+ self.name = name
153
+ self.argv = argv
154
+ self.cwd = cwd
155
+ self.timeout_seconds = timeout_seconds
156
+ self.env = env or {}
157
+
158
+ async def run(self, case: EvaluationCase) -> EvaluationOutput:
159
+ child_env = {
160
+ "PATH": os.environ.get("PATH", ""),
161
+ "HOME": os.environ.get("HOME", ""),
162
+ **self.env,
163
+ }
164
+ process = await asyncio.create_subprocess_exec(
165
+ *self.argv,
166
+ cwd=str(self.cwd) if self.cwd else None,
167
+ env=child_env,
168
+ stdin=asyncio.subprocess.PIPE,
169
+ stdout=asyncio.subprocess.PIPE,
170
+ stderr=asyncio.subprocess.PIPE,
171
+ )
172
+ request = json.dumps(asdict(case), ensure_ascii=False).encode("utf-8")
173
+ try:
174
+ stdout, stderr = await asyncio.wait_for(
175
+ process.communicate(request),
176
+ timeout=self.timeout_seconds,
177
+ )
178
+ except TimeoutError:
179
+ process.kill()
180
+ await process.wait()
181
+ raise RuntimeError(f"evaluation target timed out after {self.timeout_seconds}s") from None
182
+ if process.returncode != 0:
183
+ message = stderr.decode("utf-8", errors="replace")[-2_000:]
184
+ raise RuntimeError(f"evaluation target exited {process.returncode}: {message}")
185
+ try:
186
+ payload = json.loads(stdout.decode("utf-8"))
187
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
188
+ raise RuntimeError("evaluation target returned invalid JSON") from exc
189
+ return EvaluationOutput(
190
+ answer=str(payload.get("answer", "")),
191
+ files_touched=tuple(str(item) for item in payload.get("files_touched", [])),
192
+ tokens=int(payload.get("tokens", 0)),
193
+ tool_calls=int(payload.get("tool_calls", 0)),
194
+ metadata=dict(payload.get("metadata", {})),
195
+ )
196
+
197
+
198
+ class ExternalEvaluationSuite:
199
+ VERSION = 1
200
+
201
+ def __init__(
202
+ self,
203
+ name: str,
204
+ cases: list[EvaluationCase],
205
+ grader: DeterministicGrader | None = None,
206
+ ) -> None:
207
+ self.name = name
208
+ self.cases = cases
209
+ self.grader = grader or DeterministicGrader()
210
+
211
+ @classmethod
212
+ def load(cls, path: Path) -> ExternalEvaluationSuite:
213
+ payload = json.loads(path.read_text(encoding="utf-8"))
214
+ version = int(payload.get("version", 0))
215
+ if version != cls.VERSION:
216
+ raise ValueError(f"unsupported evaluation suite version: {version}")
217
+ cases = []
218
+ for item in payload.get("cases", []):
219
+ expectation = item.get("expectation", {})
220
+ cases.append(
221
+ EvaluationCase(
222
+ id=str(item["id"]),
223
+ prompt=str(item["prompt"]),
224
+ expectation=EvaluationExpectation(
225
+ required_strings=tuple(str(value) for value in expectation.get("required_strings", [])),
226
+ forbidden_strings=tuple(str(value) for value in expectation.get("forbidden_strings", [])),
227
+ required_paths=tuple(str(value) for value in expectation.get("required_paths", [])),
228
+ max_tokens=int(expectation["max_tokens"]) if expectation.get("max_tokens") is not None else None,
229
+ max_tool_calls=int(expectation["max_tool_calls"]) if expectation.get("max_tool_calls") is not None else None,
230
+ ),
231
+ metadata={str(key): str(value) for key, value in item.get("metadata", {}).items()},
232
+ )
233
+ )
234
+ return cls(str(payload.get("name", path.stem)), cases)
235
+
236
+ async def run(self, target: EvaluationTarget) -> EvaluationReport:
237
+ results: list[EvaluationResult] = []
238
+ for case in self.cases:
239
+ started = time.perf_counter()
240
+ try:
241
+ output = await target.run(case)
242
+ grade = self.grader.grade(case, output)
243
+ error = None
244
+ except Exception as exc:
245
+ output = EvaluationOutput(answer="")
246
+ grade = Grade(False, 0.0, (), ("execution-error",))
247
+ error = f"{type(exc).__name__}: {exc}"[:1_000]
248
+ results.append(
249
+ EvaluationResult(
250
+ case_id=case.id,
251
+ target=target.name,
252
+ duration_ms=(time.perf_counter() - started) * 1000,
253
+ output=output,
254
+ grade=grade,
255
+ error=error,
256
+ )
257
+ )
258
+ return EvaluationReport(
259
+ run_id=uuid.uuid4().hex,
260
+ suite_name=self.name,
261
+ suite_version=self.VERSION,
262
+ target=target.name,
263
+ created_at=datetime.now(UTC).isoformat(),
264
+ results=tuple(results),
265
+ )