use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct RetryConfig {
pub max_retries: u32,
pub base_delay: std::time::Duration,
pub max_delay: std::time::Duration,
pub jitter: bool,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_retries: 3,
base_delay: std::time::Duration::from_millis(100),
max_delay: std::time::Duration::from_secs(60),
jitter: true,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct RateLimitHeaders {
pub limit: Option<u64>,
pub remaining: Option<u64>,
pub reset: Option<u64>,
pub quota_used: Option<u64>,
pub quota_limit: Option<u64>,
}
impl RateLimitHeaders {
pub fn from_response(response: &reqwest::Response) -> Self {
let headers = response.headers();
fn parse(h: &reqwest::header::HeaderMap, name: &str) -> Option<u64> {
h.get(name)
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse().ok())
}
Self {
limit: parse(headers, "X-RateLimit-Limit"),
remaining: parse(headers, "X-RateLimit-Remaining"),
reset: parse(headers, "X-RateLimit-Reset"),
quota_used: parse(headers, "X-Quota-Used"),
quota_limit: parse(headers, "X-Quota-Limit"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
pub healthy: bool,
pub version: Option<String>,
pub uptime_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadinessResponse {
pub ready: bool,
pub components: Option<HashMap<String, bool>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceInfo {
#[serde(alias = "namespace")]
pub name: String,
#[serde(default)]
pub vector_count: u64,
#[serde(alias = "dimension")]
pub dimensions: Option<u32>,
pub index_type: Option<String>,
#[serde(default)]
pub created: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListNamespacesResponse {
pub namespaces: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Vector {
pub id: String,
pub values: Vec<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
impl Vector {
pub fn new(id: impl Into<String>, values: Vec<f32>) -> Self {
Self {
id: id.into(),
values,
metadata: None,
}
}
pub fn with_metadata(
id: impl Into<String>,
values: Vec<f32>,
metadata: HashMap<String, serde_json::Value>,
) -> Self {
Self {
id: id.into(),
values,
metadata: Some(metadata),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpsertRequest {
pub vectors: Vec<Vector>,
}
impl UpsertRequest {
pub fn single(vector: Vector) -> Self {
Self {
vectors: vec![vector],
}
}
pub fn batch(vectors: Vec<Vector>) -> Self {
Self { vectors }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpsertResponse {
pub upserted_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColumnUpsertRequest {
pub ids: Vec<String>,
pub vectors: Vec<Vec<f32>>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub attributes: HashMap<String, Vec<serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dimension: Option<usize>,
}
impl ColumnUpsertRequest {
pub fn new(ids: Vec<String>, vectors: Vec<Vec<f32>>) -> Self {
Self {
ids,
vectors,
attributes: HashMap::new(),
ttl_seconds: None,
dimension: None,
}
}
pub fn with_attribute(
mut self,
name: impl Into<String>,
values: Vec<serde_json::Value>,
) -> Self {
self.attributes.insert(name.into(), values);
self
}
pub fn with_ttl(mut self, seconds: u64) -> Self {
self.ttl_seconds = Some(seconds);
self
}
pub fn with_dimension(mut self, dim: usize) -> Self {
self.dimension = Some(dim);
self
}
pub fn len(&self) -> usize {
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteRequest {
pub ids: Vec<String>,
}
impl DeleteRequest {
pub fn single(id: impl Into<String>) -> Self {
Self {
ids: vec![id.into()],
}
}
pub fn batch(ids: Vec<String>) -> Self {
Self { ids }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteResponse {
pub deleted_count: u64,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ReadConsistency {
Strong,
#[default]
Eventual,
#[serde(rename = "bounded_staleness")]
BoundedStaleness,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct StalenessConfig {
#[serde(default = "default_max_staleness_ms")]
pub max_staleness_ms: u64,
}
fn default_max_staleness_ms() -> u64 {
5000 }
impl StalenessConfig {
pub fn new(max_staleness_ms: u64) -> Self {
Self { max_staleness_ms }
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DistanceMetric {
#[default]
Cosine,
Euclidean,
DotProduct,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryRequest {
pub vector: Vec<f32>,
pub top_k: u32,
#[serde(default)]
pub distance_metric: DistanceMetric,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
#[serde(default = "default_true")]
pub include_metadata: bool,
#[serde(default)]
pub include_vectors: bool,
#[serde(default)]
pub consistency: ReadConsistency,
#[serde(skip_serializing_if = "Option::is_none")]
pub staleness_config: Option<StalenessConfig>,
}
fn default_true() -> bool {
true
}
impl QueryRequest {
pub fn new(vector: Vec<f32>, top_k: u32) -> Self {
Self {
vector,
top_k,
distance_metric: DistanceMetric::default(),
filter: None,
include_metadata: true,
include_vectors: false,
consistency: ReadConsistency::default(),
staleness_config: None,
}
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn include_metadata(mut self, include: bool) -> Self {
self.include_metadata = include;
self
}
pub fn include_vectors(mut self, include: bool) -> Self {
self.include_vectors = include;
self
}
pub fn with_distance_metric(mut self, metric: DistanceMetric) -> Self {
self.distance_metric = metric;
self
}
pub fn with_consistency(mut self, consistency: ReadConsistency) -> Self {
self.consistency = consistency;
self
}
pub fn with_bounded_staleness(mut self, max_staleness_ms: u64) -> Self {
self.consistency = ReadConsistency::BoundedStaleness;
self.staleness_config = Some(StalenessConfig::new(max_staleness_ms));
self
}
pub fn with_strong_consistency(mut self) -> Self {
self.consistency = ReadConsistency::Strong;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Match {
pub id: String,
pub score: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResponse {
#[serde(alias = "matches")]
pub results: Vec<Match>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
pub id: String,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
impl Document {
pub fn new(id: impl Into<String>, text: impl Into<String>) -> Self {
Self {
id: id.into(),
text: text.into(),
metadata: None,
}
}
pub fn with_metadata(
id: impl Into<String>,
text: impl Into<String>,
metadata: HashMap<String, serde_json::Value>,
) -> Self {
Self {
id: id.into(),
text: text.into(),
metadata: Some(metadata),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexDocumentsRequest {
pub documents: Vec<Document>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexDocumentsResponse {
pub indexed_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullTextSearchRequest {
pub query: String,
pub top_k: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
}
impl FullTextSearchRequest {
pub fn new(query: impl Into<String>, top_k: u32) -> Self {
Self {
query: query.into(),
top_k,
filter: None,
}
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullTextMatch {
pub id: String,
pub score: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullTextSearchResponse {
#[serde(alias = "matches")]
pub results: Vec<FullTextMatch>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullTextStats {
pub document_count: u64,
pub term_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HybridSearchRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub vector: Option<Vec<f32>>,
pub text: String,
pub top_k: u32,
#[serde(default = "default_vector_weight")]
pub vector_weight: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
}
fn default_vector_weight() -> f32 {
0.5
}
impl HybridSearchRequest {
pub fn new(vector: Vec<f32>, text: impl Into<String>, top_k: u32) -> Self {
Self {
vector: Some(vector),
text: text.into(),
top_k,
vector_weight: 0.5,
filter: None,
}
}
pub fn text_only(text: impl Into<String>, top_k: u32) -> Self {
Self {
vector: None,
text: text.into(),
top_k,
vector_weight: 0.5,
filter: None,
}
}
pub fn with_vector_weight(mut self, weight: f32) -> Self {
self.vector_weight = weight.clamp(0.0, 1.0);
self
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HybridSearchResponse {
#[serde(alias = "matches")]
pub results: Vec<Match>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemDiagnostics {
pub system: SystemInfo,
pub resources: ResourceUsage,
pub components: ComponentHealth,
pub active_jobs: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemInfo {
pub version: String,
pub rust_version: String,
pub uptime_seconds: u64,
pub pid: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceUsage {
pub memory_bytes: u64,
pub thread_count: u64,
pub open_fds: u64,
pub cpu_percent: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentHealth {
pub storage: HealthStatus,
pub search_engine: HealthStatus,
pub cache: HealthStatus,
pub grpc: HealthStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthStatus {
pub healthy: bool,
pub message: String,
pub last_check: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobInfo {
pub id: String,
pub job_type: String,
pub status: String,
pub created_at: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub started_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completed_at: Option<u64>,
pub progress: u8,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default)]
pub metadata: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompactionRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
#[serde(default)]
pub force: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompactionResponse {
pub job_id: String,
pub message: String,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum WarmingPriority {
Critical,
High,
#[default]
Normal,
Low,
Background,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum WarmingTargetTier {
L1,
#[default]
L2,
Both,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AccessPatternHint {
#[default]
Random,
Sequential,
Temporal,
Spatial,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WarmCacheRequest {
pub namespace: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector_ids: Option<Vec<String>>,
#[serde(default)]
pub priority: WarmingPriority,
#[serde(default)]
pub target_tier: WarmingTargetTier,
#[serde(default)]
pub background: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl_hint_seconds: Option<u64>,
#[serde(default)]
pub access_pattern: AccessPatternHint,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_vectors: Option<usize>,
}
impl WarmCacheRequest {
pub fn new(namespace: impl Into<String>) -> Self {
Self {
namespace: namespace.into(),
vector_ids: None,
priority: WarmingPriority::default(),
target_tier: WarmingTargetTier::default(),
background: false,
ttl_hint_seconds: None,
access_pattern: AccessPatternHint::default(),
max_vectors: None,
}
}
pub fn with_vector_ids(mut self, ids: Vec<String>) -> Self {
self.vector_ids = Some(ids);
self
}
pub fn with_priority(mut self, priority: WarmingPriority) -> Self {
self.priority = priority;
self
}
pub fn with_target_tier(mut self, tier: WarmingTargetTier) -> Self {
self.target_tier = tier;
self
}
pub fn in_background(mut self) -> Self {
self.background = true;
self
}
pub fn with_ttl(mut self, seconds: u64) -> Self {
self.ttl_hint_seconds = Some(seconds);
self
}
pub fn with_access_pattern(mut self, pattern: AccessPatternHint) -> Self {
self.access_pattern = pattern;
self
}
pub fn with_max_vectors(mut self, max: usize) -> Self {
self.max_vectors = Some(max);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WarmCacheResponse {
pub success: bool,
pub entries_warmed: u64,
pub entries_skipped: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub job_id: Option<String>,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_completion: Option<String>,
pub target_tier: WarmingTargetTier,
pub priority: WarmingPriority,
#[serde(skip_serializing_if = "Option::is_none")]
pub bytes_warmed: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportRequest {
#[serde(default = "default_export_top_k")]
pub top_k: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
#[serde(default = "default_true")]
pub include_vectors: bool,
#[serde(default = "default_true")]
pub include_metadata: bool,
}
fn default_export_top_k() -> usize {
1000
}
impl Default for ExportRequest {
fn default() -> Self {
Self {
top_k: 1000,
cursor: None,
include_vectors: true,
include_metadata: true,
}
}
}
impl ExportRequest {
pub fn new() -> Self {
Self::default()
}
pub fn with_top_k(mut self, top_k: usize) -> Self {
self.top_k = top_k;
self
}
pub fn with_cursor(mut self, cursor: impl Into<String>) -> Self {
self.cursor = Some(cursor.into());
self
}
pub fn include_vectors(mut self, include: bool) -> Self {
self.include_vectors = include;
self
}
pub fn include_metadata(mut self, include: bool) -> Self {
self.include_metadata = include;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportedVector {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub values: Option<Vec<f32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportResponse {
pub vectors: Vec<ExportedVector>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
pub total_count: usize,
pub returned_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchQueryItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub vector: Vec<f32>,
#[serde(default = "default_batch_top_k")]
pub top_k: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
#[serde(default)]
pub include_metadata: bool,
#[serde(default)]
pub consistency: ReadConsistency,
#[serde(skip_serializing_if = "Option::is_none")]
pub staleness_config: Option<StalenessConfig>,
}
fn default_batch_top_k() -> u32 {
10
}
impl BatchQueryItem {
pub fn new(vector: Vec<f32>, top_k: u32) -> Self {
Self {
id: None,
vector,
top_k,
filter: None,
include_metadata: true,
consistency: ReadConsistency::default(),
staleness_config: None,
}
}
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn include_metadata(mut self, include: bool) -> Self {
self.include_metadata = include;
self
}
pub fn with_consistency(mut self, consistency: ReadConsistency) -> Self {
self.consistency = consistency;
self
}
pub fn with_bounded_staleness(mut self, max_staleness_ms: u64) -> Self {
self.consistency = ReadConsistency::BoundedStaleness;
self.staleness_config = Some(StalenessConfig::new(max_staleness_ms));
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchQueryRequest {
pub queries: Vec<BatchQueryItem>,
}
impl BatchQueryRequest {
pub fn new(queries: Vec<BatchQueryItem>) -> Self {
Self { queries }
}
pub fn single(query: BatchQueryItem) -> Self {
Self {
queries: vec![query],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchQueryResult {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub results: Vec<Match>,
pub latency_ms: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchQueryResponse {
pub results: Vec<BatchQueryResult>,
pub total_latency_ms: f64,
pub query_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MultiVectorSearchRequest {
pub positive_vectors: Vec<Vec<f32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub positive_weights: Option<Vec<f32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub negative_vectors: Option<Vec<Vec<f32>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub negative_weights: Option<Vec<f32>>,
#[serde(default = "default_multi_vector_top_k")]
pub top_k: u32,
#[serde(default)]
pub distance_metric: DistanceMetric,
#[serde(skip_serializing_if = "Option::is_none")]
pub score_threshold: Option<f32>,
#[serde(default)]
pub enable_mmr: bool,
#[serde(default = "default_mmr_lambda")]
pub mmr_lambda: f32,
#[serde(default = "default_true")]
pub include_metadata: bool,
#[serde(default)]
pub include_vectors: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
#[serde(default)]
pub consistency: ReadConsistency,
#[serde(skip_serializing_if = "Option::is_none")]
pub staleness_config: Option<StalenessConfig>,
}
fn default_multi_vector_top_k() -> u32 {
10
}
fn default_mmr_lambda() -> f32 {
0.5
}
impl MultiVectorSearchRequest {
pub fn new(positive_vectors: Vec<Vec<f32>>) -> Self {
Self {
positive_vectors,
positive_weights: None,
negative_vectors: None,
negative_weights: None,
top_k: 10,
distance_metric: DistanceMetric::default(),
score_threshold: None,
enable_mmr: false,
mmr_lambda: 0.5,
include_metadata: true,
include_vectors: false,
filter: None,
consistency: ReadConsistency::default(),
staleness_config: None,
}
}
pub fn with_top_k(mut self, top_k: u32) -> Self {
self.top_k = top_k;
self
}
pub fn with_positive_weights(mut self, weights: Vec<f32>) -> Self {
self.positive_weights = Some(weights);
self
}
pub fn with_negative_vectors(mut self, vectors: Vec<Vec<f32>>) -> Self {
self.negative_vectors = Some(vectors);
self
}
pub fn with_negative_weights(mut self, weights: Vec<f32>) -> Self {
self.negative_weights = Some(weights);
self
}
pub fn with_distance_metric(mut self, metric: DistanceMetric) -> Self {
self.distance_metric = metric;
self
}
pub fn with_score_threshold(mut self, threshold: f32) -> Self {
self.score_threshold = Some(threshold);
self
}
pub fn with_mmr(mut self, lambda: f32) -> Self {
self.enable_mmr = true;
self.mmr_lambda = lambda.clamp(0.0, 1.0);
self
}
pub fn include_metadata(mut self, include: bool) -> Self {
self.include_metadata = include;
self
}
pub fn include_vectors(mut self, include: bool) -> Self {
self.include_vectors = include;
self
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn with_consistency(mut self, consistency: ReadConsistency) -> Self {
self.consistency = consistency;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MultiVectorSearchResult {
pub id: String,
pub score: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub mmr_score: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub original_rank: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector: Option<Vec<f32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MultiVectorSearchResponse {
pub results: Vec<MultiVectorSearchResult>,
#[serde(skip_serializing_if = "Option::is_none")]
pub computed_query_vector: Option<Vec<f32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AggregateFunction {
Count,
Sum { field: String },
Avg { field: String },
Min { field: String },
Max { field: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregationRequest {
pub aggregate_by: HashMap<String, serde_json::Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub group_by: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
#[serde(default = "default_agg_limit")]
pub limit: usize,
}
fn default_agg_limit() -> usize {
100
}
impl AggregationRequest {
pub fn new() -> Self {
Self {
aggregate_by: HashMap::new(),
group_by: Vec::new(),
filter: None,
limit: 100,
}
}
pub fn with_count(mut self, name: impl Into<String>) -> Self {
self.aggregate_by
.insert(name.into(), serde_json::json!(["Count"]));
self
}
pub fn with_sum(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
self.aggregate_by
.insert(name.into(), serde_json::json!(["Sum", field.into()]));
self
}
pub fn with_avg(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
self.aggregate_by
.insert(name.into(), serde_json::json!(["Avg", field.into()]));
self
}
pub fn with_min(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
self.aggregate_by
.insert(name.into(), serde_json::json!(["Min", field.into()]));
self
}
pub fn with_max(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
self.aggregate_by
.insert(name.into(), serde_json::json!(["Max", field.into()]));
self
}
pub fn group_by(mut self, fields: Vec<String>) -> Self {
self.group_by = fields;
self
}
pub fn with_group_by(mut self, field: impl Into<String>) -> Self {
self.group_by.push(field.into());
self
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.limit = limit;
self
}
}
impl Default for AggregationRequest {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregationResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub aggregations: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aggregation_groups: Option<Vec<AggregationGroup>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregationGroup {
#[serde(flatten)]
pub group_key: HashMap<String, serde_json::Value>,
#[serde(flatten)]
pub aggregations: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
pub enum VectorSearchMethod {
#[default]
ANN,
#[serde(rename = "kNN")]
KNN,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum SortDirection {
Asc,
#[default]
Desc,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RankBy {
VectorSearch {
field: String,
method: VectorSearchMethod,
query_vector: Vec<f32>,
},
FullTextSearch {
field: String,
method: String, query: String,
},
AttributeOrder {
field: String,
direction: SortDirection,
},
Sum(Vec<RankBy>),
Max(Vec<RankBy>),
Product { weight: f32, ranking: Box<RankBy> },
}
impl RankBy {
pub fn vector_ann(field: impl Into<String>, query_vector: Vec<f32>) -> Self {
RankBy::VectorSearch {
field: field.into(),
method: VectorSearchMethod::ANN,
query_vector,
}
}
pub fn ann(query_vector: Vec<f32>) -> Self {
Self::vector_ann("vector", query_vector)
}
pub fn vector_knn(field: impl Into<String>, query_vector: Vec<f32>) -> Self {
RankBy::VectorSearch {
field: field.into(),
method: VectorSearchMethod::KNN,
query_vector,
}
}
pub fn knn(query_vector: Vec<f32>) -> Self {
Self::vector_knn("vector", query_vector)
}
pub fn bm25(field: impl Into<String>, query: impl Into<String>) -> Self {
RankBy::FullTextSearch {
field: field.into(),
method: "BM25".to_string(),
query: query.into(),
}
}
pub fn asc(field: impl Into<String>) -> Self {
RankBy::AttributeOrder {
field: field.into(),
direction: SortDirection::Asc,
}
}
pub fn desc(field: impl Into<String>) -> Self {
RankBy::AttributeOrder {
field: field.into(),
direction: SortDirection::Desc,
}
}
pub fn sum(rankings: Vec<RankBy>) -> Self {
RankBy::Sum(rankings)
}
pub fn max(rankings: Vec<RankBy>) -> Self {
RankBy::Max(rankings)
}
pub fn product(weight: f32, ranking: RankBy) -> Self {
RankBy::Product {
weight,
ranking: Box::new(ranking),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnifiedQueryRequest {
pub rank_by: serde_json::Value,
#[serde(default = "default_unified_top_k")]
pub top_k: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
#[serde(default = "default_true")]
pub include_metadata: bool,
#[serde(default)]
pub include_vectors: bool,
#[serde(default)]
pub distance_metric: DistanceMetric,
}
fn default_unified_top_k() -> usize {
10
}
impl UnifiedQueryRequest {
pub fn vector_search(query_vector: Vec<f32>, top_k: usize) -> Self {
Self {
rank_by: serde_json::json!(["ANN", query_vector]),
top_k,
filter: None,
include_metadata: true,
include_vectors: false,
distance_metric: DistanceMetric::default(),
}
}
pub fn vector_knn_search(query_vector: Vec<f32>, top_k: usize) -> Self {
Self {
rank_by: serde_json::json!(["kNN", query_vector]),
top_k,
filter: None,
include_metadata: true,
include_vectors: false,
distance_metric: DistanceMetric::default(),
}
}
pub fn fulltext_search(
field: impl Into<String>,
query: impl Into<String>,
top_k: usize,
) -> Self {
Self {
rank_by: serde_json::json!([field.into(), "BM25", query.into()]),
top_k,
filter: None,
include_metadata: true,
include_vectors: false,
distance_metric: DistanceMetric::default(),
}
}
pub fn attribute_order(
field: impl Into<String>,
direction: SortDirection,
top_k: usize,
) -> Self {
let dir = match direction {
SortDirection::Asc => "asc",
SortDirection::Desc => "desc",
};
Self {
rank_by: serde_json::json!([field.into(), dir]),
top_k,
filter: None,
include_metadata: true,
include_vectors: false,
distance_metric: DistanceMetric::default(),
}
}
pub fn with_rank_by(rank_by: serde_json::Value, top_k: usize) -> Self {
Self {
rank_by,
top_k,
filter: None,
include_metadata: true,
include_vectors: false,
distance_metric: DistanceMetric::default(),
}
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn include_metadata(mut self, include: bool) -> Self {
self.include_metadata = include;
self
}
pub fn include_vectors(mut self, include: bool) -> Self {
self.include_vectors = include;
self
}
pub fn with_distance_metric(mut self, metric: DistanceMetric) -> Self {
self.distance_metric = metric;
self
}
pub fn with_top_k(mut self, top_k: usize) -> Self {
self.top_k = top_k;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnifiedSearchResult {
pub id: String,
#[serde(rename = "$dist", skip_serializing_if = "Option::is_none")]
pub dist: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector: Option<Vec<f32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnifiedQueryResponse {
pub results: Vec<UnifiedSearchResult>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
fn default_explain_top_k() -> usize {
10
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum ExplainQueryType {
#[default]
VectorSearch,
FullTextSearch,
HybridSearch,
MultiVector,
BatchQuery,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryExplainRequest {
#[serde(default)]
pub query_type: ExplainQueryType,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector: Option<Vec<f32>>,
#[serde(default = "default_explain_top_k")]
pub top_k: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text_query: Option<String>,
#[serde(default = "default_distance_metric")]
pub distance_metric: String,
#[serde(default)]
pub execute: bool,
#[serde(default)]
pub verbose: bool,
}
fn default_distance_metric() -> String {
"cosine".to_string()
}
impl QueryExplainRequest {
pub fn vector_search(vector: Vec<f32>, top_k: usize) -> Self {
Self {
query_type: ExplainQueryType::VectorSearch,
vector: Some(vector),
top_k,
filter: None,
text_query: None,
distance_metric: "cosine".to_string(),
execute: false,
verbose: false,
}
}
pub fn fulltext_search(text_query: impl Into<String>, top_k: usize) -> Self {
Self {
query_type: ExplainQueryType::FullTextSearch,
vector: None,
top_k,
filter: None,
text_query: Some(text_query.into()),
distance_metric: "bm25".to_string(),
execute: false,
verbose: false,
}
}
pub fn hybrid_search(vector: Vec<f32>, text_query: impl Into<String>, top_k: usize) -> Self {
Self {
query_type: ExplainQueryType::HybridSearch,
vector: Some(vector),
top_k,
filter: None,
text_query: Some(text_query.into()),
distance_metric: "hybrid".to_string(),
execute: false,
verbose: false,
}
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn with_distance_metric(mut self, metric: impl Into<String>) -> Self {
self.distance_metric = metric.into();
self
}
pub fn with_execution(mut self) -> Self {
self.execute = true;
self
}
pub fn with_verbose(mut self) -> Self {
self.verbose = true;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionStage {
pub name: String,
pub description: String,
pub order: u32,
pub estimated_input: u64,
pub estimated_output: u64,
pub estimated_cost: f64,
#[serde(default)]
pub details: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostEstimate {
pub total_cost: f64,
pub estimated_time_ms: u64,
pub estimated_memory_bytes: u64,
pub estimated_io_ops: u64,
#[serde(default)]
pub cost_breakdown: HashMap<String, f64>,
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActualStats {
pub execution_time_ms: u64,
pub results_returned: usize,
pub vectors_scanned: u64,
pub vectors_after_filter: u64,
pub index_lookups: u64,
pub cache_hits: u64,
pub cache_misses: u64,
pub memory_used_bytes: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Recommendation {
pub recommendation_type: String,
pub priority: String,
pub description: String,
pub expected_improvement: String,
pub implementation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexSelection {
pub index_type: String,
pub selection_reason: String,
#[serde(default)]
pub alternatives_considered: Vec<IndexAlternative>,
#[serde(default)]
pub index_config: HashMap<String, serde_json::Value>,
pub index_stats: IndexStatistics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexAlternative {
pub index_type: String,
pub rejection_reason: String,
pub estimated_cost: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexStatistics {
pub vector_count: u64,
pub dimension: usize,
pub memory_bytes: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub build_time_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_updated: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryParams {
pub top_k: usize,
pub has_filter: bool,
pub filter_complexity: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector_dimension: Option<usize>,
pub distance_metric: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub text_query_length: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryExplainResponse {
pub query_type: ExplainQueryType,
pub namespace: String,
pub index_selection: IndexSelection,
pub stages: Vec<ExecutionStage>,
pub cost_estimate: CostEstimate,
#[serde(skip_serializing_if = "Option::is_none")]
pub actual_stats: Option<ActualStats>,
#[serde(default)]
pub recommendations: Vec<Recommendation>,
pub summary: String,
pub query_params: QueryParams,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum EmbeddingModel {
#[default]
BgeLarge,
Minilm,
BgeSmall,
E5Small,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextDocument {
pub id: String,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ttl_seconds: Option<u64>,
}
impl TextDocument {
pub fn new(id: impl Into<String>, text: impl Into<String>) -> Self {
Self {
id: id.into(),
text: text.into(),
metadata: None,
ttl_seconds: None,
}
}
pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
self.metadata = Some(metadata);
self
}
pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
self.ttl_seconds = Some(ttl_seconds);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpsertTextRequest {
pub documents: Vec<TextDocument>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<EmbeddingModel>,
}
impl UpsertTextRequest {
pub fn new(documents: Vec<TextDocument>) -> Self {
Self {
documents,
model: None,
}
}
pub fn with_model(mut self, model: EmbeddingModel) -> Self {
self.model = Some(model);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextUpsertResponse {
pub upserted_count: u64,
pub tokens_processed: u64,
pub model: EmbeddingModel,
pub embedding_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextSearchResult {
pub id: String,
pub score: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector: Option<Vec<f32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryTextRequest {
pub text: String,
pub top_k: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
pub include_text: bool,
pub include_vectors: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<EmbeddingModel>,
}
impl QueryTextRequest {
pub fn new(text: impl Into<String>, top_k: u32) -> Self {
Self {
text: text.into(),
top_k,
filter: None,
include_text: true,
include_vectors: false,
model: None,
}
}
pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
self.filter = Some(filter);
self
}
pub fn include_text(mut self, include: bool) -> Self {
self.include_text = include;
self
}
pub fn include_vectors(mut self, include: bool) -> Self {
self.include_vectors = include;
self
}
pub fn with_model(mut self, model: EmbeddingModel) -> Self {
self.model = Some(model);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextQueryResponse {
pub results: Vec<TextSearchResult>,
pub model: EmbeddingModel,
pub embedding_time_ms: u64,
pub search_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchQueryTextRequest {
pub queries: Vec<String>,
pub top_k: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
pub include_vectors: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<EmbeddingModel>,
}
impl BatchQueryTextRequest {
pub fn new(queries: Vec<String>, top_k: u32) -> Self {
Self {
queries,
top_k,
filter: None,
include_vectors: false,
model: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchQueryTextResponse {
pub results: Vec<Vec<TextSearchResult>>,
pub model: EmbeddingModel,
pub embedding_time_ms: u64,
pub search_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FetchRequest {
pub ids: Vec<String>,
pub include_values: bool,
pub include_metadata: bool,
}
impl FetchRequest {
pub fn new(ids: Vec<String>) -> Self {
Self {
ids,
include_values: true,
include_metadata: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FetchResponse {
pub vectors: Vec<Vector>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CreateNamespaceRequest {
#[serde(rename = "dimension", skip_serializing_if = "Option::is_none")]
pub dimensions: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
impl CreateNamespaceRequest {
pub fn new() -> Self {
Self::default()
}
pub fn with_dimensions(mut self, dimensions: u32) -> Self {
self.dimensions = Some(dimensions);
self
}
pub fn with_index_type(mut self, index_type: impl Into<String>) -> Self {
self.index_type = Some(index_type.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigureNamespaceRequest {
pub dimension: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub distance: Option<DistanceMetric>,
}
impl ConfigureNamespaceRequest {
pub fn new(dimension: usize) -> Self {
Self {
dimension,
distance: None,
}
}
pub fn with_distance(mut self, distance: DistanceMetric) -> Self {
self.distance = Some(distance);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigureNamespaceResponse {
pub namespace: String,
pub dimension: usize,
pub distance: DistanceMetric,
pub created: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EdgeType {
RelatedTo,
SharesEntity,
Precedes,
#[default]
LinkedBy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphEdge {
pub id: String,
pub source_id: String,
pub target_id: String,
pub edge_type: EdgeType,
pub weight: f64,
pub created_at: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNode {
pub memory_id: String,
pub content_preview: String,
pub importance: f64,
pub depth: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryGraph {
pub root_id: String,
pub depth: u32,
pub nodes: Vec<GraphNode>,
pub edges: Vec<GraphEdge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphPath {
pub source_id: String,
pub target_id: String,
pub path: Vec<String>,
pub hops: i32,
pub edges: Vec<GraphEdge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphLinkRequest {
pub target_id: String,
pub edge_type: EdgeType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphLinkResponse {
pub edge: GraphEdge,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphExport {
pub agent_id: String,
pub format: String,
pub data: String,
pub node_count: u64,
pub edge_count: u64,
}
#[derive(Debug, Clone, Default)]
pub struct GraphOptions {
pub depth: Option<u32>,
pub types: Option<Vec<EdgeType>>,
}
impl GraphOptions {
pub fn new() -> Self {
Self::default()
}
pub fn depth(mut self, depth: u32) -> Self {
self.depth = Some(depth);
self
}
pub fn types(mut self, types: Vec<EdgeType>) -> Self {
self.types = Some(types);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct NamespaceNerConfig {
pub extract_entities: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub entity_types: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedEntity {
pub entity_type: String,
pub value: String,
pub score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityExtractionResponse {
pub entities: Vec<ExtractedEntity>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryEntitiesResponse {
pub memory_id: String,
pub entities: Vec<ExtractedEntity>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FeedbackSignal {
Upvote,
Downvote,
Flag,
Positive,
Negative,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeedbackHistoryEntry {
pub signal: FeedbackSignal,
pub timestamp: u64,
pub old_importance: f32,
pub new_importance: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryFeedbackBody {
pub agent_id: String,
pub signal: FeedbackSignal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryImportancePatch {
pub agent_id: String,
pub importance: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeedbackResponse {
pub memory_id: String,
pub new_importance: f32,
pub signal: FeedbackSignal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeedbackHistoryResponse {
pub memory_id: String,
pub entries: Vec<FeedbackHistoryEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentFeedbackSummary {
pub agent_id: String,
pub upvotes: u64,
pub downvotes: u64,
pub flags: u64,
pub total_feedback: u64,
pub health_score: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeedbackHealthResponse {
pub agent_id: String,
pub health_score: f32,
pub memory_count: usize,
pub avg_importance: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OdeEntity {
pub text: String,
pub label: String,
pub start: usize,
pub end: usize,
pub score: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractEntitiesRequest {
pub content: String,
pub agent_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entity_types: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractEntitiesResponse {
pub entities: Vec<OdeEntity>,
pub model: String,
pub processing_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KgQueryResponse {
pub agent_id: String,
pub node_count: usize,
pub edge_count: usize,
pub edges: Vec<GraphEdge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KgPathResponse {
pub agent_id: String,
pub from_id: String,
pub to_id: String,
pub hop_count: usize,
pub path: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KgExportResponse {
pub agent_id: String,
pub format: String,
pub node_count: usize,
pub edge_count: usize,
pub edges: Vec<GraphEdge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryPolicy {
#[serde(skip_serializing_if = "Option::is_none")]
pub working_ttl_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub episodic_ttl_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_ttl_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub procedural_ttl_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub working_decay: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub episodic_decay: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_decay: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub procedural_decay: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub spaced_repetition_factor: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub spaced_repetition_base_interval_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consolidation_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consolidation_threshold: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consolidation_interval_hours: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub consolidated_count: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_stores_per_minute: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_recalls_per_minute: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dedup_on_store: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dedup_threshold: Option<f32>,
}
impl Default for MemoryPolicy {
fn default() -> Self {
Self {
working_ttl_seconds: Some(14_400),
episodic_ttl_seconds: Some(2_592_000),
semantic_ttl_seconds: Some(31_536_000),
procedural_ttl_seconds: Some(63_072_000),
working_decay: Some("exponential".to_string()),
episodic_decay: Some("power_law".to_string()),
semantic_decay: Some("logarithmic".to_string()),
procedural_decay: Some("flat".to_string()),
spaced_repetition_factor: Some(1.0),
spaced_repetition_base_interval_seconds: Some(86_400),
consolidation_enabled: Some(false),
consolidation_threshold: Some(0.92),
consolidation_interval_hours: Some(24),
consolidated_count: Some(0),
rate_limit_enabled: Some(false),
rate_limit_stores_per_minute: None,
rate_limit_recalls_per_minute: None,
dedup_on_store: Some(false),
dedup_threshold: Some(0.92),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkUpdateRequest {
pub filter: serde_json::Value,
pub update: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkUpdateResponse {
pub updated: u64,
pub failed: u64,
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkDeleteRequest {
pub filter: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkDeleteResponse {
pub deleted: u64,
pub failed: u64,
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CountVectorsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CountVectorsResponse {
pub count: u64,
pub namespace: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConsolidateResponse {
pub agent_id: String,
pub memories_scanned: u64,
pub clusters_found: u64,
pub memories_deprecated: u64,
pub anchor_ids: Vec<String>,
pub deprecated_ids: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skipped: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConsolidationLogEntry {
pub timestamp: u64,
pub clusters_found: u64,
pub memories_deprecated: u64,
pub anchor_ids: Vec<String>,
pub deprecated_ids: Vec<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConsolidationConfigPatch {
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub epsilon: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_samples: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub soft_deprecation_days: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConsolidationConfig {
pub enabled: bool,
pub epsilon: f64,
pub min_samples: u32,
pub soft_deprecation_days: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceEntityConfig {
pub namespace: String,
pub extract_entities: bool,
pub entity_types: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceExtractorConfig {
pub provider: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeReplicationLag {
pub node_id: String,
pub lag_ms: u64,
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReplicationStatus {
pub replication_factor: u32,
pub healthy_replicas: u32,
pub total_nodes: u32,
#[serde(default)]
pub replication_lag: Vec<NodeReplicationLag>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShardInfo {
pub shard_id: String,
pub namespace: String,
pub primary_node: String,
#[serde(default)]
pub replica_nodes: Vec<String>,
pub state: String,
pub vector_count: u64,
pub size_bytes: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShardListResponse {
pub shards: Vec<ShardInfo>,
pub total: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ShardRebalanceRequest {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub shard_ids: Vec<String>,
#[serde(default)]
pub dry_run: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShardMove {
pub shard_id: String,
pub from_node: String,
pub to_node: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShardRebalanceResponse {
pub initiated: bool,
pub operation_id: String,
pub shards_affected: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_seconds: Option<u64>,
#[serde(default)]
pub planned_moves: Vec<ShardMove>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaintenanceStatus {
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scheduled_end: Option<u64>,
#[serde(default)]
pub nodes_in_maintenance: Vec<String>,
pub rejecting_requests: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnableMaintenanceRequest {
pub reason: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub node_ids: Vec<String>,
#[serde(default)]
pub reject_requests: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_minutes: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DisableMaintenanceRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub force: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QuotaConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_vectors: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_storage_bytes: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_dimensions: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_metadata_bytes: Option<usize>,
#[serde(default)]
pub enforcement: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QuotaUsage {
pub vector_count: u64,
pub storage_bytes: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub avg_dimensions: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub avg_metadata_bytes: Option<usize>,
pub last_updated: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuotaStatus {
pub namespace: String,
pub config: QuotaConfig,
pub usage: QuotaUsage,
#[serde(skip_serializing_if = "Option::is_none")]
pub vector_usage_percent: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub storage_usage_percent: Option<f32>,
pub is_exceeded: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub exceeded_quotas: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuotaListResponse {
pub quotas: Vec<QuotaStatus>,
pub total: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_config: Option<QuotaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DefaultQuotaResponse {
pub config: Option<QuotaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SetDefaultQuotaRequest {
pub config: Option<QuotaConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetQuotaRequest {
pub config: QuotaConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetQuotaResponse {
pub success: bool,
pub namespace: String,
pub config: QuotaConfig,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuotaCheckRequest {
pub vector_ids: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dimensions: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata_bytes: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuotaCheckResult {
pub allowed: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
pub usage: QuotaUsage,
#[serde(skip_serializing_if = "Option::is_none")]
pub exceeded_quota: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdminBackupInfo {
pub backup_id: String,
pub name: String,
pub backup_type: String,
pub status: String,
#[serde(default)]
pub namespaces: Vec<String>,
pub vector_count: u64,
pub size_bytes: u64,
pub created_at: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub completed_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub storage_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub encrypted: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub compression: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupListResponse {
pub backups: Vec<AdminBackupInfo>,
pub total: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateBackupRequest {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub backup_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub namespaces: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypt: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub compression: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateBackupResponse {
pub backup: AdminBackupInfo,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_completion: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RestoreBackupRequest {
pub backup_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_namespaces: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overwrite: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub point_in_time: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RestoreBackupResponse {
pub restore_id: String,
pub status: String,
pub backup_id: String,
#[serde(default)]
pub namespaces: Vec<String>,
pub started_at: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_completion: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub progress_percent: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vectors_restored: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completed_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupSchedule {
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub cron: Option<String>,
pub backup_type: String,
pub retention_days: u32,
pub max_backups: u32,
#[serde(default)]
pub namespaces: Vec<String>,
pub encrypt: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub compression: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_backup_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_backup_at: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UpdateBackupScheduleRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cron: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub backup_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retention_days: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_backups: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub namespaces: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypt: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub compression: Option<String>,
}
fn default_route_top_k() -> usize {
3
}
fn default_route_min_similarity() -> f32 {
0.3
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteRequest {
pub query: String,
#[serde(default = "default_route_top_k")]
pub top_k: usize,
#[serde(default = "default_route_min_similarity")]
pub min_similarity: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteMatch {
pub namespace: String,
pub similarity: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteResponse {
pub routes: Vec<RouteMatch>,
pub model: String,
pub embedding_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportJobStatus {
pub job_id: String,
pub status: String,
pub format: String,
pub total: usize,
pub imported: usize,
pub skipped: usize,
#[serde(default)]
pub errors: Vec<String>,
pub started_at: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub finished_at: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TierInfo {
pub name: String,
pub tier_type: String,
pub technology: String,
pub description: String,
pub target_latency: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub capacity: Option<String>,
pub status: String,
pub current_count: u64,
pub hit_count: u64,
pub hit_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TierConfig {
pub hot_tier_capacity: usize,
pub hot_to_warm_threshold_secs: u64,
pub warm_to_cold_threshold_secs: u64,
pub auto_tier_enabled: bool,
pub tier_check_interval_secs: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TierActivity {
pub promotions: u64,
pub demotions: u64,
pub cache_hit_rate: f64,
pub storage_backend: String,
pub promotions_to_hot: u64,
pub demotions_to_warm: u64,
pub demotions_to_cold: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageTierOverview {
pub tiers_enabled: bool,
pub architecture: Vec<TierInfo>,
pub config: TierConfig,
pub activity: TierActivity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryTypeStatsResponse {
pub total: u64,
pub working: u64,
pub episodic: u64,
pub semantic: u64,
pub procedural: u64,
pub agent_namespaces: u64,
}
fn default_target_dimension() -> usize {
1024
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrateNamespaceDimensionsRequest {
#[serde(default)]
pub namespaces: Vec<String>,
#[serde(default = "default_target_dimension")]
pub target_dimension: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NamespaceMigrationResult {
pub namespace: String,
pub original_dimension: usize,
pub vectors_migrated: usize,
pub vectors_skipped: usize,
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrateDimensionsResponse {
pub migrated: usize,
pub failed: usize,
pub already_current: usize,
pub results: Vec<NamespaceMigrationResult>,
}