recall_echo/
graph_bridge.rs1pub async fn ingest_into_graph(
11 memory_dir: &std::path::Path,
12 archive_content: &str,
13 session_id: &str,
14 log_number: Option<u32>,
15) -> Result<crate::graph::types::IngestionReport, crate::error::RecallError> {
16 let graph_dir = memory_dir.join("graph");
17 if !graph_dir.exists() {
18 return Err(crate::error::RecallError::NotInitialized(
19 "graph/ not initialized \u{2014} run `graph init` first".into(),
20 ));
21 }
22
23 let request = crate::serve::Request::IngestArchive(crate::serve::IngestArchiveArgs {
29 content: archive_content.to_string(),
30 session_id: session_id.to_string(),
31 log_number,
32 provenance: None,
33 });
34 let report: crate::graph::types::IngestionReport =
35 serde_json::from_value(crate::serve_client::execute(memory_dir, &request).await?)?;
36
37 eprintln!(
38 "recall-echo: graph ingested \u{2014} {} episodes, {} entities created, {} merged, {} skipped, {} relationships",
39 report.episodes_created,
40 report.entities_created,
41 report.entities_merged,
42 report.entities_skipped,
43 report.relationships_created,
44 );
45
46 if !report.errors.is_empty() {
47 eprintln!(
48 "recall-echo: graph ingestion had {} warnings",
49 report.errors.len()
50 );
51 }
52
53 Ok(report)
54}
55
56pub async fn sync_pipeline_into_graph(
63 memory_dir: &std::path::Path,
64 docs: crate::graph::types::PipelineDocuments,
65) -> Result<crate::graph::types::PipelineSyncReport, crate::error::RecallError> {
66 let request = crate::serve::Request::SyncPipeline(crate::serve::SyncPipelineArgs { docs });
67 Ok(serde_json::from_value(
68 crate::serve_client::execute(memory_dir, &request).await?,
69 )?)
70}
71
72#[cfg(feature = "pulse-null")]
77pub async fn ingest_into_graph_with_llm(
78 memory_dir: &std::path::Path,
79 archive_content: &str,
80 session_id: &str,
81 log_number: Option<u32>,
82 provider: Option<&dyn pulse_system_types::llm::LmProvider>,
83) -> Result<crate::graph::types::IngestionReport, crate::error::RecallError> {
84 let graph_dir = memory_dir.join("graph");
85 if !graph_dir.exists() {
86 return Err(crate::error::RecallError::NotInitialized(
87 "graph/ not initialized \u{2014} run `graph init` first".into(),
88 ));
89 }
90
91 let context = crate::graph::IngestContext::new(session_id, log_number);
94 let report = crate::serve_client::exclusive(memory_dir, |gm| async move {
95 let bridge = provider.map(GraphLlmBridge::new);
96 let llm_ref: Option<&dyn crate::graph::llm::LlmProvider> = bridge
97 .as_ref()
98 .map(|b| b as &dyn crate::graph::llm::LlmProvider);
99
100 Ok(gm
101 .ingest_archive(archive_content, &context, llm_ref)
102 .await?)
103 })
104 .await?;
105
106 eprintln!(
107 "recall-echo: graph ingested \u{2014} {} episodes, {} entities created, {} merged, {} skipped, {} relationships",
108 report.episodes_created,
109 report.entities_created,
110 report.entities_merged,
111 report.entities_skipped,
112 report.relationships_created,
113 );
114
115 if !report.errors.is_empty() {
116 eprintln!(
117 "recall-echo: graph ingestion had {} warnings",
118 report.errors.len()
119 );
120 }
121
122 Ok(report)
123}
124
125#[cfg(feature = "pulse-null")]
128pub struct GraphLlmBridge<'a> {
129 provider: &'a dyn pulse_system_types::llm::LmProvider,
130}
131
132#[cfg(feature = "pulse-null")]
133impl<'a> GraphLlmBridge<'a> {
134 pub fn new(provider: &'a dyn pulse_system_types::llm::LmProvider) -> Self {
135 Self { provider }
136 }
137}
138
139#[cfg(feature = "pulse-null")]
140#[async_trait::async_trait]
141impl crate::graph::llm::LlmProvider for GraphLlmBridge<'_> {
142 async fn complete(
143 &self,
144 system_prompt: &str,
145 user_message: &str,
146 max_tokens: u32,
147 ) -> Result<String, crate::graph::error::GraphError> {
148 use pulse_system_types::llm::{Message, MessageContent, Role};
149
150 let messages = vec![Message {
151 role: Role::User,
152 content: MessageContent::Text(user_message.to_string()),
153 source: None,
154 }];
155
156 let response = self
157 .provider
158 .invoke(system_prompt, &messages, max_tokens, None)
159 .await
160 .map_err(|e| crate::graph::error::GraphError::Llm(e.to_string()))?;
161
162 Ok(response.text())
163 }
164}