Rename the delegate and consultant roles to helper and expert
Architecture L9's role names, for the two roles with a direct counterpart: the delegation role is now 'helper' and the ask_expert role 'expert', in RoleMap, configuration (roles.helper, roles.expert), recorded events and tests. 'planner' and 'executor' stay distinct for now; they become 'driver' when the new loop merges them (Development Plan M2.3). Configurations must rename their keys: nisse-dev.example.yaml and tests/integration/nisse-test.yaml are updated here. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
1 parent
e6aaa4783d
commit
3cf4cefacd
27 files changed
+68
-89
No files matched your search
@@ -25,8 +25,8 @@ llm: # L9: LiteLLM is the only LLM destination
|
||||
roles: # L9: role -> LiteLLM model alias
|
||||
executor: qwen3.8-27b # the local tier: best executor in the benchmark (PR #55)
|
||||
planner: claude-opus-5-5 # high-end judgement: plans a weaker executor follows
|
||||
delegate: claude-opus-5-5 # takes over what the executor got stuck on
|
||||
consultant: claude-haiku-4-5 # one question at a time, no tools: cheap is enough
|
||||
helper: claude-opus-5-5 # takes over what the executor got stuck on
|
||||
expert: claude-haiku-4-5 # one question at a time, no tools: cheap is enough
|
||||
|
||||
matrix: # L5
|
||||
homeserver: https://matrix.example
|
||||
|
||||
@@ -37,7 +37,7 @@ from nisse_dev.worker import SerialWorker
|
||||
# manager allows for a stop, and a hard exit mid-work is already safe.
|
||||
SHUTDOWN_WAIT_S = 10.0
|
||||
|
||||
TOOL_DRIVING_ROLES = ("planner", "executor", "delegate")
|
||||
TOOL_DRIVING_ROLES = ("planner", "executor", "helper")
|
||||
|
||||
|
||||
def discover_models(config: AgentConfig, oplog: OpLog) -> ModelCatalog:
|
||||
@@ -220,7 +220,7 @@ def build_agent(
|
||||
expert = ExpertDesk(
|
||||
normalized, store, config.roles, budget=tuning.expert.consult_budget
|
||||
)
|
||||
delegate_model = config.roles.resolve("delegate")
|
||||
delegate_model = config.roles.resolve("helper")
|
||||
delegation = Delegation(
|
||||
select_client(delegate_model, normalized, passthrough, catalog),
|
||||
store,
|
||||
|
||||
@@ -174,7 +174,7 @@ def run_task(
|
||||
meter = MeteringClient(client, facts)
|
||||
trims_before = getattr(client, "trimmed_requests", 0)
|
||||
model = facts.alias
|
||||
roles = RoleMap(executor=model, planner=model, delegate=model, consultant=model)
|
||||
roles = RoleMap(executor=model, planner=model, helper=model, expert=model)
|
||||
executor = LLMExecutor(
|
||||
meter,
|
||||
store,
|
||||
|
||||
@@ -36,8 +36,8 @@ REQUIRED_KEYS = (
|
||||
"llm.token",
|
||||
"roles.executor",
|
||||
"roles.planner",
|
||||
"roles.delegate",
|
||||
"roles.consultant",
|
||||
"roles.helper",
|
||||
"roles.expert",
|
||||
"matrix.homeserver",
|
||||
"matrix.user_id",
|
||||
"matrix.token",
|
||||
@@ -312,8 +312,8 @@ class AgentConfig:
|
||||
roles=RoleMap(
|
||||
executor=str(values["roles.executor"]),
|
||||
planner=str(values["roles.planner"]),
|
||||
delegate=str(values["roles.delegate"]),
|
||||
consultant=str(values["roles.consultant"]),
|
||||
helper=str(values["roles.helper"]),
|
||||
expert=str(values["roles.expert"]),
|
||||
),
|
||||
matrix=MatrixConfig(
|
||||
homeserver=str(values["matrix.homeserver"]).rstrip("/"),
|
||||
@@ -371,5 +371,5 @@ class AgentConfig:
|
||||
"repo": self.gitea_repo,
|
||||
"planner": self.roles.planner,
|
||||
"executor": self.roles.executor,
|
||||
"delegate": self.roles.delegate,
|
||||
"helper": self.roles.helper,
|
||||
}
|
||||
@@ -184,9 +184,7 @@ class Delegation:
|
||||
# a reason, and the next step is to talk about that reason.
|
||||
self._cancel = cancel or Cancel()
|
||||
|
||||
def run(
|
||||
self, *, brief: str, branch: str, role: str = "delegate"
|
||||
) -> DelegationResult:
|
||||
def run(self, *, brief: str, branch: str, role: str = "helper") -> DelegationResult:
|
||||
started_at = self._now()
|
||||
base_branch = self._git.current_branch()
|
||||
# Recorded before the branch exists, so a crash anywhere after this
|
||||
|
||||
@@ -165,7 +165,7 @@ class LLMExecutor:
|
||||
# that it is circling and should get on with the work or fail.
|
||||
return ToolOutcome(str(exc), success=False)
|
||||
except LLMError as exc:
|
||||
return ToolOutcome(f"the consultant did not answer: {exc}", success=False)
|
||||
return ToolOutcome(f"the expert did not answer: {exc}", success=False)
|
||||
|
||||
def _scope(self) -> tuple[str | None, int]:
|
||||
"""The approved plan, and where this milestone's events begin.
|
||||
|
||||
@@ -17,7 +17,7 @@ memory: it survives a restart, and it is observable from outside the model,
|
||||
which is the same reasoning L9 applies to escalation triggers.
|
||||
|
||||
Consults are recorded as ``delegation_started`` / ``delegation_completed`` with
|
||||
role ``consultant``. That is deliberate — L9 wants a "consult/delegation eval
|
||||
role ``expert``. That is deliberate — L9 wants a "consult/delegation eval
|
||||
set", and reusing the composite events gets consult evaluation for free rather
|
||||
than inventing a parallel record.
|
||||
"""
|
||||
@@ -35,7 +35,7 @@ from nisse_dev.canonical import (
|
||||
from nisse_dev.llm import LLMClient, LLMError, RoleMap
|
||||
from nisse_dev.state import StateStore
|
||||
|
||||
CONSULTANT_ROLE = "consultant"
|
||||
EXPERT_ROLE = "expert"
|
||||
DEFAULT_BUDGET = 3
|
||||
|
||||
SYSTEM_PROMPT = """You are a consultant answering one self-contained question.
|
||||
@@ -111,8 +111,7 @@ class ExpertDesk:
|
||||
return sum(
|
||||
1
|
||||
for e in self._store.read_events(after=since_event_id)
|
||||
if isinstance(e.event, DelegationStarted)
|
||||
and e.event.role == CONSULTANT_ROLE
|
||||
if isinstance(e.event, DelegationStarted) and e.event.role == EXPERT_ROLE
|
||||
)
|
||||
|
||||
def ask(self, question: str, *, since_event_id: int = 0) -> ExpertAnswer:
|
||||
@@ -122,11 +121,11 @@ class ExpertDesk:
|
||||
f"consult budget of {self._budget} is spent for this task "
|
||||
f"({used} used); escalate or ask the operator instead"
|
||||
)
|
||||
model = self._roles.resolve(CONSULTANT_ROLE)
|
||||
model = self._roles.resolve(EXPERT_ROLE)
|
||||
self._store.append_event(
|
||||
DelegationStarted(
|
||||
brief=question,
|
||||
role=CONSULTANT_ROLE,
|
||||
role=EXPERT_ROLE,
|
||||
model=model,
|
||||
caps={"consults": self._budget, "used": used},
|
||||
)
|
||||
|
||||
@@ -109,10 +109,10 @@ class LLMClient(Protocol):
|
||||
class RoleMap:
|
||||
executor: str
|
||||
planner: str
|
||||
delegate: str
|
||||
consultant: str
|
||||
helper: str
|
||||
expert: str
|
||||
|
||||
ROLES: ClassVar[tuple[str, ...]] = ("executor", "planner", "delegate", "consultant")
|
||||
ROLES: ClassVar[tuple[str, ...]] = ("executor", "planner", "helper", "expert")
|
||||
|
||||
def resolve(self, role: str) -> str:
|
||||
if role not in self.ROLES:
|
||||
|
||||
@@ -263,7 +263,7 @@ def test_a_tool_less_model_warns_but_still_boots(tmp_path: Path, monkeypatch) ->
|
||||
logged = stream.getvalue()
|
||||
assert "model_tools_unsupported" in logged
|
||||
assert '"role": "executor"' in logged
|
||||
assert '"role": "consultant"' not in logged # ask_expert drives no tools
|
||||
assert '"role": "expert"' not in logged # ask_expert drives no tools
|
||||
|
||||
|
||||
def test_the_resolved_model_is_pinned_at_task_start(
|
||||
|
||||
@@ -20,7 +20,7 @@ from nisse_dev.planner import LLMPlanningAgent
|
||||
from nisse_dev.state import StateStore
|
||||
from nisse_dev.transport import InMemoryTransport
|
||||
|
||||
ROLES = RoleMap(executor="x", planner="y", delegate="y", consultant="y")
|
||||
ROLES = RoleMap(executor="x", planner="y", helper="y", expert="y")
|
||||
|
||||
|
||||
class FakeGitea:
|
||||
|
||||
@@ -27,7 +27,7 @@ ALL_EVENTS = [
|
||||
GateOpened(gate="arch", reason="plan submitted (rev-1)"),
|
||||
DelegationStarted(
|
||||
brief="fix the flaky test",
|
||||
role="delegate",
|
||||
role="helper",
|
||||
model="claude-opus-5",
|
||||
caps={"turns": 20, "tokens": 100000},
|
||||
),
|
||||
|
||||
@@ -130,13 +130,13 @@ def test_validate_names_every_bad_role_at_once() -> None:
|
||||
{
|
||||
"executor": "local-fast",
|
||||
"planner": "clause-opus-5",
|
||||
"delegate": "remote-architecture",
|
||||
"consultant": "local-fast",
|
||||
"helper": "remote-architecture",
|
||||
"expert": "local-fast",
|
||||
}
|
||||
)
|
||||
message = str(caught.value)
|
||||
assert "planner='clause-opus-5'" in message
|
||||
assert "delegate='remote-architecture'" in message
|
||||
assert "helper='remote-architecture'" in message
|
||||
assert "executor" not in message # the good ones are not noise
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ COMPLETE: dict[str, Any] = {
|
||||
"roles": {
|
||||
"executor": "claude-haiku-4-5",
|
||||
"planner": "claude-opus-5",
|
||||
"delegate": "claude-opus-5",
|
||||
"consultant": "claude-haiku-4-5",
|
||||
"helper": "claude-opus-5",
|
||||
"expert": "claude-haiku-4-5",
|
||||
},
|
||||
"matrix": {
|
||||
"homeserver": "https://matrix.test/",
|
||||
@@ -210,8 +210,8 @@ llm: {base_url: "https://litellm.test", token: "${TEST_LLM_TOKEN}"}
|
||||
roles:
|
||||
executor: local-fast
|
||||
planner: claude-opus-5
|
||||
delegate: claude-opus-5
|
||||
consultant: claude-haiku-4-5
|
||||
helper: claude-opus-5
|
||||
expert: claude-haiku-4-5
|
||||
matrix:
|
||||
homeserver: https://matrix.test
|
||||
user_id: "@nisse-dev-testing:test"
|
||||
|
||||
@@ -262,7 +262,7 @@ def test_the_executor_sees_only_its_milestone(tmp_path: Path) -> None:
|
||||
seen["system"] = system
|
||||
return [AssistantText(text="done", model=model)]
|
||||
|
||||
roles = RoleMap(executor="m", planner="m", delegate="m", consultant="m")
|
||||
roles = RoleMap(executor="m", planner="m", helper="m", expert="m")
|
||||
LLMExecutor(Client(), store, roles, Workspace()).run(1)
|
||||
|
||||
texts = [getattr(e, "text", None) for e in seen["events"]]
|
||||
@@ -286,7 +286,7 @@ def test_a_request_that_cannot_fit_stops_the_milestone(tmp_path: Path) -> None:
|
||||
|
||||
inner = RecordingClient()
|
||||
client = BudgetedClient(inner, lambda m: (4096, 1024))
|
||||
roles = RoleMap(executor="m", planner="m", delegate="m", consultant="m")
|
||||
roles = RoleMap(executor="m", planner="m", helper="m", expert="m")
|
||||
outcome = LLMExecutor(client, store, roles, Workspace()).run(1)
|
||||
|
||||
assert outcome.status == "escalate"
|
||||
|
||||
@@ -385,9 +385,7 @@ def test_clean_cut_after_a_tool_result_is_resumable(
|
||||
repo.checkout_new_branch("d")
|
||||
(repo.root / "partial.py").write_text("x")
|
||||
repo.commit_all("partial work")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(ToolCall(call_id="c", tool="write_file", arguments={}))
|
||||
store.append_event(ToolResult(call_id="c", result="wrote", success=True))
|
||||
intent = pending_delegation_intent(store)
|
||||
@@ -400,9 +398,7 @@ def test_dangling_tool_call_gates(
|
||||
) -> None:
|
||||
# The effect of the call is unknown and not verifiable by inspection.
|
||||
repo.checkout_new_branch("d")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(ToolCall(call_id="c", tool="run_command", arguments={}))
|
||||
intent = pending_delegation_intent(store)
|
||||
|
||||
@@ -412,9 +408,7 @@ def test_dangling_tool_call_gates(
|
||||
def test_missing_branch_means_nothing_started(
|
||||
store: StateStore, ws: Workspace, repo: GitRepo
|
||||
) -> None:
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(ToolResult(call_id="c", result="ok", success=True))
|
||||
intent = pending_delegation_intent(store, branch="never-created")
|
||||
|
||||
@@ -425,9 +419,7 @@ def test_a_finished_delegation_settles_rather_than_resumes(
|
||||
store: StateStore, ws: Workspace, repo: GitRepo
|
||||
) -> None:
|
||||
repo.checkout_new_branch("d")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(
|
||||
DelegationCompleted(
|
||||
handback="done",
|
||||
@@ -448,9 +440,7 @@ def test_resume_continues_the_same_delegation(
|
||||
# Interrupt after one turn, then resume: no second delegation_started,
|
||||
# because L7 defines a session as the delegation, not the process.
|
||||
repo.checkout_new_branch("d")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(ToolResult(call_id="c", result="wrote a.py", success=True))
|
||||
intent = pending_delegation_intent(store)
|
||||
|
||||
@@ -484,9 +474,7 @@ def test_recover_resumes_a_clean_cut_with_a_notice(
|
||||
store: StateStore, ws: Workspace, repo: GitRepo
|
||||
) -> None:
|
||||
repo.checkout_new_branch("d")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(ToolResult(call_id="c", result="wrote a.py", success=True))
|
||||
pending_delegation_intent(store)
|
||||
notices: list[str] = []
|
||||
@@ -505,9 +493,7 @@ def test_recover_settles_a_finished_delegation_without_resuming(
|
||||
store: StateStore, ws: Workspace, repo: GitRepo
|
||||
) -> None:
|
||||
repo.checkout_new_branch("d")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(
|
||||
DelegationCompleted(
|
||||
handback="done",
|
||||
@@ -539,9 +525,7 @@ def test_recover_leaves_an_unclean_cut_for_the_boot_gate(
|
||||
store: StateStore, ws: Workspace, repo: GitRepo
|
||||
) -> None:
|
||||
repo.checkout_new_branch("d")
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(ToolCall(call_id="c", tool="run_command", arguments={}))
|
||||
pending_delegation_intent(store)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ def record_pair(
|
||||
*,
|
||||
brief: str = "add greet",
|
||||
model: str = "claude-haiku-4-5",
|
||||
role: str = "delegate",
|
||||
role: str = "helper",
|
||||
status: str = "handback",
|
||||
) -> int:
|
||||
store.append_event(
|
||||
@@ -56,15 +56,13 @@ def test_unpaired_start_is_skipped(store: StateStore) -> None:
|
||||
# A crash mid-delegation leaves a start with no completion; a half-formed
|
||||
# record would pollute the set L9 exists to make trustworthy.
|
||||
store.append_event(
|
||||
DelegationStarted(brief="died", role="delegate", model="m", caps={})
|
||||
DelegationStarted(brief="died", role="helper", model="m", caps={})
|
||||
)
|
||||
assert collect(store) == []
|
||||
|
||||
|
||||
def test_unrelated_events_between_the_pair_are_ignored(store: StateStore) -> None:
|
||||
store.append_event(
|
||||
DelegationStarted(brief="b", role="delegate", model="m", caps={})
|
||||
)
|
||||
store.append_event(DelegationStarted(brief="b", role="helper", model="m", caps={}))
|
||||
store.append_event(AssistantText(text="working", model="m"))
|
||||
store.append_event(
|
||||
DelegationCompleted(
|
||||
@@ -97,10 +95,10 @@ def test_a_verdict_can_be_revised(store: StateStore) -> None:
|
||||
|
||||
|
||||
def test_consults_and_delegations_share_the_set(store: StateStore) -> None:
|
||||
record_pair(store, role="delegate")
|
||||
record_pair(store, role="consultant", brief="which approach?")
|
||||
record_pair(store, role="helper")
|
||||
record_pair(store, role="expert", brief="which approach?")
|
||||
roles = [r.role for r in collect(store)]
|
||||
assert roles == ["delegate", "consultant"]
|
||||
assert roles == ["helper", "expert"]
|
||||
|
||||
|
||||
def test_collect_can_start_after_a_marker(store: StateStore) -> None:
|
||||
|
||||
@@ -20,8 +20,8 @@ from nisse_dev.tools import Workspace
|
||||
ROLES = RoleMap(
|
||||
executor="llama3.2",
|
||||
planner="claude-haiku-4-5",
|
||||
delegate="claude-haiku-4-5",
|
||||
consultant="claude-haiku-4-5",
|
||||
helper="claude-haiku-4-5",
|
||||
expert="claude-haiku-4-5",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ from nisse_dev.state import StateStore
|
||||
ROLES = RoleMap(
|
||||
executor="llama3.2",
|
||||
planner="haiku",
|
||||
delegate="haiku",
|
||||
consultant="claude-haiku-4-5",
|
||||
helper="haiku",
|
||||
expert="claude-haiku-4-5",
|
||||
)
|
||||
|
||||
GOOD = '{"answer": "Use SQLite; it is enough here.", "confidence": "high", "caveats": "Assumes one writer."}'
|
||||
@@ -137,7 +137,7 @@ def test_consults_are_recorded_as_composite_events(store: StateStore) -> None:
|
||||
events = [e.event for e in store.read_events()]
|
||||
started = next(e for e in events if isinstance(e, DelegationStarted))
|
||||
completed = next(e for e in events if isinstance(e, DelegationCompleted))
|
||||
assert started.role == "consultant"
|
||||
assert started.role == "expert"
|
||||
assert started.brief == "q"
|
||||
assert completed.exit_status == "answered"
|
||||
|
||||
|
||||
@@ -184,8 +184,8 @@ def test_malformed_response_raises_llm_error() -> None:
|
||||
ROLES = RoleMap(
|
||||
executor="local/qwen",
|
||||
planner="anthropic/claude-opus-5",
|
||||
delegate="anthropic/claude-opus-5",
|
||||
consultant="anthropic/claude-opus-5",
|
||||
helper="anthropic/claude-opus-5",
|
||||
expert="anthropic/claude-opus-5",
|
||||
)
|
||||
|
||||
|
||||
@@ -195,8 +195,8 @@ def test_role_map_resolves_and_pins() -> None:
|
||||
assert roles.pinned() == {
|
||||
"executor": "local/qwen",
|
||||
"planner": "anthropic/claude-opus-5",
|
||||
"delegate": "anthropic/claude-opus-5",
|
||||
"consultant": "anthropic/claude-opus-5",
|
||||
"helper": "anthropic/claude-opus-5",
|
||||
"expert": "anthropic/claude-opus-5",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ from nisse_dev.transport import InMemoryTransport
|
||||
ROLES = RoleMap(
|
||||
executor="local/qwen",
|
||||
planner="anthropic/claude-opus-5",
|
||||
delegate="anthropic/claude-opus-5",
|
||||
consultant="anthropic/claude-opus-5",
|
||||
helper="anthropic/claude-opus-5",
|
||||
expert="anthropic/claude-opus-5",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ from nisse_dev.transport import InMemoryTransport
|
||||
ROLES = RoleMap(
|
||||
executor="llama3.2",
|
||||
planner="haiku",
|
||||
delegate="haiku",
|
||||
consultant="haiku",
|
||||
helper="haiku",
|
||||
expert="haiku",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from nisse_dev.publish import PublishResult
|
||||
from nisse_dev.state import StateStore
|
||||
from nisse_dev.transport import InMemoryTransport
|
||||
|
||||
ROLES = RoleMap(executor="x", planner="y", delegate="y", consultant="y")
|
||||
ROLES = RoleMap(executor="x", planner="y", helper="y", expert="y")
|
||||
|
||||
|
||||
class ScriptedClient:
|
||||
|
||||
@@ -31,8 +31,8 @@ from nisse_dev.worker import SerialWorker
|
||||
ROLES = RoleMap(
|
||||
executor="llama3.2",
|
||||
planner="claude-haiku-4-5",
|
||||
delegate="claude-haiku-4-5",
|
||||
consultant="claude-haiku-4-5",
|
||||
helper="claude-haiku-4-5",
|
||||
expert="claude-haiku-4-5",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ llm:
|
||||
roles:
|
||||
executor: claude-haiku-4-5
|
||||
planner: claude-haiku-4-5
|
||||
delegate: claude-haiku-4-5
|
||||
consultant: claude-haiku-4-5
|
||||
helper: claude-haiku-4-5
|
||||
expert: claude-haiku-4-5
|
||||
|
||||
matrix:
|
||||
homeserver: https://matrix.dongar.net
|
||||
|
||||
@@ -22,7 +22,7 @@ pytestmark = pytest.mark.integration
|
||||
def desk(tmp_path: Path, litellm: LiteLLMClient, live_model: str):
|
||||
def make(budget: int = 2) -> tuple[ExpertDesk, StateStore]:
|
||||
model = live_model
|
||||
roles = RoleMap(executor=model, planner=model, delegate=model, consultant=model)
|
||||
roles = RoleMap(executor=model, planner=model, helper=model, expert=model)
|
||||
store = StateStore(
|
||||
tmp_path / "state.db", oplog=OpLog("nisse-dev", io.StringIO())
|
||||
)
|
||||
@@ -43,7 +43,7 @@ def test_live_consult_parses_against_the_local_schema(desk) -> None:
|
||||
assert len(answer.answer) > 20
|
||||
# The consult is captured for L9 without any extra bookkeeping.
|
||||
[record] = collect(store)
|
||||
assert record.role == "consultant"
|
||||
assert record.role == "expert"
|
||||
assert record.exit_status == "answered"
|
||||
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ def test_plan_approve_execute_produces_a_real_file(
|
||||
# Both roles use the capable model here: the point is the whole gated
|
||||
# loop over a real proxy, not local-model competence (measured separately).
|
||||
model = live_model
|
||||
roles = RoleMap(executor=model, planner=model, delegate=model, consultant=model)
|
||||
roles = RoleMap(executor=model, planner=model, helper=model, expert=model)
|
||||
client = litellm
|
||||
store = StateStore(tmp_path / "state.db", oplog=OpLog("nisse-dev", io.StringIO()))
|
||||
workspace = Workspace(tmp_path / "work", timeout_s=60)
|
||||
|
||||
@@ -25,7 +25,7 @@ def test_live_planning_turn_returns_a_tool_action(
|
||||
tmp_path: Path, litellm: LiteLLMClient, live_model: str
|
||||
) -> None:
|
||||
model = live_model
|
||||
roles = RoleMap(executor=model, planner=model, delegate=model, consultant=model)
|
||||
roles = RoleMap(executor=model, planner=model, helper=model, expert=model)
|
||||
store = StateStore(tmp_path / "state.db", oplog=OpLog("nisse-dev", io.StringIO()))
|
||||
store.append_event(
|
||||
UserMessage(
|
||||
|
||||
Reference in new issue
Block a user