Skip to main content

adk_memory/
adapter.rs

1//! Adapter bridging [`MemoryService`] to [`adk_core::Memory`].
2//!
3//! The runner expects `Arc<dyn adk_core::Memory>`, which has a simple
4//! `search(&str)` signature. [`MemoryService`] requires a [`SearchRequest`]
5//! with `app_name` and `user_id`. This adapter binds those fields at
6//! construction time so any `MemoryService` can be used as `adk_core::Memory`.
7//!
8//! # Example
9//!
10//! ```rust,ignore
11//! use adk_memory::{InMemoryMemoryService, MemoryServiceAdapter};
12//! use std::sync::Arc;
13//!
14//! let service = Arc::new(InMemoryMemoryService::new());
15//! let memory = Arc::new(MemoryServiceAdapter::new(service, "my-app", "user-1"));
16//! // memory implements adk_core::Memory and can be passed to RunnerConfig
17//! ```
18
19use std::sync::Arc;
20
21use async_trait::async_trait;
22use chrono::Utc;
23
24use crate::{MemoryService, SearchRequest};
25
26/// Adapts any [`MemoryService`] into an [`adk_core::Memory`] implementation.
27///
28/// Binds `app_name` and `user_id` at construction so the runner's
29/// `search(query: &str)` calls are forwarded with full context.
30pub struct MemoryServiceAdapter {
31    inner: Arc<dyn MemoryService>,
32    app_name: String,
33    user_id: String,
34    project_id: Option<String>,
35}
36
37impl MemoryServiceAdapter {
38    /// Create a new adapter binding a memory service to a specific app and user.
39    pub fn new(
40        inner: Arc<dyn MemoryService>,
41        app_name: impl Into<String>,
42        user_id: impl Into<String>,
43    ) -> Self {
44        Self { inner, app_name: app_name.into(), user_id: user_id.into(), project_id: None }
45    }
46
47    /// Bind this adapter to a specific project scope.
48    ///
49    /// When set, `search` includes project entries, and `add`/`delete`
50    /// operate within the project scope.
51    pub fn with_project_id(mut self, project_id: impl Into<String>) -> Self {
52        self.project_id = Some(project_id.into());
53        self
54    }
55}
56
57#[async_trait]
58impl adk_core::Memory for MemoryServiceAdapter {
59    async fn search(&self, query: &str) -> adk_core::Result<Vec<adk_core::MemoryEntry>> {
60        let inner = self.inner.clone();
61        let resp = inner
62            .search(SearchRequest {
63                query: query.to_string(),
64                app_name: self.app_name.clone(),
65                user_id: self.user_id.clone(),
66                limit: None,
67                min_score: None,
68                project_id: self.project_id.clone(),
69            })
70            .await?;
71
72        Ok(resp
73            .memories
74            .into_iter()
75            .map(|m| adk_core::MemoryEntry { content: m.content, author: m.author })
76            .collect())
77    }
78
79    async fn add(&self, entry: adk_core::MemoryEntry) -> adk_core::Result<()> {
80        let inner = self.inner.clone();
81        let mem_entry = crate::MemoryEntry {
82            content: entry.content,
83            author: entry.author,
84            timestamp: Utc::now(),
85        };
86        if let Some(ref pid) = self.project_id {
87            inner.add_entry_to_project(&self.app_name, &self.user_id, pid, mem_entry).await
88        } else {
89            inner.add_entry(&self.app_name, &self.user_id, mem_entry).await
90        }
91    }
92
93    async fn delete(&self, query: &str) -> adk_core::Result<u64> {
94        let inner = self.inner.clone();
95        if let Some(ref pid) = self.project_id {
96            inner.delete_entries_in_project(&self.app_name, &self.user_id, pid, query).await
97        } else {
98            inner.delete_entries(&self.app_name, &self.user_id, query).await
99        }
100    }
101
102    async fn health_check(&self) -> adk_core::Result<()> {
103        let inner = self.inner.clone();
104        inner.health_check().await
105    }
106
107    fn supports_project_scoping(&self) -> bool {
108        self.inner.supports_project_scoping()
109    }
110
111    async fn search_in_project(
112        &self,
113        query: &str,
114        project_id: &str,
115    ) -> adk_core::Result<Vec<adk_core::MemoryEntry>> {
116        let inner = self.inner.clone();
117        let resp = inner
118            .search(SearchRequest {
119                query: query.to_string(),
120                app_name: self.app_name.clone(),
121                user_id: self.user_id.clone(),
122                limit: None,
123                min_score: None,
124                project_id: Some(project_id.to_string()),
125            })
126            .await?;
127
128        Ok(resp
129            .memories
130            .into_iter()
131            .map(|m| adk_core::MemoryEntry { content: m.content, author: m.author })
132            .collect())
133    }
134
135    async fn add_to_project(
136        &self,
137        entry: adk_core::MemoryEntry,
138        project_id: &str,
139    ) -> adk_core::Result<()> {
140        let inner = self.inner.clone();
141        let mem_entry = crate::MemoryEntry {
142            content: entry.content,
143            author: entry.author,
144            timestamp: Utc::now(),
145        };
146        inner.add_entry_to_project(&self.app_name, &self.user_id, project_id, mem_entry).await
147    }
148}