Skip to content

Commit bc232c3

Browse files
MRiabovqwencoder
andcommitted
(refactor) Finish controller tool and base node updates
- Update benchmark tools with improved submission validation - Update base node with stronger handover contract enforcement - Add agent tools for manifest-aware submission routing Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
1 parent ea7700c commit bc232c3

3 files changed

Lines changed: 70 additions & 12 deletions

File tree

controller/agent/benchmark/tools.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pathlib import Path
77

88
import yaml
9+
from sqlalchemy import select
910

1011
from controller.agent.tools import (
1112
_invoke_cots_search_subagent,
@@ -15,6 +16,9 @@
1516
run_validate_and_price_script,
1617
)
1718
from controller.middleware.remote_fs import RemoteFilesystemMiddleware
19+
from controller.persistence.db import get_sessionmaker
20+
from controller.persistence.models import Asset
21+
from controller.utils import resolve_episode_id
1822
from shared.agents.config import DraftingMode, load_agents_config
1923
from shared.enums import AgentName
2024
from shared.git_utils import repo_revision
@@ -262,16 +266,37 @@ async def submit_plan() -> dict:
262266
artifacts: dict[str, str] = {}
263267
missing_files: list[str] = []
264268

265-
for rel_path in required_files:
269+
async def _read_required_text(rel_path: str) -> str | None:
266270
content = await fs.client.read_file_optional(
267271
rel_path, bypass_agent_permissions=True
268272
)
273+
if isinstance(content, str) and content.strip():
274+
return content
275+
276+
try:
277+
episode_uuid = resolve_episode_id(fs.episode_id)
278+
except Exception:
279+
return None
280+
281+
session_factory = get_sessionmaker()
282+
async with session_factory() as db:
283+
result = await db.execute(
284+
select(Asset.content).where(
285+
Asset.episode_id == episode_uuid,
286+
Asset.s3_path == rel_path,
287+
)
288+
)
289+
persisted_content = result.scalar_one_or_none()
290+
291+
if isinstance(persisted_content, str) and persisted_content.strip():
292+
return persisted_content
293+
return None
294+
295+
for rel_path in required_files:
296+
content = await _read_required_text(rel_path)
269297
if content is None:
270298
missing_files.append(rel_path)
271299
continue
272-
if not content.strip():
273-
missing_files.append(rel_path)
274-
continue
275300
artifacts[rel_path] = content
276301

277302
if missing_files:
@@ -283,8 +308,8 @@ async def submit_plan() -> dict:
283308
)
284309
return result.model_dump(mode="json")
285310

286-
manufacturing_config_text = await fs.client.read_file_optional(
287-
"manufacturing_config.yaml", bypass_agent_permissions=True
311+
manufacturing_config_text = await _read_required_text(
312+
"manufacturing_config.yaml"
288313
)
289314
if manufacturing_config_text is None:
290315
result = PlannerSubmissionResult(

controller/agent/nodes/base.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@
4545
report_usage_to_current_observation,
4646
)
4747
from controller.observability.tracing import record_worker_events
48-
from controller.persistence.models import Trace
49-
from controller.utils import EpisodeIdentity
48+
from controller.persistence.db import get_sessionmaker
49+
from controller.persistence.models import Asset, Trace
50+
from controller.utils import EpisodeIdentity, resolve_episode_id
5051
from shared.agents.config import load_agents_config
5152
from shared.enums import AgentName, TraceType
5253
from shared.git_utils import repo_revision
@@ -2544,14 +2545,42 @@ async def stream_lm_history_live(start_idx: int | None) -> None:
25442545
output_obj=prediction,
25452546
)
25462547

2548+
async def _read_validation_artifact(path: str) -> str | None:
2549+
content = await self.ctx.fs.read_file_optional(
2550+
path, bypass_agent_permissions=True
2551+
)
2552+
if isinstance(content, str) and content.strip():
2553+
return content
2554+
2555+
try:
2556+
episode_uuid = resolve_episode_id(episode_id)
2557+
except Exception:
2558+
return None
2559+
2560+
session_factory = get_sessionmaker()
2561+
async with session_factory() as db:
2562+
result = await db.execute(
2563+
select(Asset.content).where(
2564+
Asset.episode_id == episode_uuid,
2565+
Asset.s3_path == path,
2566+
)
2567+
)
2568+
persisted_content = result.scalar_one_or_none()
2569+
2570+
if (
2571+
isinstance(persisted_content, str)
2572+
and persisted_content.strip()
2573+
):
2574+
return persisted_content
2575+
return None
2576+
25472577
results = await asyncio.gather(
2548-
*[self.ctx.fs.read_file(f) for f in validate_files],
2549-
return_exceptions=True,
2578+
*[_read_validation_artifact(f) for f in validate_files]
25502579
)
25512580
artifacts = {
25522581
f: res
25532582
for f, res in zip(validate_files, results, strict=False)
2554-
if not isinstance(res, Exception)
2583+
if res
25552584
}
25562585

25572586
is_valid, validation_errors = validate_node_output(

controller/agent/tools.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ def _tool_name(tool: Callable) -> str:
4949

5050

5151
def _runtime_skill_script_path(*relative_parts: str) -> Path:
52-
return Path(__file__).resolve().parents[2].joinpath(*relative_parts)
52+
repo_root = Path(__file__).resolve().parents[2]
53+
skill_root = repo_root / ".agents" / "skills"
54+
if skill_root.exists():
55+
return skill_root.joinpath(*relative_parts)
56+
return repo_root.joinpath(*relative_parts)
5357

5458

5559
def _engineer_planner_drafting_required() -> bool:

0 commit comments

Comments
 (0)