pub async fn ingest_into_graph(
memory_dir: &std::path::Path,
archive_content: &str,
session_id: &str,
log_number: Option<u32>,
) -> Result<crate::graph::types::IngestionReport, crate::error::RecallError> {
let graph_dir = memory_dir.join("graph");
if !graph_dir.exists() {
return Err(crate::error::RecallError::NotInitialized(
"graph/ not initialized \u{2014} run `graph init` first".into(),
));
}
let request = crate::serve::Request::IngestArchive(crate::serve::IngestArchiveArgs {
content: archive_content.to_string(),
session_id: session_id.to_string(),
log_number,
provenance: None,
});
let report: crate::graph::types::IngestionReport =
serde_json::from_value(crate::serve_client::execute(memory_dir, &request).await?)?;
eprintln!(
"recall-echo: graph ingested \u{2014} {} episodes, {} entities created, {} merged, {} skipped, {} relationships",
report.episodes_created,
report.entities_created,
report.entities_merged,
report.entities_skipped,
report.relationships_created,
);
if !report.errors.is_empty() {
eprintln!(
"recall-echo: graph ingestion had {} warnings",
report.errors.len()
);
}
Ok(report)
}
pub async fn sync_pipeline_into_graph(
memory_dir: &std::path::Path,
docs: crate::graph::types::PipelineDocuments,
) -> Result<crate::graph::types::PipelineSyncReport, crate::error::RecallError> {
let request = crate::serve::Request::SyncPipeline(crate::serve::SyncPipelineArgs { docs });
Ok(serde_json::from_value(
crate::serve_client::execute(memory_dir, &request).await?,
)?)
}
#[cfg(feature = "pulse-null")]
pub async fn ingest_into_graph_with_llm(
memory_dir: &std::path::Path,
archive_content: &str,
session_id: &str,
log_number: Option<u32>,
provider: Option<&dyn pulse_system_types::llm::LmProvider>,
) -> Result<crate::graph::types::IngestionReport, crate::error::RecallError> {
let graph_dir = memory_dir.join("graph");
if !graph_dir.exists() {
return Err(crate::error::RecallError::NotInitialized(
"graph/ not initialized \u{2014} run `graph init` first".into(),
));
}
let context = crate::graph::IngestContext::new(session_id, log_number);
let report = crate::serve_client::exclusive(memory_dir, |gm| async move {
let bridge = provider.map(GraphLlmBridge::new);
let llm_ref: Option<&dyn crate::graph::llm::LlmProvider> = bridge
.as_ref()
.map(|b| b as &dyn crate::graph::llm::LlmProvider);
Ok(gm
.ingest_archive(archive_content, &context, llm_ref)
.await?)
})
.await?;
eprintln!(
"recall-echo: graph ingested \u{2014} {} episodes, {} entities created, {} merged, {} skipped, {} relationships",
report.episodes_created,
report.entities_created,
report.entities_merged,
report.entities_skipped,
report.relationships_created,
);
if !report.errors.is_empty() {
eprintln!(
"recall-echo: graph ingestion had {} warnings",
report.errors.len()
);
}
Ok(report)
}
#[cfg(feature = "pulse-null")]
pub struct GraphLlmBridge<'a> {
provider: &'a dyn pulse_system_types::llm::LmProvider,
}
#[cfg(feature = "pulse-null")]
impl<'a> GraphLlmBridge<'a> {
pub fn new(provider: &'a dyn pulse_system_types::llm::LmProvider) -> Self {
Self { provider }
}
}
#[cfg(feature = "pulse-null")]
#[async_trait::async_trait]
impl crate::graph::llm::LlmProvider for GraphLlmBridge<'_> {
async fn complete(
&self,
system_prompt: &str,
user_message: &str,
max_tokens: u32,
) -> Result<String, crate::graph::error::GraphError> {
use pulse_system_types::llm::{Message, MessageContent, Role};
let messages = vec![Message {
role: Role::User,
content: MessageContent::Text(user_message.to_string()),
source: None,
}];
let response = self
.provider
.invoke(system_prompt, &messages, max_tokens, None)
.await
.map_err(|e| crate::graph::error::GraphError::Llm(e.to_string()))?;
Ok(response.text())
}
}