use serde::Deserialize;
use super::{RagAdapter, RagChunk};
use crate::config::BridgeRagConfig;
pub struct BridgeRag {
url: String,
token: Option<String>,
max_results: usize,
client: reqwest::Client,
}
impl BridgeRag {
pub fn new(cfg: &BridgeRagConfig) -> Self {
let token = cfg.token.as_deref().map(|t| {
if let Some(var) = t.strip_prefix("${").and_then(|s| s.strip_suffix('}')) {
std::env::var(var).unwrap_or_default()
} else {
t.to_string()
}
});
Self {
url: cfg.url.trim_end_matches('/').to_string(),
token,
max_results: cfg.max_results.unwrap_or(5),
client: reqwest::Client::new(),
}
}
}
#[derive(Deserialize)]
struct BridgeResponse {
results: Vec<BridgeResult>,
}
#[derive(Deserialize)]
struct BridgeResult {
source: String,
content: String,
#[serde(default)]
score: f32,
}
#[async_trait::async_trait]
impl RagAdapter for BridgeRag {
fn name(&self) -> &'static str {
"bridge"
}
async fn retrieve(&self, query: &str, project: Option<&str>) -> Vec<RagChunk> {
let mut body = serde_json::json!({
"query": query,
"max_results": self.max_results,
});
if let Some(proj) = project {
body["project"] = serde_json::Value::String(proj.to_string());
}
let mut req = self.client.post(format!("{}/search", self.url)).json(&body);
if let Some(token) = &self.token {
req = req.bearer_auth(token);
}
match req.send().await {
Ok(resp) if resp.status().is_success() => match resp.json::<BridgeResponse>().await {
Ok(data) => data
.results
.into_iter()
.map(|r| RagChunk {
adapter: "bridge",
source: r.source,
content: r.content,
score: r.score,
})
.collect(),
Err(e) => {
tracing::warn!("RAG bridge response parse error: {e}");
Vec::new()
}
},
Ok(resp) => {
tracing::warn!("RAG bridge HTTP {}", resp.status());
Vec::new()
}
Err(e) => {
tracing::warn!("RAG bridge request failed: {e}");
Vec::new()
}
}
}
}