feat: async/parallel execution with configurable concurrency
Parallelize LLM calls across minibatches to reduce wall-clock time. All domain ports (LLMPort, JudgePort, ProposerPort) are now async. Adapter implementations wrap synchronous DSPy calls with asyncio.to_thread. Judge calls run in parallel within a batch using asyncio.gather + semaphore. Evaluator parallelizes minibatch execution with configurable concurrency. Evolution loop and use case are fully async. Proposer stays sequential. Added --max-concurrency CLI flag and max_concurrency YAML config field. Added async_retry_with_backoff for async error handling. All 139 unit tests pass. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -6,12 +6,14 @@ Converts trajectories into readable format for the LLM proposer.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
import dspy
|
||||
|
||||
from prometheus.domain.entities import Prompt, Trajectory
|
||||
from prometheus.domain.ports import ProposerPort
|
||||
from prometheus.infrastructure.dspy_modules import InstructionProposer
|
||||
from prometheus.infrastructure.retry import retry_with_backoff
|
||||
from prometheus.infrastructure.retry import async_retry_with_backoff
|
||||
|
||||
|
||||
class DSPyProposerAdapter(ProposerPort):
|
||||
@@ -28,7 +30,7 @@ class DSPyProposerAdapter(ProposerPort):
|
||||
self._max_retries = max_retries
|
||||
self._retry_delay_base = retry_delay_base
|
||||
|
||||
def propose(
|
||||
async def propose(
|
||||
self,
|
||||
current_prompt: Prompt,
|
||||
trajectories: list[Trajectory],
|
||||
@@ -36,21 +38,26 @@ class DSPyProposerAdapter(ProposerPort):
|
||||
) -> Prompt:
|
||||
failure_examples = self._format_failures(trajectories)
|
||||
|
||||
def _call() -> Prompt:
|
||||
with dspy.context(lm=self._lm):
|
||||
pred = self._proposer(
|
||||
current_instruction=current_prompt.text,
|
||||
task_description=task_description,
|
||||
failure_examples=failure_examples,
|
||||
)
|
||||
return Prompt(text=pred.new_instruction)
|
||||
async def _call() -> Prompt:
|
||||
return await asyncio.to_thread(
|
||||
self._sync_propose, current_prompt, task_description, failure_examples,
|
||||
)
|
||||
|
||||
return retry_with_backoff(
|
||||
return await async_retry_with_backoff(
|
||||
_call,
|
||||
max_retries=self._max_retries,
|
||||
retry_delay_base=self._retry_delay_base,
|
||||
)
|
||||
|
||||
def _sync_propose(self, current_prompt: Prompt, task_description: str, failure_examples: str) -> Prompt:
|
||||
with dspy.context(lm=self._lm):
|
||||
pred = self._proposer(
|
||||
current_instruction=current_prompt.text,
|
||||
task_description=task_description,
|
||||
failure_examples=failure_examples,
|
||||
)
|
||||
return Prompt(text=pred.new_instruction)
|
||||
|
||||
@staticmethod
|
||||
def _format_failures(trajectories: list[Trajectory]) -> str:
|
||||
"""Convert trajectories into a structured textual report."""
|
||||
|
||||
Reference in New Issue
Block a user