diff --git a/libs/room/src/service/ai_nonstreaming.rs b/libs/room/src/service/ai_nonstreaming.rs index 089c757..e7b59eb 100644 --- a/libs/room/src/service/ai_nonstreaming.rs +++ b/libs/room/src/service/ai_nonstreaming.rs @@ -62,15 +62,7 @@ pub async fn process_message_ai_nonstreaming( tracing::warn!(error = %e, "Failed to update room_ai call stats"); } - // Record billing (non-fatal) - let _ = super::billing::record_ai_usage( - &db, - project_id, - model_id, - result.input_tokens, - result.output_tokens, - ) - .await; + // Billing handled internally by chat_service.process via record_ai_session } } Err(e) => { diff --git a/libs/room/src/service/ai_react_nonstreaming.rs b/libs/room/src/service/ai_react_nonstreaming.rs index f117522..53d5d4b 100644 --- a/libs/room/src/service/ai_react_nonstreaming.rs +++ b/libs/room/src/service/ai_react_nonstreaming.rs @@ -50,6 +50,7 @@ pub async fn process_message_ai_react_nonstreaming( { tracing::error!(error = %e, "Failed to create ReAct AI message"); } else { + // Billing handled internally by chat_service.process_react via record_ai_session let now = Utc::now(); if let Err(e) = room_ai::Entity::update_many() .col_expr( @@ -64,19 +65,6 @@ pub async fn process_message_ai_react_nonstreaming( { tracing::warn!(error = %e, "Failed to update room_ai call stats"); } - - // Record billing (non-fatal) - if let Err(e) = super::billing::record_ai_usage( - &db, - project_id, - model_id, - _input_tokens, - _output_tokens, - ) - .await - { - tracing::warn!(error = %e, "AI billing recording failed"); - } } } Err(e) => { diff --git a/libs/room/src/service/ai_react_streaming.rs b/libs/room/src/service/ai_react_streaming.rs index 3fa9e98..535bacf 100644 --- a/libs/room/src/service/ai_react_streaming.rs +++ b/libs/room/src/service/ai_react_streaming.rs @@ -155,7 +155,7 @@ pub async fn process_message_ai_react_streaming( }) .await; - let (final_content, input_tokens, output_tokens, err_msg, _should_log) = match result { + let (final_content, _input_tokens, _output_tokens, err_msg, _should_log) = match result { Ok((content, input, output)) => (content, input, output, None, false), Err(e) => { let msg = format!("[Agent error: {}]", e); @@ -250,19 +250,7 @@ pub async fn process_message_ai_react_streaming( tracing::warn!(error = %e, "Failed to update room_ai call stats"); } - // Record billing (non-fatal) - if let Err(e) = super::billing::record_ai_usage( - &db, - project_id_inner, - model_id, - input_tokens, - output_tokens, - ) - .await - { - tracing::warn!(error = %e, "AI billing recording failed"); - } - + // Billing handled internally by chat_service.process_react via record_ai_session let msg_event = queue::RoomMessageEvent { id: streaming_msg_id, room_id: room_id_inner, diff --git a/libs/room/src/service/ai_streaming.rs b/libs/room/src/service/ai_streaming.rs index 738fe0a..74bfdb2 100644 --- a/libs/room/src/service/ai_streaming.rs +++ b/libs/room/src/service/ai_streaming.rs @@ -190,18 +190,7 @@ pub async fn process_message_ai_streaming( tracing::warn!(error = %e, "Failed to update room_ai call stats"); } - // Record billing (non-fatal) - if let Err(e) = super::billing::record_ai_usage( - &db, - project_id_inner, - model_id, - result.input_tokens, - result.output_tokens, - ) - .await - { - tracing::warn!(error = %e, "AI billing recording failed"); - } + // Billing handled internally by chat_service.process_stream via record_ai_session let msg_event = queue::RoomMessageEvent { id: streaming_msg_id, diff --git a/libs/room/src/service/billing.rs b/libs/room/src/service/billing.rs deleted file mode 100644 index 6823ddb..0000000 --- a/libs/room/src/service/billing.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! AI usage billing helper for room service. -//! -//! Delegates to `agent::billing::record_ai_usage`. -//! Billing is non-fatal — failures are logged but do not block AI responses. - -use db::database::AppDatabase; -use uuid::Uuid; - -use crate::error::RoomError; - -/// Record AI token usage against a project's billing balance. -/// -/// Returns `Ok(())` on success. On billing failure (e.g. insufficient balance, -/// missing pricing), returns `Err` but the caller should still complete the AI -/// request — billing is a non-critical side-effect. -pub async fn record_ai_usage( - db: &AppDatabase, - project_id: Uuid, - model_id: Uuid, - input_tokens: i64, - output_tokens: i64, -) -> Result<(), RoomError> { - if input_tokens == 0 && output_tokens == 0 { - return Ok(()); - } - - match agent::billing::record_ai_usage(db, project_id, model_id, input_tokens, output_tokens).await { - Ok(record) => { - tracing::info!( - project_id = %project_id, - model_id = %model_id, - input_tokens = input_tokens, - output_tokens = output_tokens, - cost_usd = %record.cost, - "ai_usage_recorded" - ); - Ok(()) - } - Err(e) => { - tracing::warn!( - project_id = %project_id, - model_id = %model_id, - input_tokens = input_tokens, - output_tokens = output_tokens, - error = %e, - "ai_billing_failed_non_fatal" - ); - Err(e.into()) - } - } -}