feat(worker): clamp max-turns to a configurable ceiling
Runaway sessions were the single biggest cost driver: model_presets was never persisted (stayed code-only), default_max_turns shipped at 100, and ResolveMaxTurns had no upper bound, so a task/list override could run hundreds of turns unchecked. - TaskRunner.ResolveMaxTurns now clamps the resolved value to AppSettings.MaxTurnsCeiling (new column, default 80) and logs a warning with task id / requested / effective value when it clamps. - default_max_turns default lowered from 100 to 40 (entity, EF config, and the seeded row via the new AddMaxTurnsCeiling migration). - AppSettingsRepository.GetAsync backfills model_presets with the shipping defaults on first read instead of leaving the column null. - Settings > General's per-model preset table and the task/list agent editor now show a hint when a set max-turns value exceeds the ceiling.
This commit is contained in:
@@ -522,12 +522,21 @@ public sealed class TaskRunner
|
||||
var model = task.Model ?? listConfig?.Model ?? global.DefaultModel;
|
||||
var preset = Data.Models.ModelPresets.For(global.ModelPresets, model, global.DefaultMaxTurns);
|
||||
|
||||
var requestedMaxTurns = task.MaxTurns ?? listConfig?.MaxTurns ?? preset.MaxTurns;
|
||||
var maxTurns = ResolveMaxTurns(task.MaxTurns, listConfig?.MaxTurns, preset.MaxTurns, global.MaxTurnsCeiling);
|
||||
if (maxTurns < requestedMaxTurns)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Task {TaskId}: max turns clamped to ceiling (requested={Requested}, effective={Effective}, ceiling={Ceiling})",
|
||||
task.Id, requestedMaxTurns, maxTurns, global.MaxTurnsCeiling);
|
||||
}
|
||||
|
||||
return new ClaudeRunConfig(
|
||||
Model: model,
|
||||
SystemPrompt: string.IsNullOrWhiteSpace(instructions) ? null : instructions,
|
||||
AgentPath: task.AgentPath ?? listConfig?.AgentPath,
|
||||
ResumeSessionId: resumeSessionId,
|
||||
MaxTurns: ResolveMaxTurns(task.MaxTurns, listConfig?.MaxTurns, preset.MaxTurns),
|
||||
MaxTurns: maxTurns,
|
||||
PermissionMode: global.DefaultPermissionMode,
|
||||
SkillNames: skillNames,
|
||||
Effort: preset.Effort);
|
||||
@@ -586,8 +595,8 @@ public sealed class TaskRunner
|
||||
return names;
|
||||
}
|
||||
|
||||
internal static int? ResolveMaxTurns(int? taskTurns, int? listTurns, int globalDefault)
|
||||
=> taskTurns ?? listTurns ?? globalDefault;
|
||||
internal static int? ResolveMaxTurns(int? taskTurns, int? listTurns, int globalDefault, int ceiling)
|
||||
=> Math.Min(taskTurns ?? listTurns ?? globalDefault, ceiling);
|
||||
|
||||
public static string MergeInstructions(params string?[] parts)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user