feat(service): trigger Qdrant embedding on issue/repo/skill creation
- After issue_create: spawn embed_issue_chunked (non-blocking) - After skill_create/update: spawn embed_skill - After repo create/update in fctool: spawn embed_repo - Wire EmbedService through AppService, available for all triggers
This commit is contained in:
parent
93ec515f29
commit
62727a93a1
@ -169,6 +169,19 @@ pub async fn create_repo_exec(
|
||||
git2::Repository::init_bare(&repo_dir)
|
||||
.map_err(|e| ToolError::ExecutionError(format!("Failed to init bare repo: {}", e)))?;
|
||||
|
||||
// Embed repo into Qdrant for semantic search (non-blocking)
|
||||
if let Some(embed) = ctx.embed_service() {
|
||||
let es = embed.clone();
|
||||
let repo_id = model.id.to_string();
|
||||
let repo_name = model.repo_name.clone();
|
||||
let repo_desc = model.description.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = es.embed_repo(&repo_id, &repo_name, repo_desc.as_deref()).await {
|
||||
tracing::warn!(error = %e, repo_id = %repo_id, "failed to embed repo");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"id": model.id.to_string(),
|
||||
"name": model.repo_name,
|
||||
@ -256,6 +269,19 @@ pub async fn update_repo_exec(
|
||||
.await
|
||||
.map_err(|e| ToolError::ExecutionError(e.to_string()))?;
|
||||
|
||||
// Re-embed repo on update (non-blocking)
|
||||
if let Some(embed) = ctx.embed_service() {
|
||||
let es = embed.clone();
|
||||
let repo_id = model.id.to_string();
|
||||
let repo_name = model.repo_name.clone();
|
||||
let repo_desc = model.description.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = es.embed_repo(&repo_id, &repo_name, repo_desc.as_deref()).await {
|
||||
tracing::warn!(error = %e, repo_id = %repo_id, "failed to re-embed repo on update");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"id": model.id.to_string(),
|
||||
"name": model.repo_name,
|
||||
|
||||
@ -292,6 +292,19 @@ impl AppService {
|
||||
let _ = this.triage_issue(project_name_clone, issue_number).await;
|
||||
});
|
||||
|
||||
// Embed issue into Qdrant for semantic search (non-blocking)
|
||||
if let Some(ref embed) = self.embed_service {
|
||||
let issue_id = model.id.to_string();
|
||||
let issue_title = model.title.clone();
|
||||
let issue_body = model.body.clone();
|
||||
let es = embed.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = es.embed_issue_chunked(&issue_id, &issue_title, issue_body.as_deref()).await {
|
||||
tracing::warn!(error = %e, issue_id = %issue_id, "failed to embed issue");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(IssueResponse::from(model))
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ pub struct AppService {
|
||||
pub queue_producer: MessageProducer,
|
||||
pub storage: Option<AppStorage>,
|
||||
pub push: Option<WebPushService>,
|
||||
pub embed_service: Option<Arc<EmbedService>>,
|
||||
}
|
||||
|
||||
impl AppService {
|
||||
@ -185,6 +186,8 @@ impl AppService {
|
||||
}
|
||||
};
|
||||
|
||||
let embed_service_for_app = embed_service.clone();
|
||||
|
||||
// Build ChatService if AI is configured; otherwise AI chat is disabled (graceful degradation)
|
||||
let chat_service: Option<Arc<ChatService>> =
|
||||
match (config.ai_api_key(), config.ai_basic_url()) {
|
||||
@ -276,6 +279,7 @@ impl AppService {
|
||||
queue_producer: message_producer,
|
||||
storage,
|
||||
push,
|
||||
embed_service: embed_service_for_app,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -104,6 +104,22 @@ impl AppService {
|
||||
};
|
||||
|
||||
let inserted = active.insert(&self.db).await?;
|
||||
|
||||
// Embed skill into Qdrant (non-blocking)
|
||||
if let Some(ref embed) = self.embed_service {
|
||||
let es = embed.clone();
|
||||
let sid = inserted.id;
|
||||
let sname = inserted.name.clone();
|
||||
let sdesc = inserted.description.clone();
|
||||
let scontent = inserted.content.clone();
|
||||
let sproj = inserted.project_uuid.to_string();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = es.embed_skill(sid, &sname, sdesc.as_deref(), &scontent, &sproj).await {
|
||||
tracing::warn!(error = %e, skill_id = %sid, "failed to embed skill");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SkillResponse::from(inserted))
|
||||
}
|
||||
|
||||
@ -144,6 +160,22 @@ impl AppService {
|
||||
active.updated_at = Set(Utc::now());
|
||||
|
||||
let updated = active.update(&self.db).await?;
|
||||
|
||||
// Re-embed skill on update (non-blocking)
|
||||
if let Some(ref embed) = self.embed_service {
|
||||
let es = embed.clone();
|
||||
let sid = updated.id;
|
||||
let sname = updated.name.clone();
|
||||
let sdesc = updated.description.clone();
|
||||
let scontent = updated.content.clone();
|
||||
let sproj = updated.project_uuid.to_string();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = es.embed_skill(sid, &sname, sdesc.as_deref(), &scontent, &sproj).await {
|
||||
tracing::warn!(error = %e, skill_id = %sid, "failed to re-embed skill on update");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SkillResponse::from(updated))
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user