Skip to content

Commit 779407b

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: introduce _BoundEmitter to eliminate event emission boilerplate in engine
Every emit call in engine.py repeated `Event(type=..., run_id=state.run_id, data={...})`. A lightweight _BoundEmitter callable captures the run_id once and simplifies all 15 call sites from `emitter.emit(Event(type=X, run_id=state.run_id, data={...}))` to `emit(X, {...})`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9303fe0 commit 779407b

1 file changed

Lines changed: 83 additions & 118 deletions

File tree

src/ralphify/engine.py

Lines changed: 83 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from datetime import datetime
1717
from enum import Enum
1818
from pathlib import Path
19-
from typing import NamedTuple
19+
from typing import Any, NamedTuple
2020
from ralphify._events import Event, EventEmitter, EventType, NullEmitter
2121
from ralphify._output import collect_output, format_duration
2222
from ralphify.checks import (
@@ -172,34 +172,47 @@ def _discover_enabled_primitives(root: Path) -> EnabledPrimitives:
172172
)
173173

174174

175-
def _wait_for_resume(state: RunState, emitter: EventEmitter) -> bool:
175+
class _BoundEmitter:
176+
"""Wraps an EventEmitter with a fixed run_id for concise emission.
177+
178+
Engine-internal helper so every call site doesn't have to repeat
179+
``Event(type=..., run_id=state.run_id, data={...})``.
180+
"""
181+
182+
def __init__(self, emitter: EventEmitter, run_id: str) -> None:
183+
self._emitter = emitter
184+
self._run_id = run_id
185+
186+
def __call__(
187+
self, event_type: EventType, data: dict[str, Any] | None = None,
188+
) -> None:
189+
self._emitter.emit(Event(
190+
type=event_type, run_id=self._run_id, data=data or {},
191+
))
192+
193+
194+
def _wait_for_resume(state: RunState, emit: _BoundEmitter) -> bool:
176195
"""Block until the run is resumed or a stop is requested.
177196
178197
Returns ``True`` if the run should continue, ``False`` if a stop was
179198
requested while paused.
180199
"""
181-
emitter.emit(Event(
182-
type=EventType.RUN_PAUSED,
183-
run_id=state.run_id,
184-
))
200+
emit(EventType.RUN_PAUSED)
185201
while not state.wait_for_unpause(timeout=0.25):
186202
if state.stop_requested:
187203
break
188204
if state.stop_requested:
189205
state.status = RunStatus.STOPPED
190206
return False
191-
emitter.emit(Event(
192-
type=EventType.RUN_RESUMED,
193-
run_id=state.run_id,
194-
))
207+
emit(EventType.RUN_RESUMED)
195208
return True
196209

197210

198211
def _handle_loop_transitions(
199212
state: RunState,
200213
config: RunConfig,
201214
primitives: EnabledPrimitives,
202-
emitter: EventEmitter,
215+
emit: _BoundEmitter,
203216
) -> tuple[bool, EnabledPrimitives]:
204217
"""Handle stop, pause, and reload transitions at the top of each iteration.
205218
@@ -212,20 +225,16 @@ def _handle_loop_transitions(
212225
return False, primitives
213226

214227
if state.paused:
215-
if not _wait_for_resume(state, emitter):
228+
if not _wait_for_resume(state, emit):
216229
return False, primitives
217230

218231
if state.consume_reload_request():
219232
primitives = _discover_enabled_primitives(config.project_root)
220-
emitter.emit(Event(
221-
type=EventType.PRIMITIVES_RELOADED,
222-
run_id=state.run_id,
223-
data={
224-
"checks": len(primitives.checks),
225-
"contexts": len(primitives.contexts),
226-
"instructions": len(primitives.instructions),
227-
},
228-
))
233+
emit(EventType.PRIMITIVES_RELOADED, {
234+
"checks": len(primitives.checks),
235+
"contexts": len(primitives.contexts),
236+
"instructions": len(primitives.instructions),
237+
})
229238

230239
return True, primitives
231240

@@ -262,7 +271,7 @@ def _execute_agent(
262271
config: RunConfig,
263272
state: RunState,
264273
log_path_dir: Path | None,
265-
emitter: EventEmitter,
274+
emit: _BoundEmitter,
266275
) -> int | None:
267276
"""Run the agent subprocess and emit the result event.
268277
@@ -318,26 +327,22 @@ def _execute_agent(
318327
event_type = EventType.ITERATION_FAILED
319328
state_detail = f"failed with exit code {returncode} ({duration})"
320329

321-
emitter.emit(Event(
322-
type=event_type,
323-
run_id=state.run_id,
324-
data={
325-
"iteration": iteration,
326-
"returncode": returncode,
327-
"duration": elapsed,
328-
"duration_formatted": duration,
329-
"detail": state_detail,
330-
"log_file": str(log_file) if log_file else None,
331-
},
332-
))
330+
emit(event_type, {
331+
"iteration": iteration,
332+
"returncode": returncode,
333+
"duration": elapsed,
334+
"duration_formatted": duration,
335+
"detail": state_detail,
336+
"log_file": str(log_file) if log_file else None,
337+
})
333338
return returncode
334339

335340

336341
def _run_checks_phase(
337342
enabled_checks: list[Check],
338343
project_root: Path,
339344
state: RunState,
340-
emitter: EventEmitter,
345+
emit: _BoundEmitter,
341346
) -> str:
342347
"""Execute all checks, emit per-check and summary events.
343348
@@ -346,11 +351,7 @@ def _run_checks_phase(
346351
"""
347352
iteration = state.iteration
348353

349-
emitter.emit(Event(
350-
type=EventType.CHECKS_STARTED,
351-
run_id=state.run_id,
352-
data={"iteration": iteration, "count": len(enabled_checks)},
353-
))
354+
emit(EventType.CHECKS_STARTED, {"iteration": iteration, "count": len(enabled_checks)})
354355

355356
check_results = run_all_checks(enabled_checks, project_root)
356357

@@ -364,23 +365,18 @@ def _run_checks_phase(
364365
"timed_out": cr.timed_out,
365366
}
366367
results_data.append(result)
367-
emitter.emit(Event(
368-
type=EventType.CHECK_PASSED if cr.passed else EventType.CHECK_FAILED,
369-
run_id=state.run_id,
370-
data={"iteration": iteration, **result},
371-
))
368+
emit(
369+
EventType.CHECK_PASSED if cr.passed else EventType.CHECK_FAILED,
370+
{"iteration": iteration, **result},
371+
)
372372

373373
passed = sum(1 for r in results_data if r["passed"])
374-
emitter.emit(Event(
375-
type=EventType.CHECKS_COMPLETED,
376-
run_id=state.run_id,
377-
data={
378-
"iteration": iteration,
379-
"passed": passed,
380-
"failed": len(results_data) - passed,
381-
"results": results_data,
382-
},
383-
))
374+
emit(EventType.CHECKS_COMPLETED, {
375+
"iteration": iteration,
376+
"passed": passed,
377+
"failed": len(results_data) - passed,
378+
"results": results_data,
379+
})
384380

385381
return format_check_failures(check_results)
386382

@@ -391,7 +387,7 @@ def _run_iteration(
391387
primitives: EnabledPrimitives,
392388
log_path_dir: Path | None,
393389
check_failures_text: str,
394-
emitter: EventEmitter,
390+
emit: _BoundEmitter,
395391
) -> tuple[str, bool]:
396392
"""Execute one iteration of the agent loop.
397393
@@ -402,49 +398,33 @@ def _run_iteration(
402398
"""
403399
iteration = state.iteration
404400

405-
emitter.emit(Event(
406-
type=EventType.ITERATION_STARTED,
407-
run_id=state.run_id,
408-
data={"iteration": iteration},
409-
))
401+
emit(EventType.ITERATION_STARTED, {"iteration": iteration})
410402

411403
# Run contexts (subprocess I/O)
412404
context_results: list[ContextResult] = []
413405
if primitives.contexts:
414406
context_results = run_all_contexts(
415407
primitives.contexts, config.project_root,
416408
)
417-
emitter.emit(Event(
418-
type=EventType.CONTEXTS_RESOLVED,
419-
run_id=state.run_id,
420-
data={"iteration": iteration, "count": len(primitives.contexts)},
421-
))
409+
emit(EventType.CONTEXTS_RESOLVED, {"iteration": iteration, "count": len(primitives.contexts)})
422410

423411
# Assemble prompt (pure text resolution)
424412
prompt = _assemble_prompt(
425413
config, primitives, context_results, check_failures_text,
426414
)
427-
emitter.emit(Event(
428-
type=EventType.PROMPT_ASSEMBLED,
429-
run_id=state.run_id,
430-
data={"iteration": iteration, "prompt_length": len(prompt)},
431-
))
415+
emit(EventType.PROMPT_ASSEMBLED, {"iteration": iteration, "prompt_length": len(prompt)})
432416

433417
returncode = _execute_agent(
434-
prompt, config, state, log_path_dir, emitter,
418+
prompt, config, state, log_path_dir, emit,
435419
)
436420

437421
if returncode != 0 and config.stop_on_error:
438-
emitter.emit(Event(
439-
type=EventType.LOG_MESSAGE,
440-
run_id=state.run_id,
441-
data={"message": "Stopping due to --stop-on-error.", "level": "error"},
442-
))
422+
emit(EventType.LOG_MESSAGE, {"message": "Stopping due to --stop-on-error.", "level": "error"})
443423
return check_failures_text, False
444424

445425
if primitives.checks:
446426
check_failures_text = _run_checks_phase(
447-
primitives.checks, config.project_root, state, emitter,
427+
primitives.checks, config.project_root, state, emit,
448428
)
449429

450430
return check_failures_text, True
@@ -467,6 +447,7 @@ def run_loop(
467447
if emitter is None:
468448
emitter = NullEmitter()
469449

450+
emit = _BoundEmitter(emitter, state.run_id)
470451
state.status = RunStatus.RUNNING
471452

472453
log_path_dir: Path | None = None
@@ -477,24 +458,20 @@ def run_loop(
477458
check_failures_text = ""
478459
primitives = _discover_enabled_primitives(config.project_root)
479460

480-
emitter.emit(Event(
481-
type=EventType.RUN_STARTED,
482-
run_id=state.run_id,
483-
data={
484-
"checks": len(primitives.checks),
485-
"contexts": len(primitives.contexts),
486-
"instructions": len(primitives.instructions),
487-
"max_iterations": config.max_iterations,
488-
"timeout": config.timeout,
489-
"delay": config.delay,
490-
"prompt_name": config.prompt_name,
491-
},
492-
))
461+
emit(EventType.RUN_STARTED, {
462+
"checks": len(primitives.checks),
463+
"contexts": len(primitives.contexts),
464+
"instructions": len(primitives.instructions),
465+
"max_iterations": config.max_iterations,
466+
"timeout": config.timeout,
467+
"delay": config.delay,
468+
"prompt_name": config.prompt_name,
469+
})
493470

494471
try:
495472
while True:
496473
should_continue, primitives = _handle_loop_transitions(
497-
state, config, primitives, emitter,
474+
state, config, primitives, emit,
498475
)
499476
if not should_continue:
500477
break
@@ -504,7 +481,7 @@ def run_loop(
504481
break
505482

506483
check_failures_text, should_continue = _run_iteration(
507-
config, state, primitives, log_path_dir, check_failures_text, emitter,
484+
config, state, primitives, log_path_dir, check_failures_text, emit,
508485
)
509486
if not should_continue:
510487
break
@@ -513,27 +490,19 @@ def run_loop(
513490
if config.delay > 0 and (
514491
config.max_iterations is None or state.iteration < config.max_iterations
515492
):
516-
emitter.emit(Event(
517-
type=EventType.LOG_MESSAGE,
518-
run_id=state.run_id,
519-
data={"message": f"Waiting {config.delay}s...", "level": "info"},
520-
))
493+
emit(EventType.LOG_MESSAGE, {"message": f"Waiting {config.delay}s...", "level": "info"})
521494
time.sleep(config.delay)
522495

523496
except KeyboardInterrupt:
524497
pass
525498
except Exception as exc:
526499
state.status = RunStatus.FAILED
527500
tb = traceback.format_exc()
528-
emitter.emit(Event(
529-
type=EventType.LOG_MESSAGE,
530-
run_id=state.run_id,
531-
data={
532-
"message": f"Run crashed: {exc}",
533-
"level": "error",
534-
"traceback": tb,
535-
},
536-
))
501+
emit(EventType.LOG_MESSAGE, {
502+
"message": f"Run crashed: {exc}",
503+
"level": "error",
504+
"traceback": tb,
505+
})
537506

538507
if state.status == RunStatus.RUNNING:
539508
state.status = RunStatus.COMPLETED
@@ -544,14 +513,10 @@ def run_loop(
544513
else "completed"
545514
)
546515
total = state.completed + state.failed
547-
emitter.emit(Event(
548-
type=EventType.RUN_STOPPED,
549-
run_id=state.run_id,
550-
data={
551-
"reason": reason,
552-
"total": total,
553-
"completed": state.completed,
554-
"failed": state.failed,
555-
"timed_out": state.timed_out,
556-
},
557-
))
516+
emit(EventType.RUN_STOPPED, {
517+
"reason": reason,
518+
"total": total,
519+
"completed": state.completed,
520+
"failed": state.failed,
521+
"timed_out": state.timed_out,
522+
})

0 commit comments

Comments
 (0)