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.
- codecortex/__init__.py +3 -0
- codecortex/architecture/__init__.py +23 -0
- codecortex/architecture/drift.py +182 -0
- codecortex/architecture/inference.py +160 -0
- codecortex/backends/__init__.py +35 -0
- codecortex/backends/base.py +38 -0
- codecortex/backends/context.py +118 -0
- codecortex/backends/contracts.py +65 -0
- codecortex/backends/factory.py +60 -0
- codecortex/backends/graph.py +107 -0
- codecortex/backends/manager.py +303 -0
- codecortex/backends/mcp_client.py +196 -0
- codecortex/backends/pool.py +128 -0
- codecortex/backends/spec.py +83 -0
- codecortex/backends/symbols.py +189 -0
- codecortex/benchmark.py +211 -0
- codecortex/cli.py +409 -0
- codecortex/config.py +27 -0
- codecortex/context/__init__.py +13 -0
- codecortex/context/budget.py +62 -0
- codecortex/context/integrated.py +60 -0
- codecortex/context/pipeline.py +223 -0
- codecortex/core/__init__.py +1 -0
- codecortex/core/contracts.py +45 -0
- codecortex/core/errors.py +17 -0
- codecortex/core/models.py +69 -0
- codecortex/dashboard.py +259 -0
- codecortex/editing.py +41 -0
- codecortex/engines/__init__.py +5 -0
- codecortex/engines/builtin/__init__.py +5 -0
- codecortex/engines/builtin/factory.py +26 -0
- codecortex/engines/builtin/memory.py +35 -0
- codecortex/engines/builtin/repository.py +73 -0
- codecortex/engines/builtin/symbols.py +89 -0
- codecortex/engines/builtin/validation.py +54 -0
- codecortex/engines/registry.py +26 -0
- codecortex/entrypoint.py +266 -0
- codecortex/evaluation/__init__.py +55 -0
- codecortex/evaluation/external.py +265 -0
- codecortex/evaluation/production.py +670 -0
- codecortex/evaluation/regression.py +188 -0
- codecortex/gateway.py +38 -0
- codecortex/git_intelligence.py +252 -0
- codecortex/indexing/__init__.py +6 -0
- codecortex/indexing/graph.py +78 -0
- codecortex/indexing/impact.py +127 -0
- codecortex/indexing/incremental.py +163 -0
- codecortex/indexing/incremental_graph.py +189 -0
- codecortex/indexing/indexer.py +172 -0
- codecortex/indexing/relationships.py +179 -0
- codecortex/indexing/resolution.py +88 -0
- codecortex/integrations/__init__.py +5 -0
- codecortex/integrations/agents.py +235 -0
- codecortex/interfaces/__init__.py +1 -0
- codecortex/interfaces/mcp_bridge.py +66 -0
- codecortex/languages/__init__.py +5 -0
- codecortex/languages/native.py +166 -0
- codecortex/languages/registry.py +232 -0
- codecortex/mcp/__init__.py +5 -0
- codecortex/mcp/extended.py +114 -0
- codecortex/mcp/server.py +473 -0
- codecortex/memory/__init__.py +11 -0
- codecortex/memory/json_store.py +52 -0
- codecortex/memory/knowledge.py +193 -0
- codecortex/memory/team_store.py +193 -0
- codecortex/orchestrator.py +153 -0
- codecortex/pr_intelligence.py +214 -0
- codecortex/retrieval/__init__.py +16 -0
- codecortex/retrieval/hybrid.py +67 -0
- codecortex/retrieval/index.py +135 -0
- codecortex/retrieval/providers.py +67 -0
- codecortex/retrieval/repository.py +94 -0
- codecortex/router/__init__.py +5 -0
- codecortex/router/router.py +79 -0
- codecortex/runtime.py +69 -0
- codecortex/setup.py +100 -0
- codecortex/symbols/__init__.py +5 -0
- codecortex/symbols/providers.py +192 -0
- codecortex/telemetry/__init__.py +5 -0
- codecortex/telemetry/collector.py +43 -0
- codecortex/tracing/__init__.py +9 -0
- codecortex/tracing/task_trace.py +235 -0
- codecortex/workspace/__init__.py +9 -0
- codecortex/workspace/federation.py +173 -0
- codecortex_context_engine-0.1.0a1.dist-info/METADATA +381 -0
- codecortex_context_engine-0.1.0a1.dist-info/RECORD +90 -0
- codecortex_context_engine-0.1.0a1.dist-info/WHEEL +4 -0
- codecortex_context_engine-0.1.0a1.dist-info/entry_points.txt +3 -0
- codecortex_context_engine-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- 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
|
+
)
|