lance-context 0.2.4__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {lance_context-0.2.4 → lance_context-0.3.0}/PKG-INFO +7 -6
  2. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context/Cargo.toml +2 -2
  3. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context-core/Cargo.toml +11 -8
  4. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context-core/src/lib.rs +5 -2
  5. lance_context-0.3.0/crates/lance-context-core/src/record.rs +197 -0
  6. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context-core/src/serde.rs +1 -0
  7. lance_context-0.3.0/crates/lance-context-core/src/store.rs +2016 -0
  8. {lance_context-0.2.4 → lance_context-0.3.0}/pyproject.toml +8 -6
  9. {lance_context-0.2.4 → lance_context-0.3.0}/python/Cargo.toml +2 -1
  10. lance_context-0.3.0/python/lance_context/__init__.py +9 -0
  11. lance_context-0.3.0/python/lance_context/api.py +687 -0
  12. {lance_context-0.2.4 → lance_context-0.3.0}/python/python/tests/test_context.py +13 -0
  13. lance_context-0.3.0/python/src/lib.rs +741 -0
  14. lance_context-0.3.0/python/tests/test_add_many.py +67 -0
  15. lance_context-0.3.0/python/tests/test_async.py +177 -0
  16. {lance_context-0.2.4 → lance_context-0.3.0}/python/tests/test_compaction.py +8 -5
  17. lance_context-0.3.0/python/tests/test_delete.py +97 -0
  18. lance_context-0.3.0/python/tests/test_external_id.py +50 -0
  19. lance_context-0.3.0/python/tests/test_gcs_persistence.py +90 -0
  20. lance_context-0.3.0/python/tests/test_id_index.py +84 -0
  21. {lance_context-0.2.4 → lance_context-0.3.0}/python/tests/test_persistence.py +125 -10
  22. lance_context-0.3.0/python/tests/test_search.py +670 -0
  23. lance_context-0.3.0/python/tests/test_storage_options.py +176 -0
  24. {lance_context-0.2.4 → lance_context-0.3.0}/python/uv.lock +669 -33
  25. lance_context-0.2.4/crates/lance-context-core/src/record.rs +0 -33
  26. lance_context-0.2.4/crates/lance-context-core/src/store.rs +0 -953
  27. lance_context-0.2.4/python/lance_context/__init__.py +0 -5
  28. lance_context-0.2.4/python/lance_context/api.py +0 -343
  29. lance_context-0.2.4/python/src/lib.rs +0 -406
  30. lance_context-0.2.4/python/tests/test_search.py +0 -292
  31. {lance_context-0.2.4 → lance_context-0.3.0}/Cargo.toml +0 -0
  32. {lance_context-0.2.4 → lance_context-0.3.0}/LICENSE +0 -0
  33. {lance_context-0.2.4 → lance_context-0.3.0}/README.md +0 -0
  34. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context/README.md +0 -0
  35. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context/src/lib.rs +0 -0
  36. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context-core/README.md +0 -0
  37. {lance_context-0.2.4 → lance_context-0.3.0}/crates/lance-context-core/src/context.rs +0 -0
  38. {lance_context-0.2.4 → lance_context-0.3.0}/python/Cargo.lock +0 -0
  39. {lance_context-0.2.4 → lance_context-0.3.0}/python/LICENSE +0 -0
  40. {lance_context-0.2.4 → lance_context-0.3.0}/python/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lance-context
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: License :: OSI Approved :: Apache Software License
@@ -14,15 +14,16 @@ Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Rust
16
16
  Classifier: Topic :: Scientific/Engineering
17
- Requires-Dist: pylance==1.0.2
18
- Requires-Dist: lancedb==0.26.1
19
- Requires-Dist: lance-namespace==0.4.5
20
- Requires-Dist: lance-graph==0.4.0
17
+ Requires-Dist: pylance==7.0.0
18
+ Requires-Dist: lancedb==0.27.1
19
+ Requires-Dist: lance-namespace==0.7.7
20
+ Requires-Dist: lance-graph==0.5.4
21
21
  Requires-Dist: ruff ; extra == 'dev'
22
22
  Requires-Dist: pyright ; extra == 'dev'
23
23
  Requires-Dist: pytest ; extra == 'tests'
24
+ Requires-Dist: pytest-asyncio ; extra == 'tests'
24
25
  Requires-Dist: ruff ; extra == 'tests'
25
- Requires-Dist: moto[s3] ; extra == 'tests'
26
+ Requires-Dist: moto[s3,server] ; extra == 'tests'
26
27
  Requires-Dist: boto3 ; extra == 'tests'
27
28
  Requires-Dist: botocore ; extra == 'tests'
28
29
  Provides-Extra: dev
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lance-context"
3
- version = "0.2.4"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
  license = "Apache-2.0"
6
6
  authors = ["Lance Devs <dev@lancedb.com>"]
@@ -9,4 +9,4 @@ description = "Public re-export crate for lance-context bindings"
9
9
  readme = "README.md"
10
10
 
11
11
  [dependencies]
12
- lance-context-core = { version = "0.2.4", path = "../lance-context-core" }
12
+ lance-context-core = { version = "0.3.0", path = "../lance-context-core" }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lance-context-core"
3
- version = "0.2.4"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
  license = "Apache-2.0"
6
6
  authors = ["Lance Devs <dev@lancedb.com>"]
@@ -11,18 +11,21 @@ keywords = ["context", "multimodal", "lance", "agents", "storage"]
11
11
  categories = ["database", "data-structures", "science"]
12
12
 
13
13
  [dependencies]
14
- arrow-array = "56.2.0"
15
- arrow-ipc = "56.2.0"
16
- arrow-schema = "56.2.0"
14
+ arrow-array = "58"
15
+ arrow-ipc = "58"
16
+ arrow-schema = "58"
17
17
  chrono = { version = "0.4", default-features = false, features = ["clock"] }
18
- lance = "1.0.0"
19
- lancedb = "0.23.1"
20
- lance-namespace = "1.0.1"
21
- lance-graph = "0.4.0"
18
+ lance = "7.0.0"
19
+ lance-index = "7.0.0"
20
+ lance-namespace = "7.0.0"
21
+ lancedb = "0.30.0"
22
+ lance-graph = "0.5.4"
22
23
  serde = { version = "1", features = ["derive"] }
24
+ serde_json = "1"
23
25
  futures = "0.3"
24
26
  tokio = { version = "1", features = ["sync", "time"] }
25
27
  tracing = "0.1"
28
+ uuid = { version = "1.20.0", features = ["v4", "v5"] }
26
29
 
27
30
  [dev-dependencies]
28
31
  tempfile = "3"
@@ -1,4 +1,5 @@
1
1
  //! Core types for the lance-context storage layer.
2
+ #![recursion_limit = "256"]
2
3
 
3
4
  mod context;
4
5
  mod record;
@@ -6,8 +7,10 @@ pub mod serde;
6
7
  mod store;
7
8
 
8
9
  pub use context::{Context, ContextEntry, Snapshot};
9
- pub use record::{ContextRecord, SearchResult, StateMetadata};
10
- pub use store::{CompactionConfig, CompactionStats, ContextStore, ContextStoreOptions};
10
+ pub use record::{ContextRecord, MetadataFilter, RecordFilters, SearchResult, StateMetadata};
11
+ pub use store::{
12
+ CompactionConfig, CompactionStats, ContextStore, ContextStoreOptions, IdIndexType,
13
+ };
11
14
 
12
15
  // Re-export CompactionMetrics from lance for Python bindings
13
16
  pub use lance::dataset::optimize::CompactionMetrics;
@@ -0,0 +1,197 @@
1
+ use chrono::{DateTime, Utc};
2
+ use serde_json::Value;
3
+ use std::collections::HashMap;
4
+
5
+ use crate::serde::CONTENT_TYPE_TOMBSTONE;
6
+
7
+ /// Structured metadata captured alongside each context entry.
8
+ #[derive(Debug, Clone, Default)]
9
+ pub struct StateMetadata {
10
+ pub step: Option<i32>,
11
+ pub active_plan_id: Option<String>,
12
+ pub tokens_used: Option<i32>,
13
+ pub custom: Option<String>,
14
+ }
15
+
16
+ /// User-facing representation of a context entry written to storage.
17
+ #[derive(Debug, Clone)]
18
+ pub struct ContextRecord {
19
+ pub id: String,
20
+ pub external_id: Option<String>,
21
+ pub run_id: String,
22
+ pub bot_id: Option<String>,
23
+ pub session_id: Option<String>,
24
+ pub created_at: DateTime<Utc>,
25
+ pub role: String,
26
+ pub state_metadata: Option<StateMetadata>,
27
+ pub metadata: Option<Value>,
28
+ pub content_type: String,
29
+ pub text_payload: Option<String>,
30
+ pub binary_payload: Option<Vec<u8>>,
31
+ pub embedding: Option<Vec<f32>>,
32
+ }
33
+
34
+ impl ContextRecord {
35
+ #[must_use]
36
+ pub fn is_tombstone(&self) -> bool {
37
+ self.content_type == CONTENT_TYPE_TOMBSTONE
38
+ }
39
+ }
40
+
41
+ /// Result returned from a vector similarity search.
42
+ #[derive(Debug, Clone)]
43
+ pub struct SearchResult {
44
+ pub record: ContextRecord,
45
+ pub distance: f32,
46
+ }
47
+
48
+ /// Metadata matching operation for filtered retrieval.
49
+ #[derive(Debug, Clone, PartialEq)]
50
+ pub enum MetadataFilter {
51
+ Equals(Value),
52
+ Contains(Value),
53
+ }
54
+
55
+ /// Filters applied to records before list pagination or search ranking.
56
+ #[derive(Debug, Clone, Default, PartialEq)]
57
+ pub struct RecordFilters {
58
+ pub bot_id: Option<String>,
59
+ pub session_id: Option<String>,
60
+ pub role: Option<String>,
61
+ pub content_type: Option<String>,
62
+ pub created_at_start: Option<DateTime<Utc>>,
63
+ pub created_at_end: Option<DateTime<Utc>>,
64
+ pub metadata: HashMap<String, MetadataFilter>,
65
+ }
66
+
67
+ impl RecordFilters {
68
+ #[must_use]
69
+ pub fn is_empty(&self) -> bool {
70
+ self.bot_id.is_none()
71
+ && self.session_id.is_none()
72
+ && self.role.is_none()
73
+ && self.content_type.is_none()
74
+ && self.created_at_start.is_none()
75
+ && self.created_at_end.is_none()
76
+ && self.metadata.is_empty()
77
+ }
78
+
79
+ #[must_use]
80
+ pub fn matches(&self, record: &ContextRecord) -> bool {
81
+ if self
82
+ .bot_id
83
+ .as_deref()
84
+ .is_some_and(|value| record.bot_id.as_deref() != Some(value))
85
+ {
86
+ return false;
87
+ }
88
+ if self
89
+ .session_id
90
+ .as_deref()
91
+ .is_some_and(|value| record.session_id.as_deref() != Some(value))
92
+ {
93
+ return false;
94
+ }
95
+ if self
96
+ .role
97
+ .as_deref()
98
+ .is_some_and(|value| record.role != value)
99
+ {
100
+ return false;
101
+ }
102
+ if self
103
+ .content_type
104
+ .as_deref()
105
+ .is_some_and(|value| record.content_type != value)
106
+ {
107
+ return false;
108
+ }
109
+ if self
110
+ .created_at_start
111
+ .is_some_and(|start| record.created_at < start)
112
+ {
113
+ return false;
114
+ }
115
+ if self
116
+ .created_at_end
117
+ .is_some_and(|end| record.created_at > end)
118
+ {
119
+ return false;
120
+ }
121
+
122
+ self.metadata.iter().all(|(key, filter)| {
123
+ let Some(Value::Object(metadata)) = &record.metadata else {
124
+ return false;
125
+ };
126
+ let Some(value) = metadata.get(key) else {
127
+ return false;
128
+ };
129
+ match filter {
130
+ MetadataFilter::Equals(expected) => value == expected,
131
+ MetadataFilter::Contains(expected) => metadata_contains(value, expected),
132
+ }
133
+ })
134
+ }
135
+ }
136
+
137
+ fn metadata_contains(value: &Value, expected: &Value) -> bool {
138
+ match (value, expected) {
139
+ (Value::Array(items), expected) => items.iter().any(|item| item == expected),
140
+ (Value::String(value), Value::String(expected)) => value.contains(expected),
141
+ _ => false,
142
+ }
143
+ }
144
+
145
+ #[cfg(test)]
146
+ mod tests {
147
+ use super::*;
148
+ use chrono::TimeZone;
149
+ use serde_json::json;
150
+
151
+ fn record() -> ContextRecord {
152
+ ContextRecord {
153
+ id: "rec-1".to_string(),
154
+ external_id: None,
155
+ run_id: "run-1".to_string(),
156
+ bot_id: Some("support-bot".to_string()),
157
+ session_id: Some("incident-1".to_string()),
158
+ created_at: Utc.with_ymd_and_hms(2026, 6, 9, 3, 0, 0).unwrap(),
159
+ role: "assistant".to_string(),
160
+ state_metadata: None,
161
+ metadata: Some(json!({
162
+ "scope": "team",
163
+ "tags": ["runbook", "ownership"],
164
+ "confidence": 0.92
165
+ })),
166
+ content_type: "text/plain".to_string(),
167
+ text_payload: Some("hello".to_string()),
168
+ binary_payload: None,
169
+ embedding: None,
170
+ }
171
+ }
172
+
173
+ #[test]
174
+ fn filters_match_builtin_fields_timestamps_and_metadata() {
175
+ let mut filters = RecordFilters {
176
+ bot_id: Some("support-bot".to_string()),
177
+ session_id: Some("incident-1".to_string()),
178
+ role: Some("assistant".to_string()),
179
+ content_type: Some("text/plain".to_string()),
180
+ created_at_start: Some(Utc.with_ymd_and_hms(2026, 6, 9, 2, 0, 0).unwrap()),
181
+ created_at_end: Some(Utc.with_ymd_and_hms(2026, 6, 9, 4, 0, 0).unwrap()),
182
+ metadata: HashMap::new(),
183
+ };
184
+ filters
185
+ .metadata
186
+ .insert("scope".to_string(), MetadataFilter::Equals(json!("team")));
187
+ filters.metadata.insert(
188
+ "tags".to_string(),
189
+ MetadataFilter::Contains(json!("runbook")),
190
+ );
191
+
192
+ assert!(filters.matches(&record()));
193
+
194
+ filters.session_id = Some("other".to_string());
195
+ assert!(!filters.matches(&record()));
196
+ }
197
+ }
@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
5
5
 
6
6
  pub const CONTENT_TYPE_TEXT: &str = "text/plain";
7
7
  pub const CONTENT_TYPE_ARROW_STREAM: &str = "application/vnd.apache.arrow.stream";
8
+ pub const CONTENT_TYPE_TOMBSTONE: &str = "application/vnd.lance-context.tombstone";
8
9
 
9
10
  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10
11
  pub struct SerializedContent {