use memvid_core::PutOptions;
use rig::memory::MemoryError;
use rig_memory_policy::{Committable, TextWriter};
use crate::dedup::{DedupSet, compute_key, hex_encode_key};
use crate::hook::MemoryConfig;
use crate::metadata::{FrameKind, MemvidFrameMetadata};
pub(crate) async fn write_frame<S>(
store: &S,
config: &MemoryConfig,
dedup: &DedupSet,
kind: FrameKind,
conversation_id: &str,
chat_role: &str,
text: &str,
) -> Result<bool, MemoryError>
where
S: TextWriter<Options = PutOptions>,
<S as TextWriter>::Error: std::error::Error + Send + Sync + 'static,
{
if text.is_empty() {
return Ok(false);
}
let scope = config.scope.as_deref();
let key = compute_key(kind.as_str(), conversation_id, chat_role, scope, text);
if dedup
.contains(&key)
.map_err(|err| MemoryError::backend(Box::new(err)))?
{
tracing::debug!(
target: "rig_memvid::frame_writer",
conversation_id,
role = chat_role,
kind = kind.as_str(),
"skipping duplicate frame",
);
return Ok(false);
}
let key_hex = hex_encode_key(&key);
let opts = build_put_options(config, kind, conversation_id, chat_role, &key_hex);
if let Err(err) = store.write_text(text, opts).await {
tracing::warn!(
target: "rig_memvid::frame_writer",
error = %err,
conversation_id,
role = chat_role,
kind = kind.as_str(),
"failed to persist frame into memory backend",
);
return Err(MemoryError::backend(Box::new(err)));
}
dedup
.insert(key)
.map_err(|err| MemoryError::backend(Box::new(err)))?;
#[cfg(feature = "observe")]
rig_tap::emit_kind(
conversation_id,
rig_tap::EventKind::MemoryFrameWritten {
frame_kind: kind.as_str().to_string(),
frame_count_after: None,
bytes_written: text.len(),
},
);
Ok(true)
}
pub(crate) async fn commit_if_each_turn<S>(
store: &S,
config: &MemoryConfig,
) -> Result<(), MemoryError>
where
S: Committable,
<S as Committable>::Error: std::error::Error + Send + Sync + 'static,
{
if config.commit_each_turn {
store
.commit()
.await
.map_err(|err| MemoryError::backend(Box::new(err)))?;
}
Ok(())
}
fn build_put_options(
config: &MemoryConfig,
kind: FrameKind,
conversation_id: &str,
chat_role: &str,
dedup_key_hex: &str,
) -> PutOptions {
let mut opts = PutOptions {
tags: config.default_tags.clone(),
auto_tag: config.auto_tag,
extract_dates: config.extract_dates,
extract_triplets: config.extract_triplets,
..PutOptions::default()
};
if let Some(scope) = config.scope.as_deref() {
opts.uri = Some(scope.to_string());
}
let metadata = MemvidFrameMetadata {
schema_version: 1,
kind,
conversation_id: conversation_id.to_string(),
chat_role: chat_role.to_string(),
dedup_key: dedup_key_hex.to_string(),
scope: config.scope.clone(),
};
opts.extra_metadata = metadata.into_map();
opts
}