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