feat(room): inject repository details into AI system prompt on mention

When a user mentions a repository in room chat, extract the repo name
from @[repo:name:label] brackets, look up the full repo model from the
database, and inject its details (name, description, default branch,
visibility) into the AI message context. Works independently of
embed_service availability.
This commit is contained in:
ZhenYi 2026-04-26 23:58:52 +08:00
parent d72019e39f
commit adbc0705db
3 changed files with 76 additions and 36 deletions

View File

@ -605,19 +605,37 @@ impl ChatService {
} }
} }
if let Some(embed_service) = &self.embed_service {
for mention in &request.mention { for mention in &request.mention {
match mention { match mention {
Mention::Repo(repo) => { Mention::Repo(repo) => {
// Inject repo details into system prompt so AI knows the repo context
let mut parts = vec![
format!("Name: {}", repo.repo_name),
format!("ID: {}", repo.id),
];
if let Some(ref desc) = repo.description {
parts.push(format!("Description: {}", desc));
}
parts.push(format!("Default branch: {}", repo.default_branch));
parts.push(format!("Private: {}", if repo.is_private { "yes" } else { "no" }));
parts.push(format!("Created: {}", repo.created_at.format("%Y-%m-%d")));
messages.push(ChatRequestMessage::system(format!(
"Mentioned repository:\n{}",
parts.join("\n")
)));
// Vector search for related issues and repos (enhancement, optional)
if let Some(embed_service) = &self.embed_service {
let query = format!( let query = format!(
"{} {}", "{} {}",
repo.repo_name, repo.repo_name,
repo.description.as_deref().unwrap_or_default() repo.description.as_deref().unwrap_or_default()
); );
match embed_service.search_issues(&query, 5).await { if let Ok(issues) = embed_service.search_issues(&query, 5).await {
Ok(issues) if !issues.is_empty() => { if !issues.is_empty() {
let context = format!( let context = format!(
"Related issues:\n{}", "Related issues for repo {}:\n{}",
repo.repo_name,
issues issues
.iter() .iter()
.map(|i| format!("- {}", i.payload.text)) .map(|i| format!("- {}", i.payload.text))
@ -626,15 +644,11 @@ impl ChatService {
); );
messages.push(ChatRequestMessage::system(context)); messages.push(ChatRequestMessage::system(context));
} }
Err(e) => {
let _ = e;
} }
_ => {} if let Ok(repos) = embed_service.search_repos(&query, 3).await {
} if !repos.is_empty() {
match embed_service.search_repos(&query, 3).await {
Ok(repos) if !repos.is_empty() => {
let context = format!( let context = format!(
"Related repositories:\n{}", "Similar repositories:\n{}",
repos repos
.iter() .iter()
.map(|r| format!("- {}", r.payload.text)) .map(|r| format!("- {}", r.payload.text))
@ -643,10 +657,7 @@ impl ChatService {
); );
messages.push(ChatRequestMessage::system(context)); messages.push(ChatRequestMessage::system(context));
} }
Err(e) => {
let _ = e;
} }
_ => {}
} }
} }
Mention::User(user) => { Mention::User(user) => {
@ -667,7 +678,6 @@ impl ChatService {
} }
} }
} }
}
let skill_contexts = self.build_skill_context(request).await; let skill_contexts = self.build_skill_context(request).await;
for ctx in skill_contexts { for ctx in skill_contexts {

View File

@ -1,9 +1,11 @@
use db::database::AppDatabase; use db::database::AppDatabase;
use models::repos::repo;
use models::rooms::room_ai; use models::rooms::room_ai;
use models::rooms::room_message::{Column as RmCol, Entity as RoomMessage}; use models::rooms::room_message::{Column as RmCol, Entity as RoomMessage};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
use uuid::Uuid; use uuid::Uuid;
use super::patterns::mention_bracket_re;
use crate::error::RoomError; use crate::error::RoomError;
pub async fn get_room_history( pub async fn get_room_history(
@ -57,6 +59,34 @@ pub async fn get_room_ai_config(
Ok(ai_config) Ok(ai_config)
} }
pub async fn extract_mention_context(_content: &str) -> Vec<agent::chat::Mention> { pub async fn extract_mention_context(
Vec::new() db: &AppDatabase,
project_id: Uuid,
content: &str,
) -> Vec<agent::chat::Mention> {
let mut mentions: Vec<agent::chat::Mention> = Vec::new();
let mut seen_repos: Vec<String> = Vec::new();
for cap in mention_bracket_re().captures_iter(content) {
if let (Some(type_m), Some(id_m)) = (cap.get(1), cap.get(2)) {
if type_m.as_str() == "repo" {
let repo_name = id_m.as_str().trim().to_string();
if repo_name.is_empty() || seen_repos.contains(&repo_name) {
continue;
}
seen_repos.push(repo_name.clone());
if let Ok(Some(repo_model)) = repo::Entity::find()
.filter(repo::Column::Project.eq(project_id))
.filter(repo::Column::RepoName.eq(&repo_name))
.one(db)
.await
{
mentions.push(agent::chat::Mention::Repo(repo_model));
}
}
}
}
mentions
} }

View File

@ -374,7 +374,7 @@ impl RoomService {
.collect(); .collect();
let user_names = self.get_user_names(&user_ids).await; let user_names = self.get_user_names(&user_ids).await;
let mentions = history::extract_mention_context(&content).await; let mentions = history::extract_mention_context(&self.db, room.project, &content).await;
let request = AiRequest { let request = AiRequest {
db: self.db.clone(), db: self.db.clone(),