Merge pull request #1398 from NousResearch/hermes/hermes-1b6f4583
fix(cron): support per-job runtime overrides
This commit is contained in:
commit
5b11570517
6 changed files with 173 additions and 9 deletions
|
|
@ -309,6 +309,57 @@ class TestRunJobConfigLogging:
|
|||
f"Expected 'failed to parse prefill messages' warning in logs, got: {[r.message for r in caplog.records]}"
|
||||
|
||||
|
||||
class TestRunJobPerJobOverrides:
|
||||
def test_job_level_model_provider_and_base_url_overrides_are_used(self, tmp_path):
|
||||
config_yaml = tmp_path / "config.yaml"
|
||||
config_yaml.write_text(
|
||||
"model:\n"
|
||||
" default: gpt-5.4\n"
|
||||
" provider: openai-codex\n"
|
||||
" base_url: https://chatgpt.com/backend-api/codex\n"
|
||||
)
|
||||
|
||||
job = {
|
||||
"id": "briefing-job",
|
||||
"name": "briefing",
|
||||
"prompt": "hello",
|
||||
"model": "perplexity/sonar-pro",
|
||||
"provider": "custom",
|
||||
"base_url": "http://127.0.0.1:4000/v1",
|
||||
}
|
||||
|
||||
fake_db = MagicMock()
|
||||
fake_runtime = {
|
||||
"provider": "openrouter",
|
||||
"api_mode": "chat_completions",
|
||||
"base_url": "http://127.0.0.1:4000/v1",
|
||||
"api_key": "***",
|
||||
}
|
||||
|
||||
with patch("cron.scheduler._hermes_home", tmp_path), \
|
||||
patch("cron.scheduler._resolve_origin", return_value=None), \
|
||||
patch("dotenv.load_dotenv"), \
|
||||
patch("hermes_state.SessionDB", return_value=fake_db), \
|
||||
patch("hermes_cli.runtime_provider.resolve_runtime_provider", return_value=fake_runtime) as runtime_mock, \
|
||||
patch("run_agent.AIAgent") as mock_agent_cls:
|
||||
mock_agent = MagicMock()
|
||||
mock_agent.run_conversation.return_value = {"final_response": "ok"}
|
||||
mock_agent_cls.return_value = mock_agent
|
||||
|
||||
success, output, final_response, error = run_job(job)
|
||||
|
||||
assert success is True
|
||||
assert error is None
|
||||
assert final_response == "ok"
|
||||
assert "ok" in output
|
||||
runtime_mock.assert_called_once_with(
|
||||
requested="custom",
|
||||
explicit_base_url="http://127.0.0.1:4000/v1",
|
||||
)
|
||||
assert mock_agent_cls.call_args.kwargs["model"] == "perplexity/sonar-pro"
|
||||
fake_db.close.assert_called_once()
|
||||
|
||||
|
||||
class TestRunJobSkillBacked:
|
||||
def test_run_job_loads_skill_and_disables_recursive_cron_tools(self, tmp_path):
|
||||
job = {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ def test_nous_oauth_setup_keeps_current_model_when_syncing_disk_provider(
|
|||
|
||||
config = load_config()
|
||||
|
||||
prompt_choices = iter([0, 2])
|
||||
# Provider selection always comes first. Depending on available vision
|
||||
# backends, setup may either skip the optional vision step or prompt for
|
||||
# it before the default-model choice. Provide enough selections for both
|
||||
# paths while still ending on "keep current model".
|
||||
prompt_choices = iter([0, 2, 2])
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.setup.prompt_choice",
|
||||
lambda *args, **kwargs: next(prompt_choices),
|
||||
|
|
|
|||
|
|
@ -137,6 +137,22 @@ class TestScheduleCronjob:
|
|||
))
|
||||
assert result["repeat"] == "5 times"
|
||||
|
||||
def test_schedule_persists_runtime_overrides(self):
|
||||
result = json.loads(schedule_cronjob(
|
||||
prompt="Pinned job",
|
||||
schedule="every 1h",
|
||||
model="anthropic/claude-sonnet-4",
|
||||
provider="custom",
|
||||
base_url="http://127.0.0.1:4000/v1/",
|
||||
))
|
||||
assert result["success"] is True
|
||||
|
||||
listing = json.loads(list_cronjobs())
|
||||
job = listing["jobs"][0]
|
||||
assert job["model"] == "anthropic/claude-sonnet-4"
|
||||
assert job["provider"] == "custom"
|
||||
assert job["base_url"] == "http://127.0.0.1:4000/v1"
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# list_cronjobs
|
||||
|
|
@ -249,6 +265,33 @@ class TestUnifiedCronjobTool:
|
|||
assert updated["job"]["name"] == "New Name"
|
||||
assert updated["job"]["schedule"] == "every 120m"
|
||||
|
||||
def test_update_runtime_overrides_can_set_and_clear(self):
|
||||
created = json.loads(
|
||||
cronjob(
|
||||
action="create",
|
||||
prompt="Check",
|
||||
schedule="every 1h",
|
||||
model="anthropic/claude-sonnet-4",
|
||||
provider="custom",
|
||||
base_url="http://127.0.0.1:4000/v1",
|
||||
)
|
||||
)
|
||||
job_id = created["job_id"]
|
||||
|
||||
updated = json.loads(
|
||||
cronjob(
|
||||
action="update",
|
||||
job_id=job_id,
|
||||
model="openai/gpt-4.1",
|
||||
provider="openrouter",
|
||||
base_url="",
|
||||
)
|
||||
)
|
||||
assert updated["success"] is True
|
||||
assert updated["job"]["model"] == "openai/gpt-4.1"
|
||||
assert updated["job"]["provider"] == "openrouter"
|
||||
assert updated["job"]["base_url"] is None
|
||||
|
||||
def test_create_skill_backed_job(self):
|
||||
result = json.loads(
|
||||
cronjob(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue