Skip to content
Closed
Show file tree
Hide file tree
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
9 changes: 9 additions & 0 deletions cli-config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,15 @@ prompt_caching:
# # extra_body:
# # enable_thinking: false

# Background memory/skill review uses the active conversation runtime by default.
# Set this to route those non-user-visible review turns to a cheaper or isolated
# endpoint.
#
# background_review:
# provider: "" # empty/unset = inherit active conversation provider
# model: "" # empty/unset = inherit active conversation model
# max_iterations: 8

# =============================================================================
# Persistent Memory
# =============================================================================
Expand Down
26 changes: 23 additions & 3 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,26 @@ def __init__(
# broad pseudo-public config object on the agent instance.
self._aux_compression_context_length_config = None

self._background_review_model = None
self._background_review_provider = None
self._background_review_max_iterations = 8
try:
_bg_review_config = _agent_cfg.get("background_review", {})
if not isinstance(_bg_review_config, dict):
_bg_review_config = {}
_bg_review_model = _bg_review_config.get("model")
_bg_review_provider = _bg_review_config.get("provider")
if _bg_review_model:
self._background_review_model = str(_bg_review_model).strip()
if _bg_review_provider:
self._background_review_provider = str(_bg_review_provider).strip()
self._background_review_max_iterations = max(
1,
int(_bg_review_config.get("max_iterations", 8)),
)
except Exception:
pass

# Persistent memory (MEMORY.md + USER.md) -- loaded from disk
self._memory_store = None
self._memory_enabled = False
Expand Down Expand Up @@ -3226,11 +3246,11 @@ def _run_review():
contextlib.redirect_stdout(_devnull), \
contextlib.redirect_stderr(_devnull):
review_agent = AIAgent(
model=self.model,
max_iterations=8,
model=self._background_review_model or self.model,
max_iterations=self._background_review_max_iterations,
quiet_mode=True,
platform=self.platform,
provider=self.provider,
provider=self._background_review_provider or self.provider,
parent_session_id=self.session_id,
)
review_agent._memory_write_origin = "background_review"
Expand Down
65 changes: 65 additions & 0 deletions tests/run_agent/test_background_review_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"""

import json
import threading
from unittest.mock import patch

import run_agent
from run_agent import AIAgent


Expand Down Expand Up @@ -128,3 +131,65 @@ def test_removed_or_replaced_relabels_by_target():

assert "User profile updated" in actions
assert "Memory updated" in actions


def test_spawn_background_review_uses_configured_runtime(monkeypatch):
"""Background review agents can be routed away from the active model."""
with (
patch("run_agent.get_tool_definitions", return_value=[]),
patch("run_agent.check_toolset_requirements", return_value={}),
patch("run_agent.OpenAI"),
patch(
"hermes_cli.config.load_config",
return_value={
"background_review": {
"model": "qwen3.6-35b-q8",
"provider": "artemis-direct",
"max_iterations": 4,
}
},
),
):
agent = AIAgent(
model="qwen3.6-27b",
provider="voyager",
api_key="test-key",
base_url="http://localhost:8080/v1",
quiet_mode=True,
skip_context_files=True,
skip_memory=True,
)

captured = {}

class FakeReviewAgent:
def __init__(self, **kwargs):
captured.update(kwargs)
self._session_messages = []

def run_conversation(self, **kwargs):
captured["conversation"] = kwargs

def close(self):
captured["closed"] = True

class ImmediateThread:
def __init__(self, target, daemon, name):
self._target = target
captured["thread"] = {"daemon": daemon, "name": name}

def start(self):
self._target()

monkeypatch.setattr(run_agent, "AIAgent", FakeReviewAgent)
monkeypatch.setattr(threading, "Thread", ImmediateThread)

snapshot = [{"role": "user", "content": "hello"}]
agent._spawn_background_review(snapshot, review_skills=True)

assert captured["model"] == "qwen3.6-35b-q8"
assert captured["provider"] == "artemis-direct"
assert captured["max_iterations"] == 4
assert captured["parent_session_id"] == agent.session_id
assert captured["conversation"]["conversation_history"] == snapshot
assert captured["closed"] is True