use std::collections::HashMap;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::client::Client;
use crate::error::BoxxkiteError;
#[derive(Debug, Clone, Deserialize)]
pub struct Memory {
pub id: String,
pub scope: String,
pub kind: String,
pub content: String,
#[serde(default)]
pub metadata: HashMap<String, Value>,
pub source_session_id: Option<String>,
pub created_at: String,
pub updated_at: String,
pub expires_at: Option<String>,
pub source_content: Option<String>,
pub document_date: Option<String>,
#[serde(default)]
pub event_dates: Vec<String>,
pub importance: f64,
pub access_count: i64,
pub last_accessed_at: Option<String>,
pub superseded_at: Option<String>,
pub superseded_by_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MemorySearchHit {
#[serde(flatten)]
pub memory: Memory,
pub score: f64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MemorySearchResponse {
pub query: String,
pub memories: Vec<MemorySearchHit>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MemoryIngestResponse {
pub source_session_id: Option<String>,
pub memories: Vec<Memory>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MemoryProfileResponse {
pub r#static: Vec<Memory>,
pub dynamic: Vec<Memory>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MemoryRelation {
pub source_memory_id: String,
pub target_memory_id: String,
pub relation_type: String,
pub confidence: f64,
pub created_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MemoryRelationsResponse {
pub memory_id: String,
pub relations: Vec<MemoryRelation>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct RememberOptions {
#[serde(skip_serializing_if = "Option::is_none")]
scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
kind: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
source_session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
importance: Option<f64>,
}
impl RememberOptions {
pub fn new() -> Self {
Self::default()
}
pub fn scope(mut self, scope: impl Into<String>) -> Self {
self.scope = Some(scope.into());
self
}
pub fn kind(mut self, kind: impl Into<String>) -> Self {
self.kind = Some(kind.into());
self
}
pub fn metadata(mut self, metadata: HashMap<String, Value>) -> Self {
self.metadata = Some(metadata);
self
}
pub fn source_session_id(mut self, source_session_id: impl Into<String>) -> Self {
self.source_session_id = Some(source_session_id.into());
self
}
pub fn importance(mut self, importance: f64) -> Self {
self.importance = Some(importance);
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct IngestMemoryOptions {
#[serde(skip_serializing_if = "Option::is_none")]
scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
source_session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<HashMap<String, Value>>,
}
impl IngestMemoryOptions {
pub fn new() -> Self {
Self::default()
}
pub fn scope(mut self, scope: impl Into<String>) -> Self {
self.scope = Some(scope.into());
self
}
pub fn source_session_id(mut self, source_session_id: impl Into<String>) -> Self {
self.source_session_id = Some(source_session_id.into());
self
}
pub fn metadata(mut self, metadata: HashMap<String, Value>) -> Self {
self.metadata = Some(metadata);
self
}
}
#[derive(Debug, Clone, Default)]
pub struct ListMemoriesOptions {
pub scope: Option<String>,
pub include_superseded: bool,
pub limit: Option<u32>,
pub offset: Option<u32>,
}
impl Client {
pub async fn remember(
&self,
content: &str,
options: RememberOptions,
) -> Result<Memory, BoxxkiteError> {
#[derive(Serialize)]
struct Body<'a> {
content: &'a str,
#[serde(flatten)]
options: &'a RememberOptions,
}
let builder = self.request(Method::POST, "/v1/memory").json(&Body {
content,
options: &options,
});
self.send(builder).await
}
pub async fn ingest_memory(
&self,
content: &str,
options: IngestMemoryOptions,
) -> Result<MemoryIngestResponse, BoxxkiteError> {
#[derive(Serialize)]
struct Body<'a> {
content: &'a str,
#[serde(flatten)]
options: &'a IngestMemoryOptions,
}
let builder = self.request(Method::POST, "/v1/memory/ingest").json(&Body {
content,
options: &options,
});
self.send(builder).await
}
pub async fn recall(
&self,
query: &str,
scope: Option<&str>,
limit: Option<u32>,
) -> Result<MemorySearchResponse, BoxxkiteError> {
let mut params = vec![("q".to_string(), query.to_string())];
params.push(("limit".to_string(), limit.unwrap_or(10).to_string()));
if let Some(scope) = scope {
params.push(("scope".to_string(), scope.to_string()));
}
let builder = self
.request(Method::GET, "/v1/memory/search")
.query(¶ms);
self.send(builder).await
}
pub async fn memory_profile(
&self,
scope: Option<&str>,
limit: Option<u32>,
) -> Result<MemoryProfileResponse, BoxxkiteError> {
let mut params = vec![("limit".to_string(), limit.unwrap_or(20).to_string())];
if let Some(scope) = scope {
params.push(("scope".to_string(), scope.to_string()));
}
let builder = self
.request(Method::GET, "/v1/memory/profile")
.query(¶ms);
self.send(builder).await
}
pub async fn list_memories(
&self,
options: ListMemoriesOptions,
) -> Result<Vec<Memory>, BoxxkiteError> {
let mut params = vec![
(
"include_superseded".to_string(),
options.include_superseded.to_string(),
),
("limit".to_string(), options.limit.unwrap_or(50).to_string()),
(
"offset".to_string(),
options.offset.unwrap_or(0).to_string(),
),
];
if let Some(scope) = options.scope {
params.push(("scope".to_string(), scope));
}
let builder = self.request(Method::GET, "/v1/memory").query(¶ms);
self.send_or_default(builder).await
}
pub async fn get_memory(&self, memory_id: &str) -> Result<Memory, BoxxkiteError> {
let builder = self.request(Method::GET, &format!("/v1/memory/{memory_id}"));
self.send(builder).await
}
pub async fn memory_relations(
&self,
memory_id: &str,
limit: Option<u32>,
) -> Result<MemoryRelationsResponse, BoxxkiteError> {
let builder = self
.request(Method::GET, &format!("/v1/memory/{memory_id}/relations"))
.query(&[("limit", limit.unwrap_or(20).to_string())]);
self.send(builder).await
}
pub async fn forget_memory(&self, memory_id: &str) -> Result<(), BoxxkiteError> {
let builder = self.request(Method::DELETE, &format!("/v1/memory/{memory_id}"));
self.send_no_content(builder).await
}
pub async fn export_memories(&self, scope: Option<&str>) -> Result<Value, BoxxkiteError> {
let mut builder = self.request(Method::GET, "/v1/memory/export");
if let Some(scope) = scope {
builder = builder.query(&[("scope", scope)]);
}
self.send(builder).await
}
pub async fn import_memories(
&self,
memories: Vec<Value>,
relations: Vec<Value>,
) -> Result<MemoryIngestResponse, BoxxkiteError> {
#[derive(Serialize)]
struct Body {
memories: Vec<Value>,
relations: Vec<Value>,
}
let builder = self.request(Method::POST, "/v1/memory/import").json(&Body {
memories,
relations,
});
self.send(builder).await
}
}