Skip to content

Template Model Override

Plan templates can specify a custom AI model for plan instantiation. This lets you use cheaper models for simple tasks and reserve powerful models for complex workflows — without changing the system default.

Each PlanTemplate has an optional model_override field. When a plan is instantiated from that template, the override takes precedence over the system default resolved from AiArea::InstantiatePlan.

Template with model_override = Some("deepseek/deepseek-v3.2")
→ Plan instantiation uses deepseek/deepseek-v3.2
Template with model_override = None
→ Plan instantiation uses system default (from ai_model table)

Start condition assessment is not affected by the override. It always uses the system model for AiArea::AssessPlanTemplateStartCondition — a deliberate choice to keep the pre-filter fast and cheap.

The model_override column is a nullable VARCHAR on the plan_template table, added in migration m20260327_113254_plan_template_add_model_override.

pub struct PlanTemplate {
// ... existing fields ...
pub model_override: Option<String>, // OpenRouter model ID
}

The create/update DTO mirrors this:

pub struct PlanTemplateData {
// ... existing fields ...
pub model_override: Option<String>,
}
GET /api/plan-templates/available-models

Returns the list of models available on OpenRouter. Requires PlanTemplateCollectionPermission::List.

Response:

{
"models": [
{ "id": "anthropic/claude-opus-4.6", "name": "Claude Opus 4.6" },
{ "id": "deepseek/deepseek-v3.2", "name": "DeepSeek V3.2" }
]
}

Models are sorted alphabetically by name. The endpoint delegates to a shared fetch_available_models() service in src/mods/ai/services/fetch_available_models_service.rs.

Both POST /api/plan-templates and PUT /api/plan-templates/{id} accept model_override in the request body. Pass null or omit the field to use the system default.

POST /api/ai-builder/plan-template/generate

The AI builder now returns a suggested_model field based on task complexity:

{
"description": "...",
"suggested_action_ids": ["..."],
"suggested_model": "anthropic/claude-sonnet-4.6"
}

The suggestion follows these tiers:

ComplexityModelUse case
Simpledeepseek/deepseek-v3.2Single-step: confirmations, reminders
Mediumgoogle/gemini-3.1-pro-previewMulti-step: follow-ups, standard outreach
Complexanthropic/claude-sonnet-4.6Nuanced: sales, negotiations
Most complexanthropic/claude-opus-4.6Deep reasoning: complex workflows

When create_plan_from_template runs, it resolves the model like this:

let model_name = match &template.model_override {
Some(m) if !m.is_empty() => m.clone(),
_ => resolve_model(AiArea::InstantiatePlan).await?,
};

The same logic applies in create_plans_from_call and create_plans_from_sms. The override only affects plan instantiation — the assessment phase and plan execution continue using their own model resolution paths.

The Create Plan Template and Edit Plan Template forms include a searchable model dropdown after the sender phone field. The dropdown shows “System Default” as the first option, followed by all available OpenRouter models.

When the AI builder generates a template, it pre-fills the model selector with its suggestion. Users can accept, change, or clear the selection.

Template cards display a purple Custom model badge when an override is set.

AreaFile
Migrationmigration/src/m20260327_113254_plan_template_add_model_override.rs
Typessrc/mods/plan/types/plan_template_type.rs, plan_template_data_type.rs
Available models endpointsrc/mods/plan/api/get_available_plan_models_api.rs
Shared model fetchsrc/mods/ai/services/fetch_available_models_service.rs
Instantiation logicsrc/mods/plan/services/create_plan_from_template_service.rs
AI suggestionsrc/shared/ai_builder/api/plan_template_generate_api.rs
UI componentssrc/mods/plan/components/create_plan_template_component.rs