feat(heartbeat): add model override for heartbeat phases#3368
feat(heartbeat): add model override for heartbeat phases#3368hussein1362 wants to merge 1 commit intoHKUDS:mainfrom
Conversation
186642e to
826a463
Compare
Add gateway.heartbeat.model config option that lets operators run
heartbeat on a different (typically cheaper) model than the agent's
primary model.
The override flows cleanly through the call chain without mutating
shared agent state:
process_direct(model_override=...)
→ _process_message(model_override=...)
→ _run_agent_loop(model_override=...)
→ AgentRunSpec(model=override or self.model)
This is a first-class implementation: the model override is scoped to
the request and never touches agent.model, so concurrent message
processing (if ever enabled) remains safe.
Phase 1 (heartbeat decision) uses the override via HeartbeatService
which stores it as self.model for the provider.chat_with_retry call.
Phase 2 (agent execution) passes model_override through process_direct
to AgentRunSpec.
Config example:
{
"gateway": {
"heartbeat": {
"model": "anthropic/claude-haiku-3.5"
}
}
}
826a463 to
4ffaf47
Compare
|
Thanks for the PR 💖 I'm curious about the use case — is there user feedback or a real scenario where heartbeat model cost is a concern? Also, the current approach threads |
|
Thanks for the thoughtful feedback! The numbers might help explain the motivation. I run a gateway with multiple agents where heartbeat fires every 30 minutes. A single heartbeat run processes For most users, heartbeat is background housekeeping that doesn't need flagship reasoning. But right now there's no way to decouple it from the primary model — so anyone who wants a capable chat model is paying that same rate for "check if anything happened in the last 30 minutes." That's a real barrier to enabling heartbeat at all, especially for self-hosters watching their API bill. On the implementation — I explored a few approaches before landing on this one:
I went with #4 but if any of the others feel more aligned with how you'd want nanobot to handle per-request overrides, happy to rework it. Or if there's a pattern I missed entirely — even better. |
Summary
Add
gateway.heartbeat.modelconfig option that lets operators run heartbeat on a different (typically cheaper) model than the agent's primary model. First-class implementation — no shared state mutation.Motivation
Heartbeat runs are periodic background checks (email, calendar, signals) that don't need the full reasoning power of a flagship model. Currently, heartbeat always uses the agent's primary model — so if you run
gpt-5.4for chat, heartbeat also burns those tokens on routine checks.This PR lets you decouple the two:
{ "gateway": { "heartbeat": { "model": "anthropic/claude-haiku-3.5" } } }Chat stays on the primary model, heartbeat runs on a cheaper one.
Architecture
The model override flows cleanly through the call chain without mutating shared agent state:
This is request-scoped:
agent.modelis never touched, so concurrent message processing remains safe. Compare to the naive approach of temporarily swappingagent.modelin a try/finally — that mutates shared state and creates a race window.Phase 1 (decision):
HeartbeatServicestores the override asself.modeland passes it toprovider.chat_with_retry()directly.Phase 2 (execution): The override passes through
process_direct → _process_message → _run_agent_loop → AgentRunSpecwithout touching the agent's model field.Changes
nanobot/config/schema.py— Add optionalmodel: str | NonetoHeartbeatConfig(default:None)nanobot/agent/loop.py— Addmodel_override: str | None = Noneparameter toprocess_direct,_process_message, and_run_agent_loop.AgentRunSpecreceivesmodel_override or self.model.nanobot/heartbeat/service.py— Acceptheartbeat_modelparam; use it for Phase 1 LLM callsnanobot/cli/commands.py— Passhb_cfg.modelasmodel_overridetoprocess_direct(clean, no try/finally mutation)tests/heartbeat/test_heartbeat_model_override.py— 7 tests: config defaults, service model selection, Phase 1 routing,process_directsignature validationBehavior
modelnot setmodel: "haiku"Tests