remem-ai 0.5.96

Persistent memory for Claude Code and OpenAI Codex coding agents
Documentation
use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};

use crate::{db, memory};

use super::types::{ErrorDetail, ErrorResponse, MemoryItem};

pub(super) fn memory_to_item_with_conn(
    conn: &rusqlite::Connection,
    memory: &memory::Memory,
) -> MemoryItem {
    let now_epoch = chrono::Utc::now().timestamp();
    let staleness = match memory::memory_staleness_label_with_conn(conn, memory, now_epoch) {
        Ok(label) => label,
        Err(err) => {
            crate::log::warn(
                "api",
                &format!(
                    "memory {} staleness source-anchor lookup failed: {}",
                    memory.id, err
                ),
            );
            memory::memory_staleness_label(memory, now_epoch)
        }
    };
    memory_to_item_with_staleness(memory, staleness)
}

pub(super) fn memories_to_items_with_conn(
    conn: &rusqlite::Connection,
    memories: &[memory::Memory],
) -> Vec<MemoryItem> {
    let now_epoch = chrono::Utc::now().timestamp();
    let staleness_labels = memory::staleness::memory_staleness_labels_for_memories_lossy(
        conn,
        memories,
        now_epoch,
        |id, error| {
            crate::log::warn(
                "api",
                &format!("memory {id} staleness source-anchor lookup failed: {error}"),
            );
        },
    )
    .unwrap_or_else(|error| {
        crate::log::warn("api", &format!("staleness batch fallback: {error}"));
        memories
            .iter()
            .map(|memory| (memory.id, memory::memory_staleness_label(memory, now_epoch)))
            .collect()
    });
    memories
        .iter()
        .map(|memory| {
            let staleness = staleness_labels
                .get(&memory.id)
                .cloned()
                .unwrap_or_else(|| memory::memory_staleness_label(memory, now_epoch));
            memory_to_item_with_staleness(memory, staleness)
        })
        .collect()
}

fn memory_to_item_with_staleness(
    memory: &memory::Memory,
    staleness: memory::MemoryStalenessLabel,
) -> MemoryItem {
    MemoryItem {
        id: memory.id,
        title: memory.title.clone(),
        content: memory.text.clone(),
        memory_type: memory.memory_type.clone(),
        project: memory.project.clone(),
        scope: memory.scope.clone(),
        status: memory.status.clone(),
        staleness,
        topic_key: memory.topic_key.clone(),
        branch: memory.branch.clone(),
        created_at_epoch: memory.created_at_epoch,
        updated_at_epoch: memory.updated_at_epoch,
    }
}

pub(super) fn error_response(status: StatusCode, code: &str, message: &str) -> impl IntoResponse {
    (
        status,
        Json(ErrorResponse {
            error: ErrorDetail {
                code: code.to_string(),
                message: message.to_string(),
            },
        }),
    )
}

#[allow(clippy::result_large_err)]
pub(super) fn open_request_db() -> Result<rusqlite::Connection, Response> {
    db::open_db().map_err(|err| {
        error_response(
            StatusCode::INTERNAL_SERVER_ERROR,
            "db_open_failed",
            &err.to_string(),
        )
        .into_response()
    })
}