gitdataai/libs/git/hook/mod.rs
ZhenYi 2a9ec6d509 feat(tag): vectorize repo tags after hook sync with incremental embedding + FC tool
- HookWorker gains optional embed_service field
- Captures changed tag names during webhook dispatch, batch-embeds after completion
- HookService auto-inits EmbedService from config for standalone git-hook binary
- Adds agent dep to git crate (no circular dep)
- SSH/HTTP servers no longer call start_worker (dedicated git-hook handles it)
- git_tag_search FC tool for agent semantic tag search with project isolation
2026-04-28 13:04:10 +08:00

94 lines
3.0 KiB
Rust

use config::AppConfig;
use db::cache::AppCache;
use db::database::AppDatabase;
use deadpool_redis::cluster::Pool as RedisPool;
use tokio_util::sync::CancellationToken;
pub mod pool;
pub mod sync;
pub mod webhook_dispatch;
pub use pool::{HookWorker, PoolConfig, RedisConsumer};
pub use pool::types::{HookTask, TaskType};
/// Helper to initialize an optional EmbedService from config (graceful degradation).
async fn init_embed_service(config: &AppConfig, db: &AppDatabase) -> Option<agent::embed::EmbedService> {
match agent::new_embed_client(config).await {
Ok(client) => {
let model_name = config
.get_embed_model_name()
.unwrap_or_else(|_| "text-embedding-3-small".into());
let dimensions = config
.get_embed_model_dimensions()
.unwrap_or(1536);
let svc = agent::embed::EmbedService::new(
client,
db.writer().clone(),
model_name,
dimensions,
);
// Ensure the repo_tag collection exists
let _ = svc.ensure_collections().await;
tracing::info!("hook worker: EmbedService initialized for tag embedding");
Some(svc)
}
Err(e) => {
tracing::warn!(error = %e, "hook worker: EmbedService not available — tag embedding disabled");
None
}
}
}
/// Hook service that manages the Redis-backed task queue worker.
/// Multiple gitserver pods can run concurrently — the worker acquires a
/// per-repo Redis lock before processing each task.
#[derive(Clone)]
pub struct HookService {
pub(crate) db: AppDatabase,
pub(crate) cache: AppCache,
pub(crate) redis_pool: RedisPool,
pub(crate) config: AppConfig,
pub(crate) embed_service: Option<agent::embed::EmbedService>,
}
impl HookService {
pub fn new(
db: AppDatabase,
cache: AppCache,
redis_pool: RedisPool,
config: AppConfig,
) -> Self {
Self {
db,
cache,
redis_pool,
config,
embed_service: None,
}
}
/// Set an externally-initialized EmbedService (e.g. from the web app).
pub fn with_embed_service(mut self, svc: agent::embed::EmbedService) -> Self {
self.embed_service = Some(svc);
self
}
/// Start the background worker and return a cancellation token.
pub async fn start_worker(&self) -> CancellationToken {
// Auto-init embed_service if not set and config allows (standalone binaries)
let embed = match self.embed_service.clone() {
Some(svc) => Some(svc),
None => init_embed_service(&self.config, &self.db).await,
};
let pool_config = PoolConfig::from_env(&self.config);
pool::start_worker(
self.db.clone(),
self.cache.clone(),
self.redis_pool.clone(),
pool_config,
embed,
)
}
}