neurocode-ai 0.1.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.
@@ -0,0 +1,638 @@
1
+ """
2
+ NeuroCode End-to-End Bug Resolution Benchmark Harness.
3
+ Directly addresses Academic Review Gap 1:
4
+ Evaluates real repository repair with automated test suites (pytest),
5
+ comparing:
6
+ 1. Raw Context Dump (Naive Baseline)
7
+ 2. BM25 Lexical RAG (Agentless Baseline)
8
+ 3. Unconstrained Graph Traversal (RepoGraph Baseline)
9
+ 4. NeuroCode Priority-Ranked Knapsack Slicer (Our Proposed Method)
10
+
11
+ Measures:
12
+ - Retrieval Accuracy (Target Bug Entity captured)
13
+ - Patch Generation & Syntactic Validity
14
+ - Unit Test Pass Rate (% Resolved)
15
+ - Token Footprint & Peak GPU VRAM
16
+ - Preprocessing, Inference, and Total Latencies
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ # Ensure project root is in sys.path
24
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
25
+
26
+ import time
27
+ import tempfile
28
+ import subprocess
29
+ from dataclasses import dataclass, field
30
+ from typing import Dict, List, Optional, Tuple, Any
31
+
32
+ import torch
33
+ from transformers import AutoModelForCausalLM, AutoTokenizer
34
+
35
+ from src.graph.builder import RepoGraphBuilder
36
+ from src.graph.slicer import CodeGraphSlicer
37
+ from src.agent.patcher import PatchApplier, SYSTEM_PROMPT
38
+
39
+
40
+ @dataclass
41
+ class BugTask:
42
+ task_id: str
43
+ title: str
44
+ problem_statement: str
45
+ files: Dict[str, str]
46
+ test_path: str
47
+ test_code: str
48
+ target_bug_file: str
49
+ target_bug_symbol: str
50
+ category: str
51
+
52
+
53
+ @dataclass
54
+ class MethodEvaluationResult:
55
+ method: str
56
+ task_id: str
57
+ input_tokens: int
58
+ output_tokens: int
59
+ prep_time_ms: float
60
+ infer_time_ms: float
61
+ total_time_ms: float
62
+ peak_vram_mb: float
63
+ captured_target: bool
64
+ valid_patch_format: bool
65
+ patch_applied: bool
66
+ test_passed: bool
67
+ model_output: str
68
+ error_message: Optional[str] = None
69
+
70
+
71
+ def get_curated_benchmark_tasks() -> List[BugTask]:
72
+ """
73
+ Curated suite of realistic multi-module software engineering bugs.
74
+ Each bug has a multi-file dependency structure and an isolated unit test suite.
75
+ """
76
+ tasks = []
77
+
78
+ # -------------------------------------------------------------------------
79
+ # Task 1: Cross-File Indirect Call Bug
80
+ # Issue keywords describe API gateway crash, but root cause is in crypto signer.
81
+ # -------------------------------------------------------------------------
82
+ tasks.append(BugTask(
83
+ task_id="NC-TASK-001",
84
+ title="Indirect Callee NoneType Crash in HMAC Verification",
85
+ category="Indirect Call Chain",
86
+ problem_statement=(
87
+ "Error: handle_incoming_request in api/gateway.py crashes with AttributeError "
88
+ "when client signature payload contains None body. Client request authentication "
89
+ "must return False instead of raising an unhandled exception."
90
+ ),
91
+ files={
92
+ "api/gateway.py": (
93
+ "from crypto.signer import verify_signature\n\n"
94
+ "def handle_incoming_request(request_payload: dict) -> dict:\n"
95
+ " '''Entrypoint that processes incoming client requests.'''\n"
96
+ " sig = request_payload.get('signature')\n"
97
+ " data = request_payload.get('body')\n"
98
+ " if not verify_signature(data, sig):\n"
99
+ " raise ValueError('Invalid client signature verification failed')\n"
100
+ " return {'status': 'success'}\n"
101
+ ),
102
+ "crypto/signer.py": (
103
+ "import hmac\n"
104
+ "import hashlib\n\n"
105
+ "def verify_signature(data: str, signature: str) -> bool:\n"
106
+ " '''Calculates HMAC SHA256 digest.'''\n"
107
+ " if not signature:\n"
108
+ " return False\n"
109
+ " expected = hmac.new(b'secret_key', data.encode('utf-8'), hashlib.sha256).hexdigest()\n"
110
+ " return hmac.compare_digest(expected, signature)\n"
111
+ ),
112
+ "api/logger.py": (
113
+ "def client_request_logger(client_name: str, signature_status: str):\n"
114
+ " '''Logs client request signature verification status.'''\n"
115
+ " print(f'Client {client_name} signature: {signature_status}')\n"
116
+ ),
117
+ "common/utils.py": (
118
+ "def format_response(code: int, message: str) -> dict:\n"
119
+ " return {'code': code, 'message': message}\n"
120
+ ),
121
+ },
122
+ test_path="tests/test_gateway.py",
123
+ test_code=(
124
+ "import pytest\n"
125
+ "from api.gateway import handle_incoming_request\n"
126
+ "from crypto.signer import verify_signature\n\n"
127
+ "def test_signature_handles_none():\n"
128
+ " # Fails if verify_signature crashes on data=None\n"
129
+ " res = verify_signature(None, 'some_sig')\n"
130
+ " assert res is False\n\n"
131
+ "def test_gateway_handles_none_body():\n"
132
+ " with pytest.raises(ValueError, match='Invalid client signature'):\n"
133
+ " handle_incoming_request({'body': None, 'signature': 'abc'})\n"
134
+ ),
135
+ target_bug_file="crypto/signer.py",
136
+ target_bug_symbol="def verify_signature",
137
+ ))
138
+
139
+ # -------------------------------------------------------------------------
140
+ # Task 2: Class Inheritance Hierarchy Override Bug
141
+ # Subclass handler crashes because parent BaseSerializer lacks ISO-8601 parsing.
142
+ # -------------------------------------------------------------------------
143
+ tasks.append(BugTask(
144
+ task_id="NC-TASK-002",
145
+ title="Class Inheritance Fallback in Datetime Serialization",
146
+ category="Inheritance Hierarchy",
147
+ problem_statement=(
148
+ "TypeError: CustomJsonSerializer in handlers/json_handler.py fails when serializing "
149
+ "objects containing datetime instances. Base serializer fallback must support "
150
+ "ISO-8601 formatting for datetime objects."
151
+ ),
152
+ files={
153
+ "core/base_handler.py": (
154
+ "from datetime import datetime\n\n"
155
+ "class BaseSerializer:\n"
156
+ " '''Base serializer with primitive dispatch.'''\n"
157
+ " def serialize_value(self, val):\n"
158
+ " if isinstance(val, (int, float, str, bool)):\n"
159
+ " return val\n"
160
+ " raise TypeError(f'Type {type(val)} not serializable')\n"
161
+ ),
162
+ "handlers/json_handler.py": (
163
+ "import json\n"
164
+ "from core.base_handler import BaseSerializer\n\n"
165
+ "class CustomJsonSerializer(BaseSerializer):\n"
166
+ " '''JSON serializer extending base serialization.'''\n"
167
+ " def serialize(self, data: dict) -> str:\n"
168
+ " out = {}\n"
169
+ " for k, v in data.items():\n"
170
+ " out[k] = self.serialize_value(v)\n"
171
+ " return json.dumps(out)\n"
172
+ ),
173
+ "handlers/xml_handler.py": (
174
+ "class XmlSerializer:\n"
175
+ " def serialize(self, data):\n"
176
+ " return '<xml></xml>'\n"
177
+ ),
178
+ "common/helpers.py": (
179
+ "def dump_json(obj):\n"
180
+ " return str(obj)\n"
181
+ ),
182
+ },
183
+ test_path="tests/test_serializer.py",
184
+ test_code=(
185
+ "from datetime import datetime\n"
186
+ "from handlers.json_handler import CustomJsonSerializer\n\n"
187
+ "def test_custom_serializer_datetime():\n"
188
+ " s = CustomJsonSerializer()\n"
189
+ " now = datetime(2026, 10, 18, 12, 0, 0)\n"
190
+ " res = s.serialize({'timestamp': now, 'status': 'ok'})\n"
191
+ " assert '2026-10-18T12:00:00' in res or '2026-10-18' in res\n"
192
+ ),
193
+ target_bug_file="core/base_handler.py",
194
+ target_bug_symbol="class BaseSerializer",
195
+ ))
196
+
197
+ # -------------------------------------------------------------------------
198
+ # Task 3: Cross-Module Config Validation Bug
199
+ # Issue mentions auth token error, but bug is in configuration loader.
200
+ # -------------------------------------------------------------------------
201
+ tasks.append(BugTask(
202
+ task_id="NC-TASK-003",
203
+ title="Cross-Module JWT Configuration Validation",
204
+ category="Module Import & Call",
205
+ problem_statement=(
206
+ "Configuration error: validate_jwt_settings in config/jwt_config.py fails to reject "
207
+ "RS256 algorithm when public_key is empty or None. It currently only checks secret_key."
208
+ ),
209
+ files={
210
+ "auth/token_manager.py": (
211
+ "from config.jwt_config import validate_jwt_settings\n\n"
212
+ "def create_auth_token(claims: dict, config: dict) -> str:\n"
213
+ " '''Generates verified auth token using configured algorithm.'''\n"
214
+ " if not validate_jwt_settings(config):\n"
215
+ " raise ValueError('Invalid JWT configuration')\n"
216
+ " return f'token.{claims.get(\"sub\")}'\n"
217
+ ),
218
+ "config/jwt_config.py": (
219
+ "def validate_jwt_settings(config: dict) -> bool:\n"
220
+ " '''Validates token configuration parameters.'''\n"
221
+ " algo = config.get('algorithm', 'HS256')\n"
222
+ " secret = config.get('secret_key')\n"
223
+ " if not secret:\n"
224
+ " return False\n"
225
+ " return True\n"
226
+ ),
227
+ "auth/user_service.py": (
228
+ "def get_user_claims(user_id: str) -> dict:\n"
229
+ " return {'sub': user_id, 'role': 'admin'}\n"
230
+ ),
231
+ "config/db_config.py": (
232
+ "def get_database_url():\n"
233
+ " return 'sqlite:///:memory:'\n"
234
+ ),
235
+ },
236
+ test_path="tests/test_jwt_config.py",
237
+ test_code=(
238
+ "import pytest\n"
239
+ "from config.jwt_config import validate_jwt_settings\n"
240
+ "from auth.token_manager import create_auth_token\n\n"
241
+ "def test_rs256_requires_public_key():\n"
242
+ " bad_cfg = {'algorithm': 'RS256', 'secret_key': 'secret', 'public_key': None}\n"
243
+ " assert validate_jwt_settings(bad_cfg) is False\n\n"
244
+ "def test_token_creation_fails_on_bad_rs256():\n"
245
+ " bad_cfg = {'algorithm': 'RS256', 'secret_key': 'secret', 'public_key': ''}\n"
246
+ " with pytest.raises(ValueError, match='Invalid JWT configuration'):\n"
247
+ " create_auth_token({'sub': 'user1'}, bad_cfg)\n"
248
+ ),
249
+ target_bug_file="config/jwt_config.py",
250
+ target_bug_symbol="def validate_jwt_settings",
251
+ ))
252
+
253
+ # -------------------------------------------------------------------------
254
+ # Task 4: Multi-hop Payment Charge Amount Boundary Check
255
+ # Entrypoint in checkout route calls payment client where negative amount is permitted.
256
+ # -------------------------------------------------------------------------
257
+ tasks.append(BugTask(
258
+ task_id="NC-TASK-004",
259
+ title="Nested Multi-hop Payment Negative Charge Guard",
260
+ category="Nested Call Chain",
261
+ problem_statement=(
262
+ "Bug: checkout route allows negative charge amounts to reach payment gateway. "
263
+ "create_charge in payments/gateway_client.py must raise ValueError if amount <= 0."
264
+ ),
265
+ files={
266
+ "web/checkout_route.py": (
267
+ "from services.billing import process_order_payment\n\n"
268
+ "def checkout_endpoint(order_id: str, amount: float) -> dict:\n"
269
+ " '''Web handler for processing checkout orders.'''\n"
270
+ " charge_id = process_order_payment(order_id, amount)\n"
271
+ " return {'order_id': order_id, 'charge_id': charge_id, 'status': 'paid'}\n"
272
+ ),
273
+ "services/billing.py": (
274
+ "from payments.gateway_client import create_charge\n\n"
275
+ "def process_order_payment(order_id: str, amount: float) -> str:\n"
276
+ " '''Orchestrates billing transaction for an order.'''\n"
277
+ " return create_charge(f'order_{order_id}', amount)\n"
278
+ ),
279
+ "payments/gateway_client.py": (
280
+ "def create_charge(customer_ref: str, amount: float) -> str:\n"
281
+ " '''Submits charge to payment backend.'''\n"
282
+ " return f'ch_{customer_ref}_{int(amount)}'\n"
283
+ ),
284
+ "web/distractor_payment.py": (
285
+ "def log_payment_amount(order_id, amount):\n"
286
+ " '''Logs payment checkout amount.'''\n"
287
+ " print(f'Order {order_id} checkout amount: {amount}')\n"
288
+ ),
289
+ },
290
+ test_path="tests/test_payment.py",
291
+ test_code=(
292
+ "import pytest\n"
293
+ "from payments.gateway_client import create_charge\n"
294
+ "from web.checkout_route import checkout_endpoint\n\n"
295
+ "def test_negative_charge_rejected():\n"
296
+ " with pytest.raises(ValueError, match='amount'):\n"
297
+ " create_charge('cust_1', -50.0)\n\n"
298
+ "def test_zero_charge_rejected():\n"
299
+ " with pytest.raises(ValueError, match='amount'):\n"
300
+ " create_charge('cust_1', 0.0)\n\n"
301
+ "def test_valid_charge_succeeds():\n"
302
+ " res = create_charge('cust_1', 100.0)\n"
303
+ " assert res.startswith('ch_')\n"
304
+ ),
305
+ target_bug_file="payments/gateway_client.py",
306
+ target_bug_symbol="def create_charge",
307
+ ))
308
+
309
+ # -------------------------------------------------------------------------
310
+ # Task 5: Database Query Boundary Slicing
311
+ # sanitize_limit allows negative offset/limit causing index crashes.
312
+ # -------------------------------------------------------------------------
313
+ tasks.append(BugTask(
314
+ task_id="NC-TASK-005",
315
+ title="Query Builder Sanitization Boundary Check",
316
+ category="Database Query Slicing",
317
+ problem_statement=(
318
+ "Issue: sanitize_limit in db/query_builder.py allows negative integers, "
319
+ "causing SQL syntax errors. It must enforce limit >= 1 and clamp values greater than 100 to 100."
320
+ ),
321
+ files={
322
+ "db/repository.py": (
323
+ "from db.query_builder import sanitize_limit\n\n"
324
+ "class UserRepository:\n"
325
+ " def fetch_users(self, limit: int = 10) -> str:\n"
326
+ " valid_limit = sanitize_limit(limit)\n"
327
+ " return f'SELECT * FROM users LIMIT {valid_limit}'\n"
328
+ ),
329
+ "db/query_builder.py": (
330
+ "def sanitize_limit(limit: int) -> int:\n"
331
+ " '''Sanitizes user pagination limit.'''\n"
332
+ " return limit\n"
333
+ ),
334
+ "db/connection.py": (
335
+ "def get_pool():\n"
336
+ " return {}\n"
337
+ ),
338
+ "api/user_controller.py": (
339
+ "def get_user_page(page: int, limit: int):\n"
340
+ " return {'page': page, 'limit': limit}\n"
341
+ ),
342
+ },
343
+ test_path="tests/test_query.py",
344
+ test_code=(
345
+ "import pytest\n"
346
+ "from db.query_builder import sanitize_limit\n"
347
+ "from db.repository import UserRepository\n\n"
348
+ "def test_sanitize_limit_clamping():\n"
349
+ " assert sanitize_limit(150) == 100\n"
350
+ " assert sanitize_limit(50) == 50\n\n"
351
+ "def test_sanitize_limit_negative():\n"
352
+ " with pytest.raises(ValueError, match='limit'):\n"
353
+ " sanitize_limit(-5)\n"
354
+ ),
355
+ target_bug_file="db/query_builder.py",
356
+ target_bug_symbol="def sanitize_limit",
357
+ ))
358
+
359
+ return tasks
360
+
361
+
362
+ class EndToEndBenchmarkRunner:
363
+ """
364
+ Executes real multi-condition evaluation on physical hardware.
365
+ """
366
+ def __init__(self, model_id: str = "Qwen/Qwen2.5-Coder-0.5B-Instruct", use_gpu: bool = True):
367
+ self.model_id = model_id
368
+ self.use_gpu = use_gpu and torch.cuda.is_available()
369
+ self.device = "cuda" if self.use_gpu else "cpu"
370
+ self.tokenizer = None
371
+ self.model = None
372
+
373
+ def load_model(self):
374
+ if self.model is None:
375
+ print(f"[BENCHMARK] Loading {self.model_id} on {self.device}...")
376
+ t0 = time.perf_counter()
377
+ self.tokenizer = AutoTokenizer.from_pretrained(self.model_id)
378
+ self.model = AutoModelForCausalLM.from_pretrained(
379
+ self.model_id,
380
+ torch_dtype=torch.float16 if self.use_gpu else torch.float32,
381
+ device_map=self.device
382
+ )
383
+ print(f"[BENCHMARK] Model loaded in {time.perf_counter() - t0:.2f}s")
384
+
385
+ def _setup_workspace(self, task: BugTask, target_dir: Path):
386
+ for rel_path, content in task.files.items():
387
+ file_path = target_dir / rel_path
388
+ file_path.parent.mkdir(parents=True, exist_ok=True)
389
+ file_path.write_text(content, encoding="utf-8")
390
+
391
+ test_file = target_dir / task.test_path
392
+ test_file.parent.mkdir(parents=True, exist_ok=True)
393
+ test_file.write_text(task.test_code, encoding="utf-8")
394
+
395
+ def _run_pytest(self, workspace_dir: Path, test_rel_path: str) -> bool:
396
+ env = os.environ.copy()
397
+ env["PYTHONPATH"] = str(workspace_dir)
398
+ try:
399
+ res = subprocess.run(
400
+ [sys.executable, "-m", "pytest", "-p", "no:launch_testing", test_rel_path],
401
+ cwd=str(workspace_dir),
402
+ capture_output=True,
403
+ text=True,
404
+ env=env,
405
+ timeout=15
406
+ )
407
+ return res.returncode == 0
408
+ except Exception:
409
+ return False
410
+
411
+ def build_prompt_raw(self, workspace_dir: Path, task: BugTask) -> Tuple[str, float]:
412
+ t0 = time.perf_counter()
413
+ full_text = []
414
+ for rel_path in sorted(task.files.keys()):
415
+ content = (workspace_dir / rel_path).read_text(encoding="utf-8")
416
+ full_text.append(f"FILE: {rel_path}\n```python\n{content}\n```")
417
+ context_str = "\n\n".join(full_text)
418
+ prep_ms = (time.perf_counter() - t0) * 1000
419
+
420
+ prompt = (
421
+ f"You are an expert autonomous software engineer.\n"
422
+ f"Repository Files:\n{context_str}\n\n"
423
+ f"Issue to fix: {task.problem_statement}\n"
424
+ f"Provide the fix using exact SEARCH/REPLACE format:\n"
425
+ f"FILE: <file_path>\n<<<<<<< SEARCH\n=======\n>>>>>>>\n"
426
+ )
427
+ return prompt, prep_ms
428
+
429
+ def build_prompt_bm25(self, workspace_dir: Path, task: BugTask) -> Tuple[str, float]:
430
+ t0 = time.perf_counter()
431
+ q_tokens = set(task.problem_statement.lower().replace(".", " ").replace("/", " ").split())
432
+ scores = {}
433
+ for rel_path in task.files.keys():
434
+ content = (workspace_dir / rel_path).read_text(encoding="utf-8")
435
+ words = content.lower().split()
436
+ score = sum(words.count(q) for q in q_tokens)
437
+ scores[rel_path] = score
438
+
439
+ top_files = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:2]
440
+ context_parts = []
441
+ for rel_path, _ in top_files:
442
+ content = (workspace_dir / rel_path).read_text(encoding="utf-8")
443
+ context_parts.append(f"FILE: {rel_path}\n```python\n{content}\n```")
444
+ context_str = "\n\n".join(context_parts)
445
+ prep_ms = (time.perf_counter() - t0) * 1000
446
+
447
+ prompt = (
448
+ f"You are an expert autonomous software engineer.\n"
449
+ f"Retrieved Context:\n{context_str}\n\n"
450
+ f"Issue to fix: {task.problem_statement}\n"
451
+ f"Provide the fix using exact SEARCH/REPLACE format:\n"
452
+ f"FILE: <file_path>\n<<<<<<< SEARCH\n=======\n>>>>>>>\n"
453
+ )
454
+ return prompt, prep_ms
455
+
456
+ def build_prompt_graph_unconstrained(self, workspace_dir: Path, task: BugTask) -> Tuple[str, float]:
457
+ t0 = time.perf_counter()
458
+ builder = RepoGraphBuilder(str(workspace_dir))
459
+ graph = builder.build()
460
+ slicer = CodeGraphSlicer(graph)
461
+ seeds = slicer.find_seed_nodes(task.problem_statement)
462
+ # Unconstrained 1-hop traversal without knapsack packing
463
+ subgraph = slicer.extract_subgraph(seeds, k_hops=1)
464
+
465
+ # Serialize entire subgraph without token budgeting
466
+ lines = []
467
+ for node in subgraph.nodes:
468
+ data = subgraph.nodes[node]
469
+ lines.append(f"Node: {node} ({data.get('type')})\n{data.get('source', '')}\n")
470
+ context_str = "\n".join(lines)
471
+ prep_ms = (time.perf_counter() - t0) * 1000
472
+
473
+ prompt = (
474
+ f"You are an expert autonomous software engineer.\n"
475
+ f"Graph Context:\n{context_str}\n\n"
476
+ f"Issue to fix: {task.problem_statement}\n"
477
+ f"Provide the fix using exact SEARCH/REPLACE format:\n"
478
+ f"FILE: <file_path>\n<<<<<<< SEARCH\n=======\n>>>>>>>\n"
479
+ )
480
+ return prompt, prep_ms
481
+
482
+ def build_prompt_neurocode(self, workspace_dir: Path, task: BugTask) -> Tuple[str, float]:
483
+ t0 = time.perf_counter()
484
+ builder = RepoGraphBuilder(str(workspace_dir))
485
+ graph = builder.build()
486
+ slicer = CodeGraphSlicer(graph)
487
+ seeds = slicer.find_seed_nodes(task.problem_statement)
488
+ subgraph = slicer.extract_subgraph(seeds, k_hops=1)
489
+ slice_text = slicer.format_slice_for_llm(subgraph, seeds, query=task.problem_statement, max_tokens=1500)
490
+ prep_ms = (time.perf_counter() - t0) * 1000
491
+
492
+ prompt = (
493
+ f"You are an expert autonomous software engineer.\n"
494
+ f"{slice_text}\n\n"
495
+ f"Issue to fix: {task.problem_statement}\n"
496
+ f"Provide the fix using exact SEARCH/REPLACE format:\n"
497
+ f"FILE: <file_path>\n<<<<<<< SEARCH\n=======\n>>>>>>>\n"
498
+ )
499
+ return prompt, prep_ms
500
+
501
+ def evaluate_task_method(self, task: BugTask, method: str) -> MethodEvaluationResult:
502
+ with tempfile.TemporaryDirectory() as td:
503
+ workspace = Path(td)
504
+ self._setup_workspace(task, workspace)
505
+
506
+ # 1. Verify that test originally FAILS
507
+ initial_test_passed = self._run_pytest(workspace, task.test_path)
508
+ assert not initial_test_passed, f"Task {task.task_id} test passed before bug patch! Invalid test."
509
+
510
+ builder = RepoGraphBuilder(str(workspace))
511
+ graph = builder.build()
512
+
513
+ # 2. Build Prompt according to Method
514
+ if method == "Raw Context Dump":
515
+ prompt, prep_ms = self.build_prompt_raw(workspace, task)
516
+ elif method == "BM25 Lexical RAG":
517
+ prompt, prep_ms = self.build_prompt_bm25(workspace, task)
518
+ elif method == "Graph Unconstrained":
519
+ prompt, prep_ms = self.build_prompt_graph_unconstrained(workspace, task)
520
+ elif method == "NeuroCode (Our Method)":
521
+ prompt, prep_ms = self.build_prompt_neurocode(workspace, task)
522
+ else:
523
+ raise ValueError(f"Unknown method {method}")
524
+
525
+ captured_target = task.target_bug_symbol in prompt and task.target_bug_file in prompt
526
+
527
+ messages = [
528
+ {"role": "system", "content": SYSTEM_PROMPT},
529
+ {"role": "user", "content": prompt}
530
+ ]
531
+ chat_prompt = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
532
+ inputs = self.tokenizer(chat_prompt, return_tensors="pt").to(self.device)
533
+ input_tokens = inputs.input_ids.shape[1]
534
+
535
+ # 3. Model Inference on Device
536
+ if self.use_gpu:
537
+ torch.cuda.reset_peak_memory_stats()
538
+ torch.cuda.empty_cache()
539
+
540
+ t0_infer = time.perf_counter()
541
+
542
+ with torch.no_grad():
543
+ outputs = self.model.generate(
544
+ **inputs,
545
+ max_new_tokens=220,
546
+ temperature=0.2,
547
+ do_sample=True,
548
+ pad_token_id=self.tokenizer.eos_token_id
549
+ )
550
+
551
+ infer_ms = (time.perf_counter() - t0_infer) * 1000
552
+ total_ms = prep_ms + infer_ms
553
+ output_tokens = outputs.shape[1] - inputs.input_ids.shape[1]
554
+ response_text = self.tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
555
+
556
+ peak_vram_mb = 0.0
557
+ if self.use_gpu:
558
+ peak_vram_mb = torch.cuda.max_memory_allocated() / (1024 ** 2)
559
+
560
+ # 4. Patch Application & Verification (with AST fallback)
561
+ patch_res = PatchApplier.extract_patch(response_text, graph=graph, repo_dir=str(workspace))
562
+ valid_patch = patch_res.success
563
+ patch_applied = False
564
+ test_passed = False
565
+ err_msg = None
566
+
567
+ if valid_patch:
568
+ target_file_path = workspace / patch_res.file_path
569
+ if target_file_path.exists():
570
+ applied, msg = PatchApplier.apply_patch_to_file(
571
+ str(target_file_path),
572
+ patch_res.search_block,
573
+ patch_res.replace_block
574
+ )
575
+ patch_applied = applied
576
+ if applied:
577
+ test_passed = self._run_pytest(workspace, task.test_path)
578
+ else:
579
+ err_msg = msg
580
+ else:
581
+ err_msg = f"Target file does not exist: {patch_res.file_path}"
582
+ else:
583
+ err_msg = patch_res.error_message
584
+
585
+ return MethodEvaluationResult(
586
+ method=method,
587
+ task_id=task.task_id,
588
+ input_tokens=input_tokens,
589
+ output_tokens=output_tokens,
590
+ prep_time_ms=prep_ms,
591
+ infer_time_ms=infer_ms,
592
+ total_time_ms=total_ms,
593
+ peak_vram_mb=peak_vram_mb,
594
+ captured_target=captured_target,
595
+ valid_patch_format=valid_patch,
596
+ patch_applied=patch_applied,
597
+ test_passed=test_passed,
598
+ model_output=response_text,
599
+ error_message=err_msg
600
+ )
601
+
602
+
603
+ def run_full_end_to_end_benchmark() -> List[MethodEvaluationResult]:
604
+ print("==========================================================================")
605
+ print(" NEUROCODE VERIFIED END-TO-END BUG RESOLUTION BENCHMARK ")
606
+ print("==========================================================================\n")
607
+
608
+ tasks = get_curated_benchmark_tasks()
609
+ print(f"Total Evaluated Tasks: {len(tasks)}")
610
+
611
+ runner = EndToEndBenchmarkRunner()
612
+ runner.load_model()
613
+
614
+ methods = [
615
+ "Raw Context Dump",
616
+ "BM25 Lexical RAG",
617
+ "Graph Unconstrained",
618
+ "NeuroCode (Our Method)"
619
+ ]
620
+
621
+ all_results = []
622
+
623
+ for task in tasks:
624
+ print(f"\n--------------------------------------------------------------------------")
625
+ print(f"Executing Task: [{task.task_id}] {task.title} ({task.category})")
626
+ print(f"--------------------------------------------------------------------------")
627
+ for method in methods:
628
+ print(f" -> Running Condition: {method:25s} ... ", end="", flush=True)
629
+ res = runner.evaluate_task_method(task, method)
630
+ all_results.append(res)
631
+ status = "RESOLVED [PASS]" if res.test_passed else ("APPLIED [FAIL TEST]" if res.patch_applied else "NO PATCH")
632
+ print(f"{status:18s} | In Tokens: {res.input_tokens:4d} | Target In Context: {res.captured_target} | VRAM: {res.peak_vram_mb:.1f}MB")
633
+
634
+ return all_results
635
+
636
+
637
+ if __name__ == "__main__":
638
+ results = run_full_end_to_end_benchmark()