fix(worker): normalize model alias before preset lookup, wire DefaultMaxTurns as fallback
A full model id like claude-sonnet-4-6 in list_config.model never matched any ModelPresets row (only bare aliases did), so every run under it landed on the hardcoded 30-turn/DefaultLevel fallback instead of the sonnet preset row - this is what killed two autonomous tasks at the 30-turn limit. ModelRegistry.TryNormalizeAlias (non-throwing: exact match, then substring match against a full model id) lets ModelPresets.For resolve a full model id to its alias's preset row. For a model that still doesn't resolve, the hardcoded 30 is replaced by a caller-supplied fallbackMaxTurns, and TaskRunner now passes AppSettings.DefaultMaxTurns there - so that setting has a real effect instead of being dead, matching the direction already noted in docs/open.md.
This commit is contained in:
@@ -46,20 +46,26 @@ public static class ModelPresets
|
||||
}
|
||||
|
||||
/// <summary>The preset for <paramref name="model"/>, or the default row for it. Never null so
|
||||
/// callers don't have to special-case an unconfigured or unknown model.</summary>
|
||||
public static ModelPreset For(IReadOnlyList<ModelPreset> presets, string? model)
|
||||
/// callers don't have to special-case an unconfigured or unknown model. <paramref name="model"/>
|
||||
/// is resolved through <see cref="ModelRegistry.TryNormalizeAlias"/> first, so a full CLI model
|
||||
/// id (e.g. "claude-sonnet-4-6") still hits its alias's preset row instead of falling through.
|
||||
/// If <paramref name="model"/> normalizes to nothing recognized, <paramref name="fallbackMaxTurns"/>
|
||||
/// (typically <c>AppSettings.DefaultMaxTurns</c>) is used instead of a hardcoded number, so a
|
||||
/// truly unknown model is no harsher than the configured global default.</summary>
|
||||
public static ModelPreset For(IReadOnlyList<ModelPreset> presets, string? model, int fallbackMaxTurns = 30)
|
||||
{
|
||||
var alias = (model ?? ModelRegistry.DefaultAlias).Trim();
|
||||
var alias = ModelRegistry.TryNormalizeAlias(model) ?? (model ?? ModelRegistry.DefaultAlias).Trim();
|
||||
foreach (var p in presets)
|
||||
if (string.Equals(p.Model, alias, StringComparison.OrdinalIgnoreCase))
|
||||
return p;
|
||||
foreach (var p in Defaults)
|
||||
if (string.Equals(p.Model, alias, StringComparison.OrdinalIgnoreCase))
|
||||
return p;
|
||||
return new ModelPreset(alias, EffortRegistry.DefaultLevel, 30);
|
||||
return new ModelPreset(alias, EffortRegistry.DefaultLevel, fallbackMaxTurns);
|
||||
}
|
||||
|
||||
public static ModelPreset For(string? json, string? model) => For(Parse(json), model);
|
||||
public static ModelPreset For(string? json, string? model, int fallbackMaxTurns = 30)
|
||||
=> For(Parse(json), model, fallbackMaxTurns);
|
||||
|
||||
// One row per known alias, canonical order, clamped values — so an edited or partial list
|
||||
// still round-trips into something every consumer can rely on.
|
||||
|
||||
@@ -26,4 +26,22 @@ public static class ModelRegistry
|
||||
return alias;
|
||||
throw new ArgumentException($"Unknown model '{model}'. Allowed: {string.Join(", ", Aliases)}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort alias lookup for run-path model resolution, where a full CLI model id
|
||||
/// (e.g. "claude-sonnet-4-6") is just as likely as a bare alias and a run must never be
|
||||
/// blocked by an unrecognized one. Null/blank/unrecognized → null; never throws.
|
||||
/// </summary>
|
||||
public static string? TryNormalizeAlias(string? model)
|
||||
{
|
||||
var m = model?.Trim();
|
||||
if (string.IsNullOrEmpty(m)) return null;
|
||||
foreach (var alias in Aliases)
|
||||
if (string.Equals(alias, m, StringComparison.OrdinalIgnoreCase))
|
||||
return alias;
|
||||
foreach (var alias in Aliases)
|
||||
if (m.Contains(alias, StringComparison.OrdinalIgnoreCase))
|
||||
return alias;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user