Skip to content

feat: allow before_agent_start hook to override model selection#17614

Closed
plc wants to merge 3 commits intoopenclaw:mainfrom
plc:feat/before-agent-start-model-override
Closed

feat: allow before_agent_start hook to override model selection#17614
plc wants to merge 3 commits intoopenclaw:mainfrom
plc:feat/before-agent-start-model-override

Conversation

@plc
Copy link
Copy Markdown

@plc plc commented Feb 16, 2026

Summary

Add an optional model field to PluginHookBeforeAgentStartResult so plugins can override the model for an agent run.

This builds on #14873 (which exposes model/tools/sender context to the hook) by letting plugins write back a model override. This enables plugin-based model routers.

Motivation

Plugins like cost-optimising model routers need to select models per-request based on prompt complexity, session context, and cost. Currently the only way to do this is with an external HTTP proxy (e.g. ClawRoute), which has no access to OpenClaw session context.

With this change, a plugin can:

api.on('before_agent_start', (event, ctx) => {
    const tier = classifyComplexity(event.prompt, ctx);
    return { model: tierToModel[tier] };  // e.g. 'anthropic/claude-haiku-3-5'
});

Use cases

  • Cost-optimising model routers: route simple messages to cheap models, complex ones to frontier
  • A/B testing: randomly assign models to measure quality
  • Rate-limit-aware routing: switch providers when one is throttled
  • Per-user model assignment: different users get different tiers

Changes

3 files, +46/-2 lines:

  1. src/plugins/types.ts: Add model?: string to PluginHookBeforeAgentStartResult with JSDoc
  2. src/plugins/hooks.ts: Add model to the merge function in runBeforeAgentStart (last plugin wins)
  3. src/agents/pi-embedded-runner/run.ts: Run the hook early in runEmbeddedPiAgent (before auth/context-window resolution) to extract the model override. The hook continues to run later in attempt.ts for systemPrompt and prependContext.

Design decisions

User overrides take precedence: If authProfileIdSource === 'user' (e.g. /model command), the hook model is skipped entirely.

Fail-open: If the hook throws or returns an unresolvable model, the original model is used with a warning log.

Early execution: The hook runs before resolveModel() so auth profiles, context window checks, and failover all work naturally with the overridden provider/model.

Aliases supported: Uses existing resolveModelRefFromString, so model aliases work.

Backward-compatible: model is optional; existing plugins are unaffected.

Testing notes

  • Plugin returns { model: 'anthropic/claude-haiku-3-5' } → model changes, auth resolves for new provider
  • Plugin returns { model: 'nonexistent/model' }resolveModel fails, original model used
  • Plugin returns {} or undefined → no change (existing behaviour)
  • User has /model opus override → hook is skipped entirely
  • Multiple plugins return model → last by priority wins (consistent with systemPrompt merge)

Greptile Summary

Adds plugin-based model override capability via before_agent_start hook. The hook runs early in the agent lifecycle (before auth resolution) to allow plugins to select models based on prompt complexity, session context, and cost optimization.

Key changes:

  • Added optional model field to PluginHookBeforeAgentStartResult in src/plugins/types.ts:338
  • Updated hook merge logic to honor last plugin's model selection in src/plugins/hooks.ts:203
  • Implemented early hook execution in src/agents/pi-embedded-runner/run.ts:222-252 with proper fallback handling

Strengths:

  • Proper null checking for resolveModelRefFromString result (addresses previous review feedback)
  • Falls back to original model if hook-provided model fails to resolve
  • Error handling with try-catch and informative logging
  • Backward compatible (optional field)

Known limitation:

  • User model selection via /model command is not detected (only authProfileIdSource is checked). This is documented in the code comments at src/agents/pi-embedded-runner/run.ts:216-217 as a future enhancement. Plugins could potentially override user's /model selections.

Confidence Score: 4/5

  • This PR is safe to merge with minor limitations documented in code
  • The implementation is well-designed with proper error handling, null safety, and fallback logic. Previous review feedback on null checking has been addressed. The main limitation (not detecting /model command overrides) is documented and acknowledged as a future enhancement. The code follows existing patterns and is backward compatible.
  • No files require special attention - all changes are straightforward and well-implemented

Last reviewed commit: 25cf41f

Add an optional `model` field to `PluginHookBeforeAgentStartResult`
so plugins can dynamically select the model for an agent run based on
prompt complexity, session context, or cost optimisation rules.

The model override runs early in `runEmbeddedPiAgent` (before auth
profile and context window resolution) so the full model pipeline
flows naturally with the overridden provider/model.

Safety:
- Ignored when user has set a manual model override (authProfileIdSource === 'user')
- Failed resolution falls back to original model with a warning log
- Fully backward-compatible: existing plugins continue to work unchanged
- Model aliases supported via existing resolveModelRefFromString

The hook also continues to run in attempt.ts for systemPrompt/prependContext;
only the model field is extracted in the early pass.

Use case: plugin-based model routers that classify prompt complexity
and route to the optimal model tier (e.g. Haiku for acks, Sonnet for
general chat, Opus for complex reasoning).
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0a7722873

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

- Add null check for resolveModelRefFromString result (Greptile feedback)
- Check modelSource/providerSource in addition to authProfileIdSource to properly detect user model overrides (Codex P1)
- Add fallback to original model when hook-provided model can't be resolved (Codex P1)
- Fix formatting with oxfmt
Copy link
Copy Markdown
Author

@plc plc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed:

  1. Added null check for resolveModelRefFromString
  2. Now checking modelSource/providerSource too, not just authProfileIdSource
  3. Falls back to original model if hook model can't be resolved

Those properties don't exist on RunEmbeddedPiAgentParams yet.
Kept authProfileIdSource check and added TODO comment about
future enhancement to track /model directive source.
@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Feb 16, 2026
@steipete steipete closed this Feb 16, 2026
@steipete steipete reopened this Feb 17, 2026
@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added stale Marked as stale due to inactivity and removed stale Marked as stale due to inactivity labels Feb 22, 2026
@bolander72
Copy link
Copy Markdown

This would be huge for local LLM routing. Running Qwen 3.5 via Ollama alongside Claude Max — a before_agent_start model override would let me classify messages locally and route simple ones to ollama/qwen3.5:4b while keeping complex tasks on Opus. Currently there's no way to dynamically switch models per-message without an external proxy. This + #32802 would cover both use cases (skip agent entirely OR route to a cheaper model).

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added the stale Marked as stale due to inactivity label Mar 14, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

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

Labels

agents Agent runtime and tooling size: S stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants