feat(KB-147): add planning and validator model settings
- Extend core Settings types with planningProvider, planningModelId, validatorProvider, validatorModelId fields - Add model selectors in Settings UI for planning and validator models - Use planning model for triage task specification generation - Use validator model for specification review in triage - Use validator model for code reviews in executor agent - Update AGENTS.md with model hierarchy documentation
This commit is contained in:
@@ -290,6 +290,22 @@ export interface Settings {
|
||||
* Must be set together with `defaultProvider`. When both are undefined,
|
||||
* the engine uses pi's automatic model resolution. */
|
||||
defaultModelId?: string;
|
||||
/** AI model provider for planning/triage (specification) agent.
|
||||
* Must be set together with `planningModelId`. When both are undefined,
|
||||
* falls back to `defaultProvider`/`defaultModelId`. */
|
||||
planningProvider?: string;
|
||||
/** AI model ID for planning/triage (specification) agent.
|
||||
* Must be set together with `planningProvider`. When both are undefined,
|
||||
* falls back to `defaultProvider`/`defaultModelId`. */
|
||||
planningModelId?: string;
|
||||
/** AI model provider for validator/reviewer agent.
|
||||
* Must be set together with `validatorModelId`. When both are undefined,
|
||||
* falls back to `defaultProvider`/`defaultModelId`. */
|
||||
validatorProvider?: string;
|
||||
/** AI model ID for validator/reviewer agent.
|
||||
* Must be set together with `validatorProvider`. When both are undefined,
|
||||
* falls back to `defaultProvider`/`defaultModelId`. */
|
||||
validatorModelId?: string;
|
||||
/** Default thinking effort level for AI agent sessions.
|
||||
* Controls how much reasoning effort the model uses — higher levels
|
||||
* produce better results but cost more. When undefined, the engine
|
||||
@@ -342,6 +358,10 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
includeTaskIdInCommit: true,
|
||||
defaultProvider: undefined,
|
||||
defaultModelId: undefined,
|
||||
planningProvider: undefined,
|
||||
planningModelId: undefined,
|
||||
validatorProvider: undefined,
|
||||
validatorModelId: undefined,
|
||||
defaultThinkingLevel: undefined,
|
||||
autoResolveConflicts: true,
|
||||
smartConflictResolution: true,
|
||||
|
||||
@@ -269,6 +269,12 @@ export function SettingsModal({
|
||||
const selectedValue = form.defaultProvider && form.defaultModelId
|
||||
? `${form.defaultProvider}/${form.defaultModelId}`
|
||||
: "";
|
||||
const planningValue = form.planningProvider && form.planningModelId
|
||||
? `${form.planningProvider}/${form.planningModelId}`
|
||||
: "";
|
||||
const validatorValue = form.validatorProvider && form.validatorModelId
|
||||
? `${form.validatorProvider}/${form.validatorModelId}`
|
||||
: "";
|
||||
return (
|
||||
<>
|
||||
<h4 className="settings-section-heading">Model</h4>
|
||||
@@ -279,29 +285,77 @@ export function SettingsModal({
|
||||
No models available. Configure authentication first.
|
||||
</div>
|
||||
) : (
|
||||
<div className="form-group">
|
||||
<label htmlFor="defaultModel">Default Model</label>
|
||||
<CustomModelDropdown
|
||||
id="defaultModel"
|
||||
label="Default Model"
|
||||
models={availableModels}
|
||||
value={selectedValue}
|
||||
onChange={(val) => {
|
||||
if (!val) {
|
||||
setForm((f) => ({ ...f, defaultProvider: undefined, defaultModelId: undefined }));
|
||||
} else {
|
||||
const slashIdx = val.indexOf("/");
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
defaultProvider: val.slice(0, slashIdx),
|
||||
defaultModelId: val.slice(slashIdx + 1),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
placeholder="Use default"
|
||||
/>
|
||||
<small>Select the AI model used for agent sessions. "Use default" lets the engine choose automatically.</small>
|
||||
</div>
|
||||
<>
|
||||
<div className="form-group">
|
||||
<label htmlFor="defaultModel">Default Model</label>
|
||||
<CustomModelDropdown
|
||||
id="defaultModel"
|
||||
label="Default Model"
|
||||
models={availableModels}
|
||||
value={selectedValue}
|
||||
onChange={(val) => {
|
||||
if (!val) {
|
||||
setForm((f) => ({ ...f, defaultProvider: undefined, defaultModelId: undefined }));
|
||||
} else {
|
||||
const slashIdx = val.indexOf("/");
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
defaultProvider: val.slice(0, slashIdx),
|
||||
defaultModelId: val.slice(slashIdx + 1),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
placeholder="Use default"
|
||||
/>
|
||||
<small>Default AI model used for task execution when no per-task override is set. "Use default" lets the engine choose automatically.</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="planningModel">Planning Model</label>
|
||||
<CustomModelDropdown
|
||||
id="planningModel"
|
||||
label="Planning Model"
|
||||
models={availableModels}
|
||||
value={planningValue}
|
||||
onChange={(val) => {
|
||||
if (!val) {
|
||||
setForm((f) => ({ ...f, planningProvider: undefined, planningModelId: undefined }));
|
||||
} else {
|
||||
const slashIdx = val.indexOf("/");
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
planningProvider: val.slice(0, slashIdx),
|
||||
planningModelId: val.slice(slashIdx + 1),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
placeholder="Use default"
|
||||
/>
|
||||
<small>AI model used for task planning and specification (triage). Falls back to Default Model when not set.</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="validatorModel">Validator Model</label>
|
||||
<CustomModelDropdown
|
||||
id="validatorModel"
|
||||
label="Validator Model"
|
||||
models={availableModels}
|
||||
value={validatorValue}
|
||||
onChange={(val) => {
|
||||
if (!val) {
|
||||
setForm((f) => ({ ...f, validatorProvider: undefined, validatorModelId: undefined }));
|
||||
} else {
|
||||
const slashIdx = val.indexOf("/");
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
validatorProvider: val.slice(0, slashIdx),
|
||||
validatorModelId: val.slice(slashIdx + 1),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
placeholder="Use default"
|
||||
/>
|
||||
<small>AI model used for code and specification review. Falls back to Default Model when not set.</small>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{(() => {
|
||||
const selectedModel = availableModels.find(
|
||||
|
||||
@@ -851,8 +851,9 @@ export class TaskExecutor {
|
||||
defaultProvider: settings.defaultProvider,
|
||||
defaultModelId: settings.defaultModelId,
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
validatorModelProvider: detail.validatorModelProvider,
|
||||
validatorModelId: detail.validatorModelId,
|
||||
// Per-task validator overrides take precedence over global validator settings
|
||||
validatorModelProvider: detail.validatorModelProvider ?? settings.validatorProvider,
|
||||
validatorModelId: detail.validatorModelId ?? settings.validatorModelId,
|
||||
store,
|
||||
taskId,
|
||||
},
|
||||
|
||||
@@ -465,8 +465,13 @@ export class TriageProcessor {
|
||||
onThinking: agentLogger.onThinking,
|
||||
onToolStart: agentLogger.onToolStart,
|
||||
onToolEnd: agentLogger.onToolEnd,
|
||||
defaultProvider: settings.defaultProvider,
|
||||
defaultModelId: settings.defaultModelId,
|
||||
// Use planning model settings if both provider and modelId are set, otherwise fall back to defaults
|
||||
defaultProvider: settings.planningProvider && settings.planningModelId
|
||||
? settings.planningProvider
|
||||
: settings.defaultProvider,
|
||||
defaultModelId: settings.planningProvider && settings.planningModelId
|
||||
? settings.planningModelId
|
||||
: settings.defaultModelId,
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
});
|
||||
|
||||
@@ -846,6 +851,8 @@ export class TriageProcessor {
|
||||
defaultProvider?: string;
|
||||
defaultModelId?: string;
|
||||
defaultThinkingLevel?: string;
|
||||
validatorProvider?: string;
|
||||
validatorModelId?: string;
|
||||
},
|
||||
): ToolDefinition {
|
||||
const store = this.store;
|
||||
@@ -904,6 +911,8 @@ export class TriageProcessor {
|
||||
onText: (delta) => options.onAgentText?.(taskId, delta),
|
||||
defaultProvider: settings.defaultProvider,
|
||||
defaultModelId: settings.defaultModelId,
|
||||
validatorModelProvider: settings.validatorProvider,
|
||||
validatorModelId: settings.validatorModelId,
|
||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||
store,
|
||||
taskId,
|
||||
|
||||
Reference in New Issue
Block a user