Skip to content

Fix sessions_spawn Google/Gemini model routing#36154

Closed
Octane0411 wants to merge 1 commit intoopenclaw:mainfrom
OasAIStudio:codex/fix-36134-gemini-spawn
Closed

Fix sessions_spawn Google/Gemini model routing#36154
Octane0411 wants to merge 1 commit intoopenclaw:mainfrom
OasAIStudio:codex/fix-36134-gemini-spawn

Conversation

@Octane0411
Copy link
Copy Markdown
Contributor

Fixes #36134 by resolving sessions_spawn model routing for bare Gemini model names and surfacing unavailable model overrides.

Changes:

  • Update subagent model resolution to infer provider for unqualified model names in spawn flow (including Google Gemini patterns) and normalize configured/bare overrides before patch.
  • Keep bare configured/per-agent defaults consistent with spawn model inference so Gemini values are normalized to google/gemini-style provider refs.
  • Return modelApplied: false on sessions.patch model patch failures so unavailable overrides are not reported as successfully applied.

Tests:

  • pnpm test src/agents/openclaw-tools.subagents.sessions-spawn.model.test.ts
  • pnpm test src/agents/model-selection.test.ts

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Mar 5, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR fixes Gemini/Google model routing in the sessions_spawn flow by introducing a normalizeSubagentSpawnModelSelection helper that maps bare model names (e.g. gemini-2.5-flash) to fully-qualified provider/model refs (e.g. google/gemini-2.5-flash), and ensures all three model sources (runtime override, per-agent config, global default) are normalized before being used in session patches. It also correctly sets modelApplied: false when a model patch fails.

Key changes:

  • New inferProviderFromModelHint helper that first checks configured models for a unique provider match, then falls back to pattern-based detection for Gemini model names (gemini / gemini-*google).
  • New normalizeSubagentSpawnModelSelection helper normalizes bare model names and provider-prefixed refs to canonical provider/model form.
  • resolveSubagentSpawnModelSelection now normalizes all three model sources before priority selection, so bare Gemini names are correctly routed regardless of which source supplies the model.
  • subagent-spawn.ts: modelApplied: false is now included in error returns on model patch failure.
  • Tests updated to assert the fully-qualified provider/model format and modelApplied: false on failure, with a new test covering bare Gemini model routing.

Issues found:

  • The ?? "" fallback in resolvedModelOverride creates a subtle edge case: when params.modelOverride is whitespace-only, the empty string short-circuits the ?? fallback chain instead of continuing to configured/primary model fallbacks.
  • resolveAgentModelPrimaryValue is called redundantly twice when computing normalizedPrimaryModel.

Confidence Score: 2/5

  • Core Gemini routing fix and modelApplied behavior are sound, but a subtle fallback-chain regression and redundant function call should be addressed before merging.
  • The Gemini model routing normalization and modelApplied: false error surfacing are correct and well-tested. However, the edge case where whitespace-only model overrides produce empty strings that short-circuit the ?? fallback chain is a real functional issue. Additionally, resolveAgentModelPrimaryValue is called redundantly. These should be fixed to prevent potential bugs and improve code quality. The test coverage for the happy path is strong, but edge cases around empty/whitespace model inputs are not covered.
  • src/agents/model-selection.ts requires fixes to the fallback logic and the redundant function call before merging.

Last reviewed commit: c91556c

Comment on lines +417 to +423
const resolvedModelOverride = params.modelOverride
? normalizeSubagentSpawnModelSelection({
cfg: params.cfg,
model: normalizeModelSelection(params.modelOverride) ?? "",
defaultProvider: runtimeDefault.provider,
})
: undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Empty string short-circuits the ?? fallback chain. When params.modelOverride is whitespace-only (e.g. " "), normalizeModelSelection returns undefined, then ?? "" converts it to "". Since normalizeSubagentSpawnModelSelection returns "" for empty input, resolvedModelOverride becomes "" — a truthy-but-empty string that bypasses the ?? operator's fallback logic (which only skips null/undefined). This causes the function to return "" instead of falling through to normalizedConfiguredModel or normalizedPrimaryModel.

To fix, check the normalizeModelSelection result before passing to normalizeSubagentSpawnModelSelection:

Suggested change
const resolvedModelOverride = params.modelOverride
? normalizeSubagentSpawnModelSelection({
cfg: params.cfg,
model: normalizeModelSelection(params.modelOverride) ?? "",
defaultProvider: runtimeDefault.provider,
})
: undefined;
const resolvedModelOverride = (() => {
const m = normalizeModelSelection(params.modelOverride);
if (!m) return undefined;
return normalizeSubagentSpawnModelSelection({
cfg: params.cfg,
model: m,
defaultProvider: runtimeDefault.provider,
});
})();
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-selection.ts
Line: 417-423

Comment:
Empty string short-circuits the `??` fallback chain. When `params.modelOverride` is whitespace-only (e.g. `"  "`), `normalizeModelSelection` returns `undefined`, then `?? ""` converts it to `""`. Since `normalizeSubagentSpawnModelSelection` returns `""` for empty input, `resolvedModelOverride` becomes `""` — a truthy-but-empty string that bypasses the `??` operator's fallback logic (which only skips `null`/`undefined`). This causes the function to return `""` instead of falling through to `normalizedConfiguredModel` or `normalizedPrimaryModel`.

To fix, check the `normalizeModelSelection` result before passing to `normalizeSubagentSpawnModelSelection`:

```suggestion
const resolvedModelOverride = (() => {
  const m = normalizeModelSelection(params.modelOverride);
  if (!m) return undefined;
  return normalizeSubagentSpawnModelSelection({
    cfg: params.cfg,
    model: m,
    defaultProvider: runtimeDefault.provider,
  });
})();
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +435 to +444
const normalizedPrimaryModel = resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model)
? normalizeSubagentSpawnModelSelection({
cfg: params.cfg,
model:
normalizeModelSelection(
resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model),
) ?? "",
defaultProvider: runtimeDefault.provider,
})
: undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model) is evaluated twice — once in the guard condition (line 435) and once inside the normalizeSubagentSpawnModelSelection call (lines 440-441). Extract it to a variable to avoid the redundant call and improve clarity:

Suggested change
const normalizedPrimaryModel = resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model)
? normalizeSubagentSpawnModelSelection({
cfg: params.cfg,
model:
normalizeModelSelection(
resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model),
) ?? "",
defaultProvider: runtimeDefault.provider,
})
: undefined;
const primaryModelValue = resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model);
const normalizedPrimaryModel = primaryModelValue
? normalizeSubagentSpawnModelSelection({
cfg: params.cfg,
model: normalizeModelSelection(primaryModelValue) ?? "",
defaultProvider: runtimeDefault.provider,
})
: undefined;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/model-selection.ts
Line: 435-444

Comment:
`resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model)` is evaluated twice — once in the guard condition (line 435) and once inside the `normalizeSubagentSpawnModelSelection` call (lines 440-441). Extract it to a variable to avoid the redundant call and improve clarity:

```suggestion
const primaryModelValue = resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model);
const normalizedPrimaryModel = primaryModelValue
  ? normalizeSubagentSpawnModelSelection({
      cfg: params.cfg,
      model: normalizeModelSelection(primaryModelValue) ?? "",
      defaultProvider: runtimeDefault.provider,
    })
  : undefined;
```

How can I resolve this? If you propose a fix, please make it concise.

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: c91556c06d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +245 to +247
const selectedProvider = normalizeProviderId(inferredProvider ?? params.defaultProvider);
const selectedModel = normalizeProviderModelId(selectedProvider, trimmed);
return `${selectedProvider}/${selectedModel}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve alias model names in subagent spawn selection

Do not unconditionally prefix bare model strings with a provider here, because this rewrites aliases (for example fast) into provider-qualified refs (for example anthropic/fast) before sessions.patch. Alias resolution in resolveModelRefFromString only runs for values without /, so provider-prefixing causes valid alias-based sessions_spawn overrides/defaults to bypass alias lookup and fail allowlist checks as model not allowed.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: sessions_spawn silently falls back to Anthropic (set fallback model) when targeting Google Gemini models on spawned subagent tasks.

2 participants