diff --git a/libs/room/src/ai.rs b/libs/room/src/ai.rs index 2905196..33f9163 100644 --- a/libs/room/src/ai.rs +++ b/libs/room/src/ai.rs @@ -16,21 +16,38 @@ impl RoomService { let user_id = ctx.user_id; self.require_room_member(room_id, user_id).await?; - let models = room_ai::Entity::find() + let configs = room_ai::Entity::find() .filter(room_ai::Column::Room.eq(room_id)) .all(&self.db) .await?; - let mut responses = Vec::with_capacity(models.len()); - for model in models { - let model_name = ai_model::Entity::find_by_id(model.model) - .one(&self.db) - .await - .ok() - .flatten() - .map(|m| m.name) - .unwrap_or_else(|| format!("AI {}", model.model)); - let mut resp = super::RoomAiResponse::from(model); + if configs.is_empty() { + return Ok(Vec::new()); + } + + // Batch-fetch all referenced models to avoid N+1 queries + let model_ids: Vec = configs.iter().map(|c| c.model).collect(); + let models = ai_model::Entity::find() + .filter(ai_model::Column::Id.is_in(model_ids)) + .all(&self.db) + .await?; + + let model_names: std::collections::HashMap = models + .into_iter() + .map(|m| (m.id, m.name)) + .collect(); + + let mut responses = Vec::with_capacity(configs.len()); + for config in configs { + // Skip entries where model_name cannot be resolved — never expose UID + let Some(model_name) = model_names.get(&config.model).cloned() else { + tracing::warn!( + "room_ai_list: skipping config with unknown model_id={}", + config.model + ); + continue; + }; + let mut resp = super::RoomAiResponse::from(config); resp.model_name = Some(model_name); responses.push(resp); } @@ -114,10 +131,9 @@ impl RoomService { .await .ok() .flatten() - .map(|m| m.name) - .unwrap_or_else(|| format!("AI {}", saved.model)); + .map(|m| m.name); let mut resp = super::RoomAiResponse::from(saved); - resp.model_name = Some(model_name); + resp.model_name = model_name; if let Ok(room) = self.find_room_or_404(room_id).await { self.publish_room_event( diff --git a/src/components/room/DiscordMemberList.tsx b/src/components/room/DiscordMemberList.tsx index f9491ac..e8ecd36 100644 --- a/src/components/room/DiscordMemberList.tsx +++ b/src/components/room/DiscordMemberList.tsx @@ -170,11 +170,8 @@ export const DiscordMemberList = memo(function DiscordMemberList({ title={`AI — ${aiConfigs.length}`} icon={} > - {aiConfigs.map((ai) => { - // Fallback: try modelName, then short model ID (no provider prefix), then 'AI' - const label = ai.modelName - || ai.model?.split('/').pop() - || 'AI'; + {aiConfigs.filter((ai) => !!ai.modelName).map((ai) => { + const label = ai.modelName!; return (