use cortex_proto::cortex::v1::{
cortex_service_client::CortexServiceClient, BriefingRequest, CreateEdgeRequest,
CreateNodeRequest, GetNodeRequest, HybridResultEntry, HybridSearchRequest, NodeResponse,
SearchResponse, SimilaritySearchRequest, StatsRequest, StatsResponse, SubgraphResponse,
TraverseRequest,
};
use tonic::transport::Channel;
pub use cortex_proto::cortex::v1 as proto;
pub struct CortexClient {
inner: CortexServiceClient<Channel>,
}
impl CortexClient {
pub async fn connect(addr: impl Into<String>) -> anyhow::Result<Self> {
let channel = Channel::from_shared(addr.into())?.connect().await?;
Ok(Self {
inner: CortexServiceClient::new(channel),
})
}
pub fn inner(&mut self) -> &mut CortexServiceClient<Channel> {
&mut self.inner
}
pub async fn create_node(&mut self, req: CreateNodeRequest) -> anyhow::Result<NodeResponse> {
let resp = self.inner.create_node(req).await?;
Ok(resp.into_inner())
}
pub async fn get_node(&mut self, id: &str) -> anyhow::Result<Option<NodeResponse>> {
match self.inner.get_node(GetNodeRequest { id: id.into() }).await {
Ok(resp) => Ok(Some(resp.into_inner())),
Err(status) if status.code() == tonic::Code::NotFound => Ok(None),
Err(e) => Err(e.into()),
}
}
pub async fn search(&mut self, query: &str, limit: u32) -> anyhow::Result<SearchResponse> {
let resp = self
.inner
.similarity_search(SimilaritySearchRequest {
query: query.into(),
limit,
..Default::default()
})
.await?;
Ok(resp.into_inner())
}
pub async fn search_hybrid(
&mut self,
query: &str,
anchor_ids: Vec<String>,
limit: u32,
) -> anyhow::Result<Vec<HybridResultEntry>> {
let resp = self
.inner
.hybrid_search(HybridSearchRequest {
query: query.into(),
anchor_ids,
limit,
..Default::default()
})
.await?;
Ok(resp.into_inner().results)
}
pub async fn briefing(&mut self, agent_id: &str) -> anyhow::Result<String> {
let resp = self
.inner
.get_briefing(BriefingRequest {
agent_id: agent_id.into(),
..Default::default()
})
.await?;
Ok(resp.into_inner().rendered)
}
pub async fn traverse(
&mut self,
node_id: &str,
depth: u32,
) -> anyhow::Result<SubgraphResponse> {
let resp = self
.inner
.traverse(TraverseRequest {
start_ids: vec![node_id.into()],
max_depth: depth,
..Default::default()
})
.await?;
Ok(resp.into_inner())
}
pub async fn create_edge(
&mut self,
from_id: &str,
to_id: &str,
relation: &str,
) -> anyhow::Result<String> {
let resp = self
.inner
.create_edge(CreateEdgeRequest {
from_id: from_id.into(),
to_id: to_id.into(),
relation: relation.into(),
weight: 1.0,
metadata: Default::default(),
})
.await?;
Ok(resp.into_inner().id)
}
pub async fn stats(&mut self) -> anyhow::Result<StatsResponse> {
let resp = self.inner.stats(StatsRequest {}).await?;
Ok(resp.into_inner())
}
}