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.
How It Works
Section titled “How It Works”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.
AiUsageFeature Enum
Section titled “AiUsageFeature Enum”Each variant identifies a distinct AI call site:
| Variant | Call Site |
|---|---|
EnrichContact | Contact enrichment from call data |
EnrichContactFromMessages | Contact enrichment from SMS history |
SummarizeCall | Call summary note generation |
UpdateContactMemory | Contact memory updates |
AnalyzeCall | Call analysis pipeline |
IdentifySpeakers | Speaker diarization labeling |
AutoTagContact | Automatic contact tagging |
ExtractTodos | Todo extraction from calls |
ExecuteTodo | AI-powered todo execution |
QueryKnowledge | Knowledge base queries (voice agent) |
GenerateInstructions | AI builder — generate mode |
EditInstructions | AI builder — edit mode |
CustomEditInstructions | AI builder — custom edit mode |
Transcription | Standard audio transcription |
DiarizedTranscription | Speaker-diarized transcription |
TextAgentSuggestions | Text agent reply suggestions |
RealtimeTurn | Realtime voice agent turn |
CreateAutonomousCampaign | Autonomous campaign creation |
CreateAutonomousCampaignFromSms | Campaign creation from SMS |
ExecuteAutonomousCampaign | Autonomous campaign execution |
Each variant serializes to a snake_case string via as_str() for database storage.
AiUsageEntry
Section titled “AiUsageEntry”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:
| Method | Usage Type | Use Case |
|---|---|---|
from_text_generation() | text_generation | aisdk/OpenRouter calls — takes &Usage from the response |
from_transcription() | transcription | OpenAI Whisper calls — takes audio duration and token counts |
from_realtime_turn() | realtime | Voice agent turns — takes a RealtimeUsageTick |
Database Schema
Section titled “Database Schema”The ai_usage_log table stores one row per AI call:
| Column | Type | Notes |
|---|---|---|
id | UUID | Primary key |
organization_id | UUID | FK → organization.id (CASCADE) |
call_id | UUID | FK → call.id (SET NULL), nullable |
feature | TEXT | AiUsageFeature::as_str() value |
provider | TEXT | Provider name |
model | TEXT | Full model identifier |
usage_type | TEXT | text_generation, transcription, or realtime |
input_tokens | INTEGER | Nullable |
output_tokens | INTEGER | Nullable |
cached_tokens | INTEGER | Nullable |
reasoning_tokens | INTEGER | Nullable |
input_audio_tokens | INTEGER | Nullable |
output_audio_tokens | INTEGER | Nullable |
audio_duration_secs | DOUBLE | Nullable, for transcription |
created_at | TIMESTAMPTZ | Auto-set to now() |
Indexes:
idx_ai_usage_log_org_created_at— composite on(organization_id, created_at)for org-level usage queriesidx_ai_usage_log_call_id— partial index oncall_id WHERE call_id IS NOT NULL
Adding Usage Logging to a New Call Site
Section titled “Adding Usage Logging to a New Call Site”- Add a variant to
AiUsageFeatureinsrc/mods/ai/types/ai_usage_type.rs - Add the
as_str()mapping - After the AI call, construct an
AiUsageEntryusing the appropriate factory method - Call
spawn_log_ai_usage(entry)
Key Files
Section titled “Key Files”| File | Purpose |
|---|---|
src/mods/ai/types/ai_usage_type.rs | AiUsageFeature enum and AiUsageEntry struct |
src/mods/ai/services/log_ai_usage_service.rs | spawn_log_ai_usage() function |
migration/src/m20260308_000000_create_ai_usage_log_table.rs | Table migration |