Skip to content

feat(cron): per-job reasoning_effort override in job definitions - #91038

Closed
victor-kyriazakos wants to merge 2 commits into
NousResearch:mainfrom
victor-kyriazakos:feat/cron-reasoning-effort
Closed

feat(cron): per-job reasoning_effort override in job definitions#91038
victor-kyriazakos wants to merge 2 commits into
NousResearch:mainfrom
victor-kyriazakos:feat/cron-reasoning-effort

Conversation

@victor-kyriazakos

Copy link
Copy Markdown
Contributor

Summary

Cron jobs can now pin their own reasoning (thinking) effort. A new optional job field reasoning_effort overrides both the global agent.reasoning_effort and per-model agent.reasoning_overrides for that job's runs. Heavy scheduled analyses can run at high while cheap recurring jobs run at minimal, 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 single reasoning_config production site in run_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-edited jobs.json) logs a warning and falls back to config resolution instead of failing the tick.

The cronjob model 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-effort covers 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

  • f6fa0f6 feat(cron): per-job reasoning_effort override in job definitions
  • 2f13631 test(cron): scrub tracker references from reasoning-effort test docstring

Surfaces changed

  • cron/jobs.py_normalize_reasoning_effort, create/update wiring, docstring
  • cron/scheduler.py_resolve_job_reasoning_config, call-site swap in run_job
  • tools/cronjob_tools.py — schema param, create + update branches, _format_job conditional key, registry handler
  • hermes_cli/subcommands/cron.py, hermes_cli/cron.py--reasoning-effort on create/edit
  • website/docs/user-guide/features/cron.md, website/docs/reference/cli-commands.md
  • tests/cron/test_cron_reasoning_effort.py — 32 tests

Verification

Unit (canonical runner, includes the full cron suite plus CLI/tool siblings):

=== Summary: 71 files, 948 tests passed, 0 failed, 1 skipped (100% complete) in 22.0s (8 workers) ===

Mutation checks (fixes committed first, one side mutated, restored via git checkout):

  • Scheduler helper stubbed to plain resolve_reasoning_config passthrough → 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.
  • Store validator stubbed to 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 high persists the pin in jobs.json
  • edit XHIGH normalizes to xhigh; edit "" clears (stores null, same convention as workdir)
  • --reasoning-effort turbo returns an actionable error naming the valid levels; nothing persisted
  • a real cron.scheduler.tick() fired the pinned job and logged using per-job reasoning_effort 'minimal' at resolution time
  • a direct cronjob() tool call stores the pin

Sibling-lane sweep: one reasoning_config production site in the scheduler feeds AIAgent for both the chat-completions and ACP/codex lanes; the dashboard (web_server.py) and blueprint catalog both funnel job writes through cron.jobs.create_job, so no writer bypasses validation.

Considered and not done

  • A cron.reasoning_effort global config key: agent.reasoning_effort plus reasoning_overrides already 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.
  • Model-capability validation at the store (see Design above).
  • Extending the effort level grammar; per-fallback-entry effort interplay changes; desktop/TUI cron editor surfaces.

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.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cron Cron scheduler and job management area/config Config system, migrations, profiles duplicate This issue or pull request already exists labels Aug 20, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

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.

@teknium1

Copy link
Copy Markdown
Contributor

Salvaged and merged via #91244 — both your commits cherry-picked with authorship preserved, thanks @victor-kyriazakos! One change from the original: the reasoning_effort arg was removed from the model-facing cronjob tool schema (standing policy: models don't make model-configuration decisions — same pattern as the existing model/provider/base_url omission in the tool dispatch). The CLI (hermes cron create/edit --reasoning-effort), store validation, and scheduler precedence all landed as you built them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cron Cron scheduler and job management duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants