Skip to content

AI Model Configuration

Loquent uses 17 distinct AI operations (areas), each backed by an OpenRouter model. Super admins can override the default model for any area through the admin panel. Changes take effect immediately — no restart needed.

Service needs AI → resolve_model(AiArea::ExtractTodos)
→ Query ai_model_config for "extract_todos"
→ Override exists? Use it. No row? Use hardcoded default.
→ Build OpenRouter request with resolved model ID

The ai_model_config table stores overrides only. No row means the area uses its hardcoded default. Resetting an area to its default deletes the override row.

ColumnTypeNotes
idUUIDPrimary key
areaTextUnique — maps to AiArea::key() (e.g., "extract_todos")
model_idTextOpenRouter model ID (e.g., "google/gemini-2.0-flash-001")
updated_byUUIDFK to user.id, cascade delete
updated_atTimestamptz

Migration: m20260320_130000_create_ai_model_config_table.rs

Each area has a stable key, display label, default model, and complexity tier.

AreaKeyTier
Auto Tag Contactauto_tag_contactSimple
Identify Speakersidentify_speakersSimple
Knowledge Queryknowledge_querySimple
Summarize Callsummarize_callMedium
Update System Noteupdate_system_noteMedium
Enrich Contactenrich_contactMedium
Enrich Contact From Messagesenrich_contact_from_messagesMedium
Extract Todosextract_todosMedium
Analyze Callanalyze_callMedium
Execute Todoexecute_todoComplex
Generate Instructionsgenerate_instructionsComplex
Edit Instructionsedit_instructionsComplex
Custom Edit Instructionscustom_edit_instructionsComplex
Execute Planexecute_planComplex
Execute Plan (Fallback)execute_plan_fallbackComplex
Assess Plan Template Start Conditionassess_plan_template_start_conditionSimple
Instantiate Planinstantiate_planPlan Creator

The AiArea enum lives in src/mods/ai/types/ai_models_type.rs. Each variant exposes key(), label(), default_model(), and tier(). Plan creation uses a two-step flow: a cheap assessment model checks start-condition relevance, then the full plan creator model generates the title and description (see Plan — AI model split).

Call resolve_model() anywhere a service needs an AI model:

use crate::mods::ai::{resolve_model, AiArea};
let model_name = resolve_model(AiArea::ExtractTodos).await?;
let model = Openrouter::<DynamicModel>::builder()
.api_key(openrouter_conf.openrouter_api_key)
.model_name(&model_name)
.build()?;

File: src/mods/ai/services/resolve_model_service.rs

Fallback behavior: If the ai_model_config table doesn’t exist yet (migration not run), resolve_model() logs a warning and returns the hardcoded default. AI operations never break due to missing config.

The plan executor uses two areas (ExecutePlan and ExecutePlanFallback) for automatic retry with a different model:

let models = [
resolve_model(AiArea::ExecutePlan).await?,
resolve_model(AiArea::ExecutePlanFallback).await?,
];
for model_name in &models {
match build_and_call(model_name).await {
Ok(response) => break,
Err(_) => continue, // try fallback
}
}

All endpoints require super admin access.

MethodPathPurpose
GET/api/admin/ai-model-configsList all 17 areas with current model
PUT/api/admin/ai-model-configUpdate one area’s model
GET/api/admin/available-modelsFetch live model list from OpenRouter
{
"area": "extract_todos",
"model_id": "anthropic/claude-sonnet-4-20250514"
}

Setting model_id to the area’s default model deletes the override row (reset to default).

struct AiModelConfigEntry {
area: String, // DB key
label: String, // UI label
tier: String, // "Simple" | "Medium" | "Complex" | "Plan Creator"
model_id: String, // Current model (override or default)
default_model_id: String, // Hardcoded default
is_default: bool, // true if no override exists
updated_by: Option<String>,
updated_at: Option<String>,
}

Navigate to Admin → AI Models to configure models. The tab groups areas by tier, with each row showing:

  • Area label and status badge (Default or Custom)
  • Searchable model dropdown (fetched live from OpenRouter)
  • Reset button (visible only for overridden areas)
  • Save status indicator and last-updated metadata

Component: src/mods/admin/components/admin_ai_models_tab_component.rs

Every model change records an audit entry:

ActionWhen
config.ai_model.updateModel changed to a non-default value
config.ai_model.resetModel reset to its default

Entries include the old and new model IDs in the summary, and the area key as a tag. View them in the admin System tab’s audit log.

FilePurpose
src/mods/ai/types/ai_models_type.rsAiArea enum with 17 variants
src/mods/ai/services/resolve_model_service.rsresolve_model() — DB lookup with fallback
src/mods/admin/services/admin_ai_model_config_service.rsGet/update config, audit logging
src/mods/admin/api/get_ai_model_configs_api.rsList all area configs
src/mods/admin/api/update_ai_model_config_api.rsUpdate one area
src/mods/admin/api/get_available_models_api.rsFetch OpenRouter model list
src/mods/admin/components/admin_ai_models_tab_component.rsAdmin panel UI
migration/src/m20260320_130000_create_ai_model_config_table.rsTable migration