use crate::types::common::{LettaId, Metadata, Timestamp};
use bon::Builder;
use serde::{Deserialize, Serialize};
use super::EmbeddingConfig;
#[derive(Debug, Clone, Default, Serialize, Deserialize, Builder)]
pub struct CreateBlockRequest {
pub value: String,
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_template: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub preserve_on_migration: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub read_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UpdateBlockRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_template: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preserve_on_migration: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub read_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ListBlocksParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub templates_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub identity_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub identifier_keys: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<LettaId>,
pub label: String,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(default)]
pub is_template: bool,
#[serde(default)]
pub preserve_on_migration: bool,
#[serde(default)]
pub read_only: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated_by_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<Timestamp>,
}
impl Block {
pub fn human(value: impl Into<String>) -> Self {
Self {
id: None,
label: "human".to_string(),
value: value.into(),
limit: Some(2000),
is_template: false,
preserve_on_migration: false,
read_only: false,
description: Some("Information about the human user".to_string()),
metadata: None,
name: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: None,
updated_at: None,
}
}
pub fn persona(value: impl Into<String>) -> Self {
Self {
id: None,
label: "persona".to_string(),
value: value.into(),
limit: Some(2000),
is_template: false,
preserve_on_migration: false,
read_only: false,
description: Some("The agent's persona".to_string()),
metadata: None,
name: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: None,
updated_at: None,
}
}
pub fn new(label: impl Into<String>, value: impl Into<String>) -> Self {
Self {
id: None,
label: label.into(),
value: value.into(),
limit: None,
is_template: false,
preserve_on_migration: false,
read_only: false,
description: None,
metadata: None,
name: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: None,
updated_at: None,
}
}
pub fn with_limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn as_template(mut self) -> Self {
self.is_template = true;
self
}
pub fn as_read_only(mut self) -> Self {
self.read_only = true;
self
}
pub fn preserve_on_migration(mut self) -> Self {
self.preserve_on_migration = true;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Passage {
pub id: LettaId,
pub text: String,
pub agent_id: LettaId,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding: Option<Vec<f32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding_config: Option<EmbeddingConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated_by_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<Timestamp>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_deleted: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateArchivalMemoryRequest {
pub text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateArchivalMemoryRequest {
pub id: LettaId,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated_by_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<Timestamp>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_deleted: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_id: Option<LettaId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
#[serde(rename = "metadata", skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization_id: Option<LettaId>,
pub text: String,
pub embedding: Vec<f32>,
pub embedding_config: EmbeddingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ArchivalMemoryQueryParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub search: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub after: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ascending: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Memory {
pub blocks: Vec<Block>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_blocks: Option<Vec<Block>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_template: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateCoreMemoryRequest {
pub value: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UpdateMemoryBlockRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preserve_on_migration: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub read_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateMemoryBlockRequest {
pub label: String,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default)]
pub is_template: bool,
#[serde(default)]
pub preserve_on_migration: bool,
#[serde(default)]
pub read_only: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MemoryQueryParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub query: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub after: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SummarizeMessagesRequest {
#[serde(default)]
pub force: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SummarizeMessagesResponse {
pub summary: String,
pub messages_summarized: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecallMemorySummary {
#[serde(skip_serializing_if = "Option::is_none")]
pub core_memory: Option<Memory>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub archival_memory: Vec<Passage>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub recall_memory: Vec<RecallMemoryEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecallMemoryEntry {
pub id: LettaId,
pub agent_id: LettaId,
pub role: String,
pub content: String,
pub created_at: Timestamp,
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_block_serialization() {
let block = Block {
id: Some(LettaId::from_str("block-550e8400-e29b-41d4-a716-446655440000").unwrap()),
label: "human".to_string(),
value: "The human's name is Alice.".to_string(),
limit: Some(1000),
is_template: false,
preserve_on_migration: true,
read_only: false,
description: Some("Human information".to_string()),
metadata: None,
name: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: None,
updated_at: None,
};
let json = serde_json::to_string(&block).unwrap();
let deserialized: Block = serde_json::from_str(&json).unwrap();
assert_eq!(block.label, deserialized.label);
assert_eq!(block.value, deserialized.value);
}
#[test]
fn test_memory_structure() {
let memory = Memory {
blocks: vec![
Block {
id: Some(
LettaId::from_str("block-550e8400-e29b-41d4-a716-446655440001").unwrap(),
),
label: "human".to_string(),
value: "Name: Alice".to_string(),
limit: Some(1000),
is_template: false,
preserve_on_migration: true,
read_only: false,
description: None,
metadata: None,
name: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: None,
updated_at: None,
},
Block {
id: Some(
LettaId::from_str("block-550e8400-e29b-41d4-a716-446655440002").unwrap(),
),
label: "persona".to_string(),
value: "I am a helpful assistant".to_string(),
limit: Some(500),
is_template: false,
preserve_on_migration: true,
read_only: false,
description: None,
metadata: None,
name: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: None,
updated_at: None,
},
],
file_blocks: None,
prompt_template: Some("{{human}}\n{{persona}}".to_string()),
};
let json = serde_json::to_string(&memory).unwrap();
let parsed: Memory = serde_json::from_str(&json).unwrap();
assert_eq!(memory.blocks.len(), parsed.blocks.len());
assert_eq!(memory.prompt_template, parsed.prompt_template);
}
#[test]
fn test_passage_serialization() {
let passage = Passage {
id: LettaId::from_str("passage-550e8400-e29b-41d4-a716-446655440003").unwrap(),
text: "Important information to remember".to_string(),
agent_id: LettaId::from_str("agent-00000000-0000-0000-0000-000000000000").unwrap(),
embedding: Some(vec![0.1, 0.2, 0.3]),
embedding_config: None,
source_id: None,
file_id: None,
file_name: None,
metadata: None,
organization_id: None,
created_by_id: None,
last_updated_by_id: None,
created_at: Some(chrono::Utc::now()),
updated_at: None,
is_deleted: None,
};
let json = serde_json::to_string(&passage).unwrap();
let deserialized: Passage = serde_json::from_str(&json).unwrap();
assert_eq!(passage.text, deserialized.text);
assert_eq!(passage.id, deserialized.id);
}
#[test]
fn test_memory_query_params() {
let params = MemoryQueryParams {
query: Some("search term".to_string()),
limit: Some(10),
before: None,
after: None,
};
let json = serde_json::to_string(¶ms).unwrap();
assert!(json.contains("query"));
assert!(!json.contains("before")); }
}