Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 68 additions & 13 deletions verifiers/v1/harnesses/rlm/harness.py
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
import shlex
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, PositiveInt, model_validator
from pydantic import (
BaseModel,
ConfigDict,
Field,
NonNegativeInt,
PositiveInt,
model_validator,
)
from pydantic_config import BaseConfig

from verifiers.v1.acp import ACPConfig, ACPHarness, ACPTurn, JsonObject
Expand All @@ -17,7 +24,7 @@

logger = logging.getLogger(__name__)

BuiltinSkill = Literal["edit", "search"]
BuiltinSkill = Literal["bash", "edit", "fetch", "search"]

RLM_REPO = "github.com/PrimeIntellect-ai/nano-rlm.git"
RLM_CACHE_DIR = "/tmp/vf-rlm"
Expand All @@ -43,15 +50,36 @@ class CompactionConfig(BaseConfig):


class RLMHarnessConfig(HarnessConfig):
version: str = Field(default="4ef3438", min_length=1)
"""Git ref (branch, tag, or commit) of nano-rlm to install."""
max_depth: int = 0
"""Recursion depth RLM may spawn sub-harnesses to."""
version: str = Field(default="240090d", min_length=1)
"""Git ref (branch, tag, or commit) of nano-rlm to install. Must know every
field this harness puts on the wire, i.e. be at least the default ref."""
max_depth: NonNegativeInt | None = None
"""Recursion depth RLM may spawn sub-agents to; `None` = nano-rlm's default (1).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we myb not spell out the actual default value so that this cannot diverge?

Set 0 to disable recursion."""
builtin_skills: list[BuiltinSkill] = Field(default_factory=list)
"""Built-in rlm skills to enable (RLM_SKILLS), e.g. `["edit"]`; empty enables none.
The tool set is fixed (ipython); the base `skills` field takes SKILL.md paths."""
compaction: CompactionConfig | None = None
"""Context compaction policy. Set an empty config to use automatic thresholds."""
max_concurrent_subagents: PositiveInt | None = None
"""Sub-agents running at once per session tree; `None` = nano-rlm's default (4).
nano-rlm requires it to be at least `max_depth`."""
max_total_turns: PositiveInt | None = None
"""Tree-total turn budget (one turn = one work-loop model call, any engine); every
engine stops before its next call once spent. `None` = uncapped."""
max_total_tokens: PositiveInt | None = None
"""Tree-total budget of NEW tokens (completion + uncached prompt) across the session
tree; once spent every engine stops and no further sub-agents spawn. `None` = unbounded."""
max_tool_output_bytes: PositiveInt | None = None
"""Byte budget for a single tool result entering the conversation (middle truncation);
overrides rlm's built-in 20KB default in either direction."""
append_to_system_prompt: str | None = None
"""Appended to the root engine's system prompt, after the taskset's system prompt."""
subagent_append_to_system_prompt: str | None = None
"""Append for sub-agent engines that can still delegate (depth >= 1, below
`max_depth`); unset falls back to `append_to_system_prompt`."""
leaf_append_to_system_prompt: str | None = None
"""Append for depth-capped leaf engines; unset falls back to the sub-agent append."""

@model_validator(mode="after")
def reject_disabled_tools(self) -> "RLMHarnessConfig":
Expand All @@ -60,6 +88,15 @@ def reject_disabled_tools(self) -> "RLMHarnessConfig":
"the rlm harness has a fixed tool set (ipython) and does not support "
"`disabled_tools`; use `builtin_skills` to enable built-in skills instead."
)
if (
self.max_depth is not None
and self.max_concurrent_subagents is not None
and self.max_concurrent_subagents < self.max_depth
):
raise ValueError(
"`max_concurrent_subagents` must be at least `max_depth` "
"(nano-rlm rejects the policy otherwise)."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrent default undershoots max depth

Medium Severity

When max_concurrent_subagents is left unset, the policy omits it and nano-rlm applies a fixed default of 4. That can be below max_depth, which nano-rlm rejects. The new validator only compares the two knobs when both are set, so this config is accepted and then fails at session start.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a201007. Configure here.

return self


Expand Down Expand Up @@ -109,23 +146,41 @@ def _runtime_metadata(
system_prompt: str | None,
) -> JsonObject:
compaction = self.config.compaction
appends = [
text
for text in (system_prompt, self.config.append_to_system_prompt)
if text
]
payload = {
"session_id": trace.id,
"model": ctx.model,
"provider": {
"base_url": endpoint,
"api_key": secret,
},
# None = passthrough: the key stays off the wire and nano-rlm's own
# default applies.
"policy": {
"max_depth": self.config.max_depth,
"compaction": compaction is not None,
"summarize_at_tokens": (
compaction.summarize_at_tokens if compaction else None
),
"max_concurrent_subagents": max(4, self.config.max_depth),
key: value
for key, value in {
"max_depth": self.config.max_depth,
"compaction": compaction is not None,
"summarize_at_tokens": (
compaction.summarize_at_tokens if compaction else None
),
"max_concurrent_subagents": self.config.max_concurrent_subagents,
"max_total_turns": self.config.max_total_turns,
"max_total_tokens": self.config.max_total_tokens,
"max_tool_output_bytes": self.config.max_tool_output_bytes,
}.items()
if value is not None
},
"system_prompt_path": None,
"append_to_system_prompt": system_prompt,
"append_to_system_prompt": "\n\n".join(appends) or None,
"subagent_append_to_system_prompt": (
self.config.subagent_append_to_system_prompt
),
"leaf_append_to_system_prompt": self.config.leaf_append_to_system_prompt,
Comment thread
cursor[bot] marked this conversation as resolved.
"skills": list(self.config.builtin_skills),
"kernel_env": runtime.env,
"search_api_key": self.config.resolved_env.get("SERPER_API_KEY"),
Expand Down