- Save thinking_content as {"__chunks__": [{type, content}]} for replay
- Tool call sanitization — don't expose raw results to frontend
- Billing record_ai_usage integration
- Room service module refactoring into service/ directory
50 lines
1.5 KiB
Rust
50 lines
1.5 KiB
Rust
use db::cache::AppCache;
|
|
use db::database::AppDatabase;
|
|
use models::rooms::room_message::{Column as RmCol, Entity as RoomMessage};
|
|
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
|
|
use uuid::Uuid;
|
|
|
|
use crate::error::RoomError;
|
|
|
|
pub async fn next_room_message_seq_internal(
|
|
room_id: Uuid,
|
|
db: &AppDatabase,
|
|
cache: &AppCache,
|
|
) -> Result<i64, RoomError> {
|
|
let seq_key = format!("room:seq:{}", room_id);
|
|
let mut conn = cache.conn().await.map_err(|e| {
|
|
RoomError::Internal(format!("failed to get redis connection for seq: {}", e))
|
|
})?;
|
|
|
|
let seq: i64 = redis::cmd("INCR")
|
|
.arg(&seq_key)
|
|
.query_async(&mut conn)
|
|
.await
|
|
.map_err(|e| RoomError::Internal(format!("seq INCR: {}", e)))?;
|
|
|
|
// DB reconciliation: only check every 1000 messages
|
|
if seq % 1000 == 0 {
|
|
let db_seq: Option<Option<Option<i64>>> = RoomMessage::find()
|
|
.filter(RmCol::Room.eq(room_id))
|
|
.select_only()
|
|
.column_as(RmCol::Seq.max(), "max_seq")
|
|
.into_tuple::<Option<Option<i64>>>()
|
|
.one(db)
|
|
.await?
|
|
.map(|r| r);
|
|
let db_seq = db_seq.flatten().flatten().unwrap_or(0);
|
|
|
|
if db_seq >= seq {
|
|
let _: String = redis::cmd("SET")
|
|
.arg(&seq_key)
|
|
.arg(db_seq + 1)
|
|
.query_async(&mut conn)
|
|
.await
|
|
.map_err(|e| RoomError::Internal(format!("seq SET: {}", e)))?;
|
|
return Ok(db_seq + 1);
|
|
}
|
|
}
|
|
|
|
Ok(seq)
|
|
}
|