Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ let record = ContextRecord {
custom: None,
}),
metadata: None,
expires_at: None,
retention_policy: None,
lifecycle_status: "active".into(),
retired_at: None,
retired_reason: None,
supersedes_id: None,
superseded_by_id: None,
content_type: "text/plain".into(),
text_payload: Some("hello world".into()),
binary_payload: None,
Expand Down
5 changes: 4 additions & 1 deletion crates/lance-context-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ pub mod serde;
mod store;

pub use context::{Context, ContextEntry, Snapshot};
pub use record::{ContextRecord, MetadataFilter, RecordFilters, SearchResult, StateMetadata};
pub use record::{
ContextRecord, LifecycleQueryOptions, MetadataFilter, RecordFilters, SearchResult,
StateMetadata, LIFECYCLE_ACTIVE, LIFECYCLE_CONTRADICTED,
};
pub use store::{
CompactionConfig, CompactionStats, ContextStore, ContextStoreOptions, IdIndexType,
};
Expand Down
80 changes: 80 additions & 0 deletions crates/lance-context-core/src/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use std::collections::HashMap;

use crate::serde::CONTENT_TYPE_TOMBSTONE;

pub const LIFECYCLE_ACTIVE: &str = "active";
pub const LIFECYCLE_CONTRADICTED: &str = "contradicted";

/// Structured metadata captured alongside each context entry.
#[derive(Debug, Clone, Default)]
pub struct StateMetadata {
Expand All @@ -25,6 +28,13 @@ pub struct ContextRecord {
pub role: String,
pub state_metadata: Option<StateMetadata>,
pub metadata: Option<Value>,
pub expires_at: Option<DateTime<Utc>>,
pub retention_policy: Option<String>,
pub lifecycle_status: String,
pub retired_at: Option<DateTime<Utc>>,
pub retired_reason: Option<String>,
pub supersedes_id: Option<String>,
pub superseded_by_id: Option<String>,
pub content_type: String,
pub text_payload: Option<String>,
pub binary_payload: Option<Vec<u8>>,
Expand All @@ -36,6 +46,69 @@ impl ContextRecord {
pub fn is_tombstone(&self) -> bool {
self.content_type == CONTENT_TYPE_TOMBSTONE
}

#[must_use]
pub fn is_expired_at(&self, now: DateTime<Utc>) -> bool {
self.expires_at.is_some_and(|expires_at| expires_at <= now)
}

#[must_use]
pub fn is_hidden_by_lifecycle(&self) -> bool {
if self.lifecycle_status == LIFECYCLE_ACTIVE
|| self.lifecycle_status == LIFECYCLE_CONTRADICTED
{
return self.retired_at.is_some() || self.superseded_by_id.is_some();
}

true
}

#[must_use]
pub fn has_non_default_lifecycle(&self) -> bool {
self.expires_at.is_some()
|| self.retention_policy.is_some()
|| self.lifecycle_status != LIFECYCLE_ACTIVE
|| self.retired_at.is_some()
|| self.retired_reason.is_some()
|| self.supersedes_id.is_some()
|| self.superseded_by_id.is_some()
}
}

/// Query-time controls for lifecycle-aware retrieval.
#[derive(Debug, Clone)]
pub struct LifecycleQueryOptions {
pub include_expired: bool,
pub include_retired: bool,
pub reference_time: DateTime<Utc>,
}

impl Default for LifecycleQueryOptions {
fn default() -> Self {
Self {
include_expired: false,
include_retired: false,
reference_time: Utc::now(),
}
}
}

impl LifecycleQueryOptions {
#[must_use]
pub fn new(include_expired: bool, include_retired: bool) -> Self {
Self {
include_expired,
include_retired,
..Self::default()
}
}

#[must_use]
pub fn is_visible(&self, record: &ContextRecord) -> bool {
!record.is_tombstone()
&& (self.include_expired || !record.is_expired_at(self.reference_time))
&& (self.include_retired || !record.is_hidden_by_lifecycle())
}
}

/// Result returned from a vector similarity search.
Expand Down Expand Up @@ -163,6 +236,13 @@ mod tests {
"tags": ["runbook", "ownership"],
"confidence": 0.92
})),
expires_at: None,
retention_policy: None,
lifecycle_status: LIFECYCLE_ACTIVE.to_string(),
retired_at: None,
retired_reason: None,
supersedes_id: None,
superseded_by_id: None,
content_type: "text/plain".to_string(),
text_payload: Some("hello".to_string()),
binary_payload: None,
Expand Down
Loading
Loading