feat(cron): per-job reasoning_effort override in job definitions - #91038
feat(cron): per-job reasoning_effort override in job definitions#91038victor-kyriazakos wants to merge 2 commits into
Conversation
A cron job can now pin its own reasoning (thinking) effort, independent of the global agent.reasoning_effort and per-model reasoning_overrides. Heavy scheduled analyses can run at high while cheap recurring jobs run at minimal, without touching the fleet-wide default. - cron/jobs.py: new optional job field, validated at the storage choke point against the canonical grammar via the shared hermes_constants.parse_reasoning_effort (spelling-only; capability clamping stays owned by the provider transports at send time, same as config-set effort). Empty string clears on update; invalid values raise ValueError before anything persists. Not a drift-guard axis. - cron/scheduler.py: _resolve_job_reasoning_config resolves per-job pin > agent.reasoning_overrides > agent.reasoning_effort at fire time, after the auth-fallback model swap (the pin is model-independent by design). A stored value that no longer parses warns and falls back to config resolution instead of killing the tick. - tools/cronjob_tools.py: reasoning_effort on BOTH mutation verbs (create and update), conditional key in _format_job, schema documents grammar/precedence/transport clamping/clear semantics. Agent-settable, unlike model/provider pins: it cannot redirect spend to a different model. - hermes cron create/edit --reasoning-effort (empty string clears). - Docs: cron feature page tip + CLI reference rows. Tests: tests/cron/test_cron_reasoning_effort.py (32) — store contract, scheduler precedence incl. byte-identical absent-field behavior and garbage fallback, tool create/update/clear/error paths, schema surface.
Duplicate of #7382. This implements the same per-job reasoning_effort persistence, scheduler-precedence, tool, and CLI mechanism; the extra validation/fallback details are refinements of that same implementation. |
|
Salvaged and merged via #91244 — both your commits cherry-picked with authorship preserved, thanks @victor-kyriazakos! One change from the original: the |
Summary
Cron jobs can now pin their own reasoning (thinking) effort. A new optional job field
reasoning_effortoverrides both the globalagent.reasoning_effortand per-modelagent.reasoning_overridesfor that job's runs. Heavy scheduled analyses can run athighwhile cheap recurring jobs run atminimal, without changing the install-wide default. Requested by an enterprise self-host deployment.Jobs without the field behave exactly as before.
Design
The field validates against the canonical effort grammar (
hermes_constants.parse_reasoning_effort:none|minimal|low|medium|high|xhigh|max|ultra) at the storage choke point,cron/jobs.py. Validation is spelling-only by intent: the job's model is not knowable at create time (unpinned jobs resolve at fire time, auth fallback can swap the model mid-run), and the provider transports already clamp or omit unsupported levels at request time for config-set effort. The job pins intent; the transport owns capability. Making the store model-capability-aware would duplicate the transports' live-verified capability tables and drift.Resolution happens in
cron/scheduler.py::_resolve_job_reasoning_config, called at the existing singlereasoning_configproduction site inrun_job(after the auth-fallback model swap, so a pinned effort also governs the fallback model). Precedence: job pin >agent.reasoning_overrides>agent.reasoning_effort. A stored value that no longer parses (hand-editedjobs.json) logs a warning and falls back to config resolution instead of failing the tick.The
cronjobmodel tool accepts the parameter on both create and update (empty string clears). It is agent-settable, unlike the model/provider pins: effort cannot redirect spend to a different model, it only tunes thinking within the model the job already resolves to.hermes cron create/edit --reasoning-effortcovers the CLI. The field is not a drift-guard snapshot axis (#44585 guard unchanged): a pin is explicit, and absent-field jobs keep config-following semantics.Inert with
no_agent=True; the no-agent path short-circuits before model resolution.Commits
f6fa0f6feat(cron): per-job reasoning_effort override in job definitions2f13631test(cron): scrub tracker references from reasoning-effort test docstringSurfaces changed
cron/jobs.py—_normalize_reasoning_effort, create/update wiring, docstringcron/scheduler.py—_resolve_job_reasoning_config, call-site swap inrun_jobtools/cronjob_tools.py— schema param, create + update branches,_format_jobconditional key, registry handlerhermes_cli/subcommands/cron.py,hermes_cli/cron.py—--reasoning-efforton create/editwebsite/docs/user-guide/features/cron.md,website/docs/reference/cli-commands.mdtests/cron/test_cron_reasoning_effort.py— 32 testsVerification
Unit (canonical runner, includes the full cron suite plus CLI/tool siblings):
Mutation checks (fixes committed first, one side mutated, restored via git checkout):
resolve_reasoning_configpassthrough → 4 precedence tests fail (test_job_effort_beats_global_and_per_model_override,test_job_none_disables_thinking_never_reenabled_by_config,test_garbage_in_store_warns_and_falls_back,test_job_effort_is_model_independent); restore → 32/32 green.str(value)passthrough → 10 tests fail across store and tool lanes; restore → 32/32 green.E2E against the real dev-checkout CLI with an isolated
HERMES_HOME(7/7):hermes cron create --reasoning-effort highpersists the pin injobs.jsonedit XHIGHnormalizes toxhigh;edit ""clears (stores null, same convention asworkdir)--reasoning-effort turboreturns an actionable error naming the valid levels; nothing persistedcron.scheduler.tick()fired the pinned job and loggedusing per-job reasoning_effort 'minimal'at resolution timecronjob()tool call stores the pinSibling-lane sweep: one
reasoning_configproduction site in the scheduler feedsAIAgentfor both the chat-completions and ACP/codex lanes; the dashboard (web_server.py) and blueprint catalog both funnel job writes throughcron.jobs.create_job, so no writer bypasses validation.Considered and not done
cron.reasoning_effortglobal config key:agent.reasoning_effortplusreasoning_overridesalready govern the cron default; a third global would add precedence ambiguity with no consumer story. Possible follow-up if a fleet-level cron default is requested.