Skip to content

AI Usage Logging

Loquent logs every AI interaction — text generation, transcription, and realtime voice turns — to the ai_usage_log table. Logging is fire-and-forget: it never blocks or fails the main flow.

After each AI call completes, you call spawn_log_ai_usage() with an AiUsageEntry. This spawns a Tokio task that inserts a row into ai_usage_log asynchronously. If the insert fails, it logs a warning and moves on.

use crate::mods::ai::{spawn_log_ai_usage, AiUsageEntry, AiUsageFeature};
let response = prompt.generate_text().await?;
spawn_log_ai_usage(AiUsageEntry::from_text_generation(
organization_id,
Some(call_id),
AiUsageFeature::EnrichContact,
AiModels::ENRICH_CONTACT,
&response.usage(),
));

The caller continues immediately — spawn_log_ai_usage returns () and never errors.

Each variant identifies a distinct AI call site:

VariantCall Site
EnrichContactContact enrichment from call data
EnrichContactFromMessagesContact enrichment from SMS history
SummarizeCallCall summary note generation
UpdateContactMemoryContact memory updates
AnalyzeCallCall analysis pipeline
IdentifySpeakersSpeaker diarization labeling
AutoTagContactAutomatic contact tagging
ExtractTodosTodo extraction from calls
ExecuteTodoAI-powered todo execution
QueryKnowledgeKnowledge base queries (voice agent)
GenerateInstructionsAI builder — generate mode
EditInstructionsAI builder — edit mode
CustomEditInstructionsAI builder — custom edit mode
TranscriptionStandard audio transcription
DiarizedTranscriptionSpeaker-diarized transcription
TextAgentSuggestionsText agent reply suggestions
RealtimeTurnRealtime voice agent turn
CreateAutonomousCampaignAutonomous campaign creation
CreateAutonomousCampaignFromSmsCampaign creation from SMS
ExecuteAutonomousCampaignAutonomous campaign execution

Each variant serializes to a snake_case string via as_str() for database storage.

The entry struct captures all usage metadata for a single AI call:

pub struct AiUsageEntry {
pub organization_id: Uuid,
pub call_id: Option<Uuid>,
pub feature: AiUsageFeature,
pub provider: String, // e.g. "openai", "google"
pub model: String, // OpenRouter model ID
pub usage_type: &'static str, // "text_generation", "transcription", "realtime"
pub input_tokens: Option<i32>,
pub output_tokens: Option<i32>,
pub cached_tokens: Option<i32>,
pub reasoning_tokens: Option<i32>,
pub input_audio_tokens: Option<i32>,
pub output_audio_tokens: Option<i32>,
pub audio_duration_secs: Option<f64>,
}

Three factory methods handle the different usage types:

MethodUsage TypeUse Case
from_text_generation()text_generationaisdk/OpenRouter calls — takes &Usage from the response
from_transcription()transcriptionOpenAI Whisper calls — takes audio duration and token counts
from_realtime_turn()realtimeVoice agent turns — takes a RealtimeUsageTick

The ai_usage_log table stores one row per AI call:

ColumnTypeNotes
idUUIDPrimary key
organization_idUUIDFK → organization.id (CASCADE)
call_idUUIDFK → call.id (SET NULL), nullable
featureTEXTAiUsageFeature::as_str() value
providerTEXTProvider name
modelTEXTFull model identifier
usage_typeTEXTtext_generation, transcription, or realtime
input_tokensINTEGERNullable
output_tokensINTEGERNullable
cached_tokensINTEGERNullable
reasoning_tokensINTEGERNullable
input_audio_tokensINTEGERNullable
output_audio_tokensINTEGERNullable
audio_duration_secsDOUBLENullable, for transcription
created_atTIMESTAMPTZAuto-set to now()

Indexes:

  • idx_ai_usage_log_org_created_at — composite on (organization_id, created_at) for org-level usage queries
  • idx_ai_usage_log_call_id — partial index on call_id WHERE call_id IS NOT NULL
  1. Add a variant to AiUsageFeature in src/mods/ai/types/ai_usage_type.rs
  2. Add the as_str() mapping
  3. After the AI call, construct an AiUsageEntry using the appropriate factory method
  4. Call spawn_log_ai_usage(entry)
FilePurpose
src/mods/ai/types/ai_usage_type.rsAiUsageFeature enum and AiUsageEntry struct
src/mods/ai/services/log_ai_usage_service.rsspawn_log_ai_usage() function
migration/src/m20260308_000000_create_ai_usage_log_table.rsTable migration