use std::{
collections::HashMap,
sync::{Arc, Mutex},
time::{Duration, Instant},
};
use async_trait::async_trait;
use sea_orm::{ConnectionTrait, Statement};
use tokio::time::timeout as tokio_timeout;
use crate::{
components::ComponentHandles,
config::HttpServerConfig,
routers::health::{
ComponentHealth, HealthCheckError, HealthCheckReport, HealthChecker, HealthStatus,
},
};
const COMPONENT_RELATIONAL_DB: &str = "relational_db";
const COMPONENT_VECTOR_DB: &str = "vector_db";
const COMPONENT_GRAPH_DB: &str = "graph_db";
const COMPONENT_FILE_STORAGE: &str = "file_storage";
const COMPONENT_LLM: &str = "llm_provider";
const COMPONENT_EMBEDDING: &str = "embedding_service";
#[derive(Clone)]
struct CachedReport {
report: HealthCheckReport,
cached_at: Instant,
detailed: bool,
}
pub struct RealHealthChecker {
components: Arc<ComponentHandles>,
start_time: Instant,
probe_timeout: Duration,
cache_ttl: Duration,
probe_llm: bool,
cache: Mutex<Option<CachedReport>>,
}
impl RealHealthChecker {
pub fn new(components: Arc<ComponentHandles>, config: &HttpServerConfig) -> Self {
Self {
components,
start_time: Instant::now(),
probe_timeout: Duration::from_millis(config.health_probe_timeout_ms),
cache_ttl: Duration::from_millis(config.health_cache_ttl_ms),
probe_llm: config.health_probe_llm,
cache: Mutex::new(None),
}
}
async fn run_probes(&self, detailed: bool) -> HealthCheckReport {
let db_fut = self.probe_database();
let graph_fut = self.probe_graph_db();
let vector_fut = self.probe_vector_db();
let storage_fut = self.probe_file_storage();
let (db, graph, vector, storage) = tokio::join!(db_fut, graph_fut, vector_fut, storage_fut);
let mut components = HashMap::new();
components.insert(COMPONENT_RELATIONAL_DB.to_string(), db);
components.insert(COMPONENT_GRAPH_DB.to_string(), graph);
components.insert(COMPONENT_VECTOR_DB.to_string(), vector);
components.insert(COMPONENT_FILE_STORAGE.to_string(), storage);
if detailed || self.probe_llm {
let llm = self.probe_llm_provider().await;
let embedding = self.probe_embedding_engine().await;
components.insert(COMPONENT_LLM.to_string(), llm);
components.insert(COMPONENT_EMBEDDING.to_string(), embedding);
}
let mut overall = HealthStatus::Healthy;
for (name, comp) in &components {
let is_critical = matches!(
name.as_str(),
COMPONENT_RELATIONAL_DB
| COMPONENT_VECTOR_DB
| COMPONENT_GRAPH_DB
| COMPONENT_FILE_STORAGE
);
match (comp.status, is_critical) {
(HealthStatus::Unhealthy, true) => {
overall = HealthStatus::Unhealthy;
}
(HealthStatus::Unhealthy, false) | (HealthStatus::Degraded, _)
if overall == HealthStatus::Healthy =>
{
overall = HealthStatus::Degraded;
}
_ => {}
}
}
HealthCheckReport {
status: overall,
timestamp: chrono::Utc::now(),
version: env!("CARGO_PKG_VERSION").into(),
uptime: self.start_time.elapsed(),
components,
}
}
async fn with_timeout<F>(&self, provider: &str, critical: bool, fut: F) -> ComponentHealth
where
F: std::future::Future<Output = ComponentHealth>,
{
let started = Instant::now();
match tokio_timeout(self.probe_timeout, fut).await {
Ok(result) => result,
Err(_elapsed) => ComponentHealth {
status: if critical {
HealthStatus::Unhealthy
} else {
HealthStatus::Degraded
},
provider: provider.to_string(),
response_time: started.elapsed(),
details: format!(
"probe timed out after {} ms",
self.probe_timeout.as_millis()
),
},
}
}
async fn probe_database(&self) -> ComponentHealth {
let db = Arc::clone(&self.components.database);
let provider = cognee_database::database_system_label(&db).to_string();
self.with_timeout(&provider, true, async move {
let started = Instant::now();
let backend = db.get_database_backend();
let stmt = Statement::from_string(backend, "SELECT 1".to_string());
match db.execute(stmt).await {
Ok(_) => ComponentHealth {
status: HealthStatus::Healthy,
provider: cognee_database::database_system_label(&db).to_string(),
response_time: started.elapsed(),
details: "SELECT 1 ok".into(),
},
Err(e) => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: cognee_database::database_system_label(&db).to_string(),
response_time: started.elapsed(),
details: format!("query failed: {e}"),
},
}
})
.await
}
async fn probe_graph_db(&self) -> ComponentHealth {
let graph = self.components.graph_db.as_ref().map(Arc::clone);
self.with_timeout("graph_db", true, async move {
let started = Instant::now();
match graph {
None => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "none".into(),
response_time: started.elapsed(),
details: "graph backend not wired".into(),
},
Some(g) => match g.is_empty().await {
Ok(_) => ComponentHealth {
status: HealthStatus::Healthy,
provider: "graph".into(),
response_time: started.elapsed(),
details: "is_empty ok".into(),
},
Err(e) => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "graph".into(),
response_time: started.elapsed(),
details: format!("is_empty failed: {e}"),
},
},
}
})
.await
}
async fn probe_vector_db(&self) -> ComponentHealth {
let vector = self.components.vector_db.as_ref().map(Arc::clone);
self.with_timeout("vector_db", true, async move {
let started = Instant::now();
match vector {
None => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "none".into(),
response_time: started.elapsed(),
details: "vector backend not wired".into(),
},
Some(v) => match v.list_collections().await {
Ok(cols) => ComponentHealth {
status: HealthStatus::Healthy,
provider: "vector".into(),
response_time: started.elapsed(),
details: format!("{} collection(s)", cols.len()),
},
Err(e) => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "vector".into(),
response_time: started.elapsed(),
details: format!("list_collections failed: {e}"),
},
},
}
})
.await
}
async fn probe_file_storage(&self) -> ComponentHealth {
let storage = Arc::clone(&self.components.storage);
self.with_timeout("file_storage", true, async move {
let started = Instant::now();
let probe_id = uuid::Uuid::new_v4();
let file_name = format!(".health-check-{probe_id}");
let payload = b"cognee-health-probe";
let store_result = storage.store(payload, &file_name).await;
let location = match store_result {
Ok(loc) => loc,
Err(e) => {
return ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "local".into(),
response_time: started.elapsed(),
details: format!("store failed: {e}"),
};
}
};
let retrieve_result = storage.retrieve(&location).await;
let _ = storage.delete(&location).await;
match retrieve_result {
Ok(buf) if buf == payload => ComponentHealth {
status: HealthStatus::Healthy,
provider: "local".into(),
response_time: started.elapsed(),
details: "write/read/delete ok".into(),
},
Ok(_) => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "local".into(),
response_time: started.elapsed(),
details: "round-trip payload mismatch".into(),
},
Err(e) => ComponentHealth {
status: HealthStatus::Unhealthy,
provider: "local".into(),
response_time: started.elapsed(),
details: format!("retrieve failed: {e}"),
},
}
})
.await
}
async fn probe_llm_provider(&self) -> ComponentHealth {
let llm = self.components.llm.as_ref().map(Arc::clone);
self.with_timeout("llm", false, async move {
let started = Instant::now();
match llm {
None => ComponentHealth {
status: HealthStatus::Degraded,
provider: "none".into(),
response_time: started.elapsed(),
details: "LLM backend not wired".into(),
},
Some(l) => {
let opts = cognee_llm::types::GenerationOptions {
temperature: Some(0.0),
max_tokens: Some(1),
..Default::default()
};
let msgs = vec![cognee_llm::types::Message::user("ping")];
match l.generate(msgs, Some(opts)).await {
Ok(_resp) => ComponentHealth {
status: HealthStatus::Healthy,
provider: "llm".into(),
response_time: started.elapsed(),
details: "generate ok".into(),
},
Err(e) => ComponentHealth {
status: HealthStatus::Degraded,
provider: "llm".into(),
response_time: started.elapsed(),
details: format!("generate failed: {e}"),
},
}
}
}
})
.await
}
async fn probe_embedding_engine(&self) -> ComponentHealth {
let engine = self.components.embedding_engine.as_ref().map(Arc::clone);
self.with_timeout("embedding", false, async move {
let started = Instant::now();
match engine {
None => ComponentHealth {
status: HealthStatus::Degraded,
provider: "none".into(),
response_time: started.elapsed(),
details: "embedding backend not wired".into(),
},
Some(e) => match e.embed(&["ping"]).await {
Ok(vectors) => ComponentHealth {
status: HealthStatus::Healthy,
provider: "embedding".into(),
response_time: started.elapsed(),
details: format!(
"embed ok ({} vector(s), dim={})",
vectors.len(),
vectors.first().map(|v| v.len()).unwrap_or(0)
),
},
Err(err) => ComponentHealth {
status: HealthStatus::Degraded,
provider: "embedding".into(),
response_time: started.elapsed(),
details: format!("embed failed: {err}"),
},
},
}
})
.await
}
}
#[async_trait]
impl HealthChecker for RealHealthChecker {
async fn get_health_status(
&self,
detailed: bool,
) -> Result<HealthCheckReport, HealthCheckError> {
if !self.cache_ttl.is_zero() {
#[allow(clippy::unwrap_used, reason = "lock poison is unrecoverable")]
let guard = self.cache.lock().unwrap();
if let Some(cached) = guard.as_ref()
&& cached.detailed == detailed
&& cached.cached_at.elapsed() < self.cache_ttl
{
return Ok(cached.report.clone());
}
drop(guard);
}
let deadline = self.probe_timeout + Duration::from_millis(50);
let report = match tokio_timeout(deadline, self.run_probes(detailed)).await {
Ok(r) => r,
Err(_elapsed) => {
return Err(HealthCheckError(format!(
"health probes exceeded deadline ({} ms)",
deadline.as_millis()
)));
}
};
if !self.cache_ttl.is_zero() {
#[allow(clippy::unwrap_used, reason = "lock poison is unrecoverable")]
let mut guard = self.cache.lock().unwrap();
*guard = Some(CachedReport {
report: report.clone(),
cached_at: Instant::now(),
detailed,
});
}
Ok(report)
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code — panics are acceptable failures"
)]
mod tests {
use super::*;
use cognee_database::{DatabaseConnection, connect, initialize};
use cognee_delete::DeleteService;
use cognee_embedding::MockEmbeddingEngine;
use cognee_graph::MockGraphDB;
use cognee_llm::Llm;
use cognee_ontology::OntologyManager;
use cognee_storage::{LocalStorage, StorageError, StorageTrait};
use cognee_vector::MockVectorDB;
async fn build_handles(
storage: Option<Arc<dyn StorageTrait>>,
graph_ok: bool,
vector_ok: bool,
llm: Option<Arc<dyn Llm>>,
embedding: Option<Arc<dyn cognee_embedding::EmbeddingEngine>>,
) -> Arc<ComponentHandles> {
let db: Arc<DatabaseConnection> = Arc::new(
connect("sqlite::memory:")
.await
.expect("open in-memory sqlite"),
);
initialize(&db).await.expect("run migrations");
let storage_dir = tempfile::tempdir().expect("tmp storage");
let local =
Arc::new(LocalStorage::new(storage_dir.path().to_path_buf())) as Arc<dyn StorageTrait>;
Box::leak(Box::new(storage_dir));
let storage_handle = storage.unwrap_or(local);
let delete_service = Arc::new(DeleteService::new(
Arc::clone(&storage_handle),
db.clone() as Arc<dyn cognee_database::DeleteDb>,
));
let ontology_dir = tempfile::tempdir().expect("tmp ontology");
let ontology_manager = Arc::new(OntologyManager::new(ontology_dir.path().to_path_buf()));
Box::leak(Box::new(ontology_dir));
let graph_db: Option<Arc<dyn cognee_graph::GraphDBTrait>> = if graph_ok {
Some(Arc::new(MockGraphDB::new()))
} else {
Some(Arc::new(FailingGraphDB::new()))
};
let vector_db: Option<Arc<dyn cognee_vector::VectorDB>> = if vector_ok {
Some(Arc::new(MockVectorDB::new()))
} else {
Some(Arc::new(FailingVectorDB::new()))
};
Arc::new(ComponentHandles {
database: db,
acl_db: None,
storage: storage_handle,
delete_service,
cloud_client: None,
ontology_manager,
search_orchestrator: None,
llm,
transcriber: None,
embedding_engine: embedding,
graph_db,
vector_db,
thread_pool: None,
session_store: None,
session_manager: None,
checkpoint_store: None,
ontology_resolver: None,
responses_client: None,
notebook_runner: None,
})
}
fn fast_config() -> HttpServerConfig {
HttpServerConfig {
health_probe_timeout_ms: 500,
health_cache_ttl_ms: 0,
..HttpServerConfig::default()
}
}
#[tokio::test]
async fn all_healthy_yields_overall_healthy() {
let handles = build_handles(None, true, true, None, None).await;
let checker = RealHealthChecker::new(handles, &fast_config());
let report = checker
.get_health_status(false)
.await
.expect("checker should succeed");
assert_eq!(report.status, HealthStatus::Healthy);
for key in &[
COMPONENT_RELATIONAL_DB,
COMPONENT_VECTOR_DB,
COMPONENT_GRAPH_DB,
COMPONENT_FILE_STORAGE,
] {
let comp = report
.components
.get(*key)
.unwrap_or_else(|| panic!("missing {key}"));
assert_eq!(comp.status, HealthStatus::Healthy, "{key} status");
}
assert!(!report.components.contains_key(COMPONENT_LLM));
assert!(!report.components.contains_key(COMPONENT_EMBEDDING));
}
#[tokio::test]
async fn detailed_probes_llm_and_degrades_when_unavailable() {
let handles = build_handles(None, true, true, None, None).await;
let checker = RealHealthChecker::new(handles, &fast_config());
let report = checker
.get_health_status(true)
.await
.expect("checker should succeed");
assert_eq!(report.status, HealthStatus::Degraded);
assert!(report.components.contains_key(COMPONENT_LLM));
assert_eq!(
report
.components
.get(COMPONENT_LLM)
.expect("llm entry")
.status,
HealthStatus::Degraded,
);
}
#[tokio::test]
async fn critical_graph_failure_yields_unhealthy() {
let handles = build_handles(None, false, true, None, None).await;
let checker = RealHealthChecker::new(handles, &fast_config());
let report = checker
.get_health_status(true)
.await
.expect("checker should succeed");
assert_eq!(report.status, HealthStatus::Unhealthy);
let graph = report
.components
.get(COMPONENT_GRAPH_DB)
.expect("graph entry");
assert_eq!(graph.status, HealthStatus::Unhealthy);
}
#[tokio::test]
async fn non_critical_llm_failure_yields_degraded() {
let handles = build_handles(
None,
true,
true,
Some(Arc::new(FailingLlm)),
Some(Arc::new(MockEmbeddingEngine::new(8))),
)
.await;
let cfg = HttpServerConfig {
health_probe_llm: true,
..fast_config()
};
let checker = RealHealthChecker::new(handles, &cfg);
let report = checker.get_health_status(true).await.expect("report");
assert_eq!(report.status, HealthStatus::Degraded);
let llm_entry = report.components.get(COMPONENT_LLM).expect("llm entry");
assert_eq!(llm_entry.status, HealthStatus::Degraded);
let emb_entry = report
.components
.get(COMPONENT_EMBEDDING)
.expect("embedding entry");
assert_eq!(emb_entry.status, HealthStatus::Healthy);
}
#[tokio::test]
async fn opt_in_llm_off_omits_entries() {
let handles = build_handles(
None,
true,
true,
Some(Arc::new(FailingLlm)),
Some(Arc::new(FailingEmbedding)),
)
.await;
let checker = RealHealthChecker::new(handles, &fast_config());
let report = checker.get_health_status(false).await.expect("report");
assert_eq!(report.status, HealthStatus::Healthy);
assert!(!report.components.contains_key(COMPONENT_LLM));
assert!(!report.components.contains_key(COMPONENT_EMBEDDING));
}
#[tokio::test]
async fn slow_probe_times_out_within_budget() {
let slow_storage: Arc<dyn StorageTrait> = Arc::new(SlowStorage::new());
let handles = build_handles(Some(slow_storage), true, true, None, None).await;
let cfg = HttpServerConfig {
health_probe_timeout_ms: 100,
health_cache_ttl_ms: 0,
..HttpServerConfig::default()
};
let checker = RealHealthChecker::new(handles, &cfg);
let started = Instant::now();
let report = checker.get_health_status(true).await.expect("report");
let elapsed = started.elapsed();
assert!(
elapsed < Duration::from_millis(1000),
"checker hung past per-probe timeout: {elapsed:?}"
);
let storage_entry = report
.components
.get(COMPONENT_FILE_STORAGE)
.expect("storage entry");
assert_eq!(storage_entry.status, HealthStatus::Unhealthy);
assert!(
storage_entry.details.contains("timed out"),
"expected timeout details, got: {}",
storage_entry.details
);
assert_eq!(report.status, HealthStatus::Unhealthy);
}
#[tokio::test]
async fn cache_serves_repeat_calls() {
let handles = build_handles(None, true, true, None, None).await;
let cfg = HttpServerConfig {
health_probe_timeout_ms: 500,
health_cache_ttl_ms: 10_000,
..HttpServerConfig::default()
};
let checker = RealHealthChecker::new(handles, &cfg);
let first = checker.get_health_status(true).await.expect("first");
let second = checker.get_health_status(true).await.expect("second");
assert_eq!(first.timestamp, second.timestamp);
}
struct FailingGraphDB {
inner: MockGraphDB,
}
impl FailingGraphDB {
fn new() -> Self {
Self {
inner: MockGraphDB::new(),
}
}
}
#[async_trait]
impl cognee_graph::GraphDBTrait for FailingGraphDB {
async fn initialize(&self) -> cognee_graph::GraphDBResult<()> {
self.inner.initialize().await
}
async fn is_empty(&self) -> cognee_graph::GraphDBResult<bool> {
Err(cognee_graph::GraphDBError::QueryError(
"synthetic graph failure".into(),
))
}
async fn query(
&self,
q: &str,
params: Option<
std::collections::HashMap<std::borrow::Cow<'static, str>, serde_json::Value>,
>,
) -> cognee_graph::GraphDBResult<Vec<Vec<serde_json::Value>>> {
self.inner.query(q, params).await
}
async fn delete_graph(&self) -> cognee_graph::GraphDBResult<()> {
self.inner.delete_graph().await
}
async fn has_node(&self, id: &str) -> cognee_graph::GraphDBResult<bool> {
self.inner.has_node(id).await
}
async fn add_node_raw(&self, n: serde_json::Value) -> cognee_graph::GraphDBResult<()> {
self.inner.add_node_raw(n).await
}
async fn add_nodes_raw(
&self,
ns: Vec<serde_json::Value>,
) -> cognee_graph::GraphDBResult<()> {
self.inner.add_nodes_raw(ns).await
}
async fn delete_node(&self, id: &str) -> cognee_graph::GraphDBResult<()> {
self.inner.delete_node(id).await
}
async fn delete_nodes(&self, ids: &[String]) -> cognee_graph::GraphDBResult<()> {
self.inner.delete_nodes(ids).await
}
async fn get_node(
&self,
id: &str,
) -> cognee_graph::GraphDBResult<Option<cognee_graph::NodeData>> {
self.inner.get_node(id).await
}
async fn get_nodes(
&self,
ids: &[String],
) -> cognee_graph::GraphDBResult<Vec<cognee_graph::NodeData>> {
self.inner.get_nodes(ids).await
}
async fn has_edge(&self, s: &str, t: &str, r: &str) -> cognee_graph::GraphDBResult<bool> {
self.inner.has_edge(s, t, r).await
}
async fn has_edges(
&self,
edges: &[cognee_graph::EdgeData],
) -> cognee_graph::GraphDBResult<Vec<cognee_graph::EdgeData>> {
self.inner.has_edges(edges).await
}
async fn add_edge(
&self,
s: &str,
t: &str,
r: &str,
p: Option<std::collections::HashMap<std::borrow::Cow<'static, str>, serde_json::Value>>,
) -> cognee_graph::GraphDBResult<()> {
self.inner.add_edge(s, t, r, p).await
}
async fn add_edges(
&self,
edges: &[cognee_graph::EdgeData],
) -> cognee_graph::GraphDBResult<()> {
self.inner.add_edges(edges).await
}
async fn get_edges(
&self,
id: &str,
) -> cognee_graph::GraphDBResult<Vec<cognee_graph::EdgeData>> {
self.inner.get_edges(id).await
}
async fn get_neighbors(
&self,
id: &str,
) -> cognee_graph::GraphDBResult<Vec<cognee_graph::NodeData>> {
self.inner.get_neighbors(id).await
}
async fn get_connections(
&self,
id: &str,
) -> cognee_graph::GraphDBResult<
Vec<(
cognee_graph::NodeData,
std::collections::HashMap<std::borrow::Cow<'static, str>, serde_json::Value>,
cognee_graph::NodeData,
)>,
> {
self.inner.get_connections(id).await
}
async fn get_graph_data(
&self,
) -> cognee_graph::GraphDBResult<(Vec<cognee_graph::GraphNode>, Vec<cognee_graph::EdgeData>)>
{
self.inner.get_graph_data().await
}
async fn get_graph_metrics(
&self,
include_optional: bool,
) -> cognee_graph::GraphDBResult<
std::collections::HashMap<std::borrow::Cow<'static, str>, serde_json::Value>,
> {
self.inner.get_graph_metrics(include_optional).await
}
async fn get_filtered_graph_data(
&self,
f: &std::collections::HashMap<std::borrow::Cow<'static, str>, Vec<serde_json::Value>>,
) -> cognee_graph::GraphDBResult<(Vec<cognee_graph::GraphNode>, Vec<cognee_graph::EdgeData>)>
{
self.inner.get_filtered_graph_data(f).await
}
async fn get_nodeset_subgraph(
&self,
node_type: &str,
node_names: &[String],
op: &str,
) -> cognee_graph::GraphDBResult<(Vec<cognee_graph::GraphNode>, Vec<cognee_graph::EdgeData>)>
{
self.inner
.get_nodeset_subgraph(node_type, node_names, op)
.await
}
}
struct FailingVectorDB {
inner: MockVectorDB,
}
impl FailingVectorDB {
fn new() -> Self {
Self {
inner: MockVectorDB::new(),
}
}
}
#[async_trait]
impl cognee_vector::VectorDB for FailingVectorDB {
async fn create_collection(
&self,
data_type: &str,
field_name: &str,
dimension: usize,
) -> cognee_vector::VectorDBResult<()> {
self.inner
.create_collection(data_type, field_name, dimension)
.await
}
async fn has_collection(
&self,
data_type: &str,
field_name: &str,
) -> cognee_vector::VectorDBResult<bool> {
self.inner.has_collection(data_type, field_name).await
}
async fn index_points(
&self,
data_type: &str,
field_name: &str,
points: &[cognee_vector::VectorPoint],
) -> cognee_vector::VectorDBResult<()> {
self.inner.index_points(data_type, field_name, points).await
}
async fn search_similar(
&self,
data_type: &str,
field_name: &str,
query_vector: &[f32],
top_k: usize,
) -> cognee_vector::VectorDBResult<Vec<cognee_vector::SearchResult>> {
self.inner
.search_similar(data_type, field_name, query_vector, top_k)
.await
}
async fn delete_collection(
&self,
data_type: &str,
field_name: &str,
) -> cognee_vector::VectorDBResult<()> {
self.inner.delete_collection(data_type, field_name).await
}
async fn collection_size(
&self,
data_type: &str,
field_name: &str,
) -> cognee_vector::VectorDBResult<usize> {
self.inner.collection_size(data_type, field_name).await
}
async fn list_collections(&self) -> cognee_vector::VectorDBResult<Vec<(String, String)>> {
Err(cognee_vector::VectorDBError::StorageError(
"synthetic vector failure".into(),
))
}
}
struct FailingLlm;
#[async_trait]
impl cognee_llm::Llm for FailingLlm {
fn model(&self) -> &str {
"failing-test-llm"
}
async fn generate(
&self,
_messages: Vec<cognee_llm::types::Message>,
_options: Option<cognee_llm::types::GenerationOptions>,
) -> cognee_llm::LlmResult<cognee_llm::types::GenerationResponse> {
Err(cognee_llm::LlmError::ApiError(
"synthetic llm failure".into(),
))
}
async fn create_structured_output_with_messages_raw(
&self,
_messages: Vec<cognee_llm::types::Message>,
_json_schema: &serde_json::Value,
_options: Option<cognee_llm::types::GenerationOptions>,
) -> cognee_llm::LlmResult<serde_json::Value> {
Err(cognee_llm::LlmError::ApiError(
"synthetic llm failure".into(),
))
}
}
struct FailingEmbedding;
#[async_trait]
impl cognee_embedding::EmbeddingEngine for FailingEmbedding {
async fn embed(&self, _texts: &[&str]) -> cognee_embedding::EmbeddingResult<Vec<Vec<f32>>> {
Err(cognee_embedding::EmbeddingError::InferenceError(
"synthetic embedding failure".into(),
))
}
fn dimension(&self) -> usize {
8
}
fn batch_size(&self) -> usize {
1
}
fn max_sequence_length(&self) -> usize {
16
}
}
struct SlowStorage {
inner: Arc<LocalStorage>,
_guard: Arc<tempfile::TempDir>,
}
impl SlowStorage {
fn new() -> Self {
let dir = tempfile::tempdir().expect("tmp dir");
let inner = Arc::new(LocalStorage::new(dir.path().to_path_buf()));
Self {
inner,
_guard: Arc::new(dir),
}
}
}
#[async_trait]
impl StorageTrait for SlowStorage {
async fn store(&self, _data: &[u8], _file_name: &str) -> Result<String, StorageError> {
tokio::time::sleep(Duration::from_secs(60)).await;
Ok("never".into())
}
async fn store_stream_dyn(
&self,
reader: &mut (dyn tokio::io::AsyncRead + Unpin + Send),
file_name: &str,
) -> Result<String, StorageError> {
self.inner.store_stream_dyn(reader, file_name).await
}
async fn create_writer(
&self,
file_name: &str,
) -> Result<cognee_storage::StorageWriter, StorageError> {
self.inner.create_writer(file_name).await
}
async fn retrieve(&self, location: &str) -> Result<Vec<u8>, StorageError> {
self.inner.retrieve(location).await
}
async fn exists(&self, location: &str) -> Result<bool, StorageError> {
self.inner.exists(location).await
}
async fn delete(&self, location: &str) -> Result<(), StorageError> {
self.inner.delete(location).await
}
fn get_full_path(&self, location: &str) -> std::path::PathBuf {
self.inner.get_full_path(location)
}
fn base_path(&self) -> &str {
self.inner.base_path()
}
async fn initialize(&self) -> Result<(), StorageError> {
self.inner.initialize().await
}
async fn remove_all(&self) -> Result<(), StorageError> {
self.inner.remove_all().await
}
}
}