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,
pub vector_count: u64,
#[serde(alias = "dimension")]
pub dimensions: Option<u32>,
pub index_type: Option<String>,
}
#[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 {
pub matches: 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 {
pub matches: 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 {
pub matches: 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,
pub started_at: Option<u64>,
pub completed_at: Option<u64>,
pub progress: u8,
pub message: Option<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]
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(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>,
}