feat: add project fork async job service with cli prune/clear commands and shared container image build target

This commit is contained in:
2026-06-09 14:06:02 +00:00
parent c565e3b55c
commit 05c6fa7f3b
90 changed files with 6889 additions and 1146 deletions
+60 -23
View File
@@ -10,7 +10,14 @@ from ..config import Settings
from ..errors import ToolInputError
from .lessons import LessonStore
from .loop import TraceCallback, react_loop
from .state import AgentState, get_state
from .state import (
MAX_EVAL_DEPTH,
AgentState,
get_eval_depth,
get_state,
reset_eval_depth,
set_eval_depth,
)
logger = logging.getLogger("devii.agentic.controller")
@@ -58,6 +65,7 @@ class AgenticController:
"forget_lessons": self._forget,
"verify": self._verify,
"delegate": self._delegate,
"eval": self._eval,
}
handler = handlers.get(name)
if handler is None:
@@ -147,12 +155,53 @@ class AgenticController:
{"status": "success", "verified": bool(confirmed), "summary": summary}, ensure_ascii=False
)
async def _spawn(
self, prompt: str, tools: list[dict[str, Any]], system_prompt: str = SUB_AGENT_SYSTEM_PROMPT
) -> tuple[str, AgentState]:
if self._llm is None or self._dispatcher is None:
raise ToolInputError("Sub-agent execution is not available in this context.")
depth = get_eval_depth()
if depth >= MAX_EVAL_DEPTH:
raise ToolInputError(
"Nested self-evaluation limit reached; a tool or eval cannot keep calling itself."
)
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt},
]
sub_state = AgentState()
token = set_eval_depth(depth + 1)
try:
result = await react_loop(
llm=self._llm,
dispatcher=self._dispatcher,
messages=messages,
tools=tools,
state=sub_state,
settings=self._settings,
max_iterations=self._settings.delegate_max_iterations,
plan_required=self._settings.plan_required,
verify_required=self._settings.verify_required,
on_trace=self._on_trace,
cost_tracker=self._cost_tracker,
chunk_store=self._chunk_store,
)
finally:
reset_eval_depth(token)
return result, sub_state
async def run_subagent(self, prompt: str) -> str:
prompt = str(prompt or "").strip()
if not prompt:
raise ToolInputError("A non-empty prompt is required.")
tools = [tool for tool in self._tools if tool["function"]["name"] != NO_DELEGATE]
result, _ = await self._spawn(prompt, tools)
return result
async def _delegate(self, arguments: dict[str, Any]) -> str:
task = str(arguments.get("task", "")).strip()
if not task:
raise ToolInputError("delegate requires a task description.")
if self._llm is None or self._dispatcher is None:
raise ToolInputError("Delegation is not available in this context.")
allowed = arguments.get("allowed_tools")
if allowed:
allowed_set = {str(name) for name in allowed}
@@ -163,26 +212,7 @@ class AgenticController:
]
else:
tools = [tool for tool in self._tools if tool["function"]["name"] != NO_DELEGATE]
messages = [
{"role": "system", "content": SUB_AGENT_SYSTEM_PROMPT},
{"role": "user", "content": task},
]
sub_state = AgentState()
result = await react_loop(
llm=self._llm,
dispatcher=self._dispatcher,
messages=messages,
tools=tools,
state=sub_state,
settings=self._settings,
max_iterations=self._settings.delegate_max_iterations,
plan_required=self._settings.plan_required,
verify_required=self._settings.verify_required,
on_trace=self._on_trace,
cost_tracker=self._cost_tracker,
chunk_store=self._chunk_store,
)
result, sub_state = await self._spawn(task, tools)
return json.dumps(
{
"status": "success",
@@ -193,3 +223,10 @@ class AgenticController:
},
ensure_ascii=False,
)
async def _eval(self, arguments: dict[str, Any]) -> str:
prompt = str(arguments.get("prompt", "")).strip()
if not prompt:
raise ToolInputError("eval requires a non-empty prompt.")
result = await self.run_subagent(prompt)
return json.dumps({"status": "success", "result": result}, ensure_ascii=False)