#![allow(missing_docs)]
use serde::{Deserialize, Serialize};
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_false(b: &bool) -> bool {
!*b
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct EncodeInput {
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub session: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub occurred_at: Option<u64>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct EncodeResult {
pub memory_id: String,
pub was_deduplicated: bool,
pub salience: f32,
pub kind: u8,
pub created_at_unix_nanos: u64,
pub auto_edges_added: u32,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct RecallInput {
pub query: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_results: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_trace: Option<bool>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct MemoryHit {
pub memory_id: String,
pub text: String,
pub similarity_score: f32,
pub confidence: f32,
pub salience: f32,
pub kind: u8,
pub created_at_unix_nanos: u64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RecallResult {
pub answer_kind: String,
pub memories: Vec<MemoryHit>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trace: Option<String>,
}
#[derive(Clone, Debug, Serialize)]
pub struct ForgetInput {
pub memory_id: String,
#[serde(skip_serializing_if = "is_false")]
pub hard: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ForgetResult {
pub memory_id: String,
pub was_already_forgotten: bool,
pub edges_removed: u32,
}
#[derive(Clone, Debug, Serialize)]
pub struct LinkInput {
pub source: String,
pub target: String,
pub kind: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub weight: Option<f32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct LinkResult {
pub source: String,
pub target: String,
pub kind: String,
pub weight: f32,
pub created_at_unix_nanos: u64,
pub already_existed: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct UnlinkInput {
pub source: String,
pub target: String,
pub kind: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct UnlinkResult {
pub source: String,
pub target: String,
pub kind: String,
pub removed: bool,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct Endpoint {
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_id: Option<String>,
}
impl Endpoint {
#[must_use]
pub fn text(text: impl Into<String>) -> Self {
Self {
text: Some(text.into()),
memory_id: None,
}
}
#[must_use]
pub fn memory(memory_id: impl Into<String>) -> Self {
Self {
text: None,
memory_id: Some(memory_id.into()),
}
}
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct PlanInput {
pub start: Endpoint,
pub goal: Endpoint,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_steps: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_wall_time_ms: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_branches: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strategy: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PlanStep {
pub step_index: u32,
pub memory_id: String,
pub text: String,
pub transition_kind: String,
pub confidence: f32,
pub estimated_distance_to_goal: f32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct PlanResult {
pub steps: Vec<PlanStep>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct ReasonInput {
pub observation: Endpoint,
#[serde(skip_serializing_if = "Option::is_none")]
pub depth: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confidence_threshold: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_inferences: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub budget_wall_time_ms: Option<u32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct InferenceStep {
pub step_index: u32,
pub claim: String,
pub supporting_memories: Vec<String>,
pub contradicting_memories: Vec<String>,
pub confidence: f32,
pub inference_kind: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ReasonResult {
pub inferences: Vec<InferenceStep>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Permissions {
pub can_encode: bool,
pub can_recall: bool,
pub can_plan: bool,
pub can_reason: bool,
pub can_forget: bool,
pub can_admin: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Whoami {
pub namespace: String,
pub space_id: String,
pub permissions: Permissions,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Capabilities {
pub rerank: bool,
pub llm_extractor: bool,
pub classifier_extractor: bool,
pub pattern_extractor: bool,
pub schema_namespaces: Vec<String>,
pub vector_dim: u16,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct MemoryListQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_tombstoned: Option<bool>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct MemoryListItem {
pub memory_id: String,
pub text: String,
pub kind: u8,
pub state: u8,
pub created_at_unix_nanos: u64,
pub occurred_at_unix_nanos: u64,
pub last_accessed_at_unix_nanos: u64,
pub salience: f32,
pub access_count: u32,
pub statement_count: u32,
pub entity_count: u32,
pub relation_count: u32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct MemoryListPage {
pub items: Vec<MemoryListItem>,
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StageRecord {
pub memory_id: String,
pub kind: u8,
pub salience: f32,
pub created_at_unix_nanos: u64,
pub occurred_at_unix_nanos: u64,
pub vector_dim: u32,
pub text_len: u32,
pub lsn: u64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StageKeywordField {
pub field: String,
pub terms: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StageGraphNode {
pub id: String,
pub name: String,
pub kind: String,
pub type_qname: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StageGraphEdge {
pub source: String,
pub target: String,
pub predicate: String,
pub kind: String,
pub confidence: f32,
pub event_at_unix_nanos: Option<u64>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StageGraph {
pub nodes: Vec<StageGraphNode>,
pub edges: Vec<StageGraphEdge>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct StageArtifact {
pub vector: Vec<f32>,
pub record: Option<StageRecord>,
pub hype_questions: Vec<String>,
pub keyword_fields: Vec<StageKeywordField>,
pub graph: Option<StageGraph>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct MemoryInspect {
pub found: bool,
pub memory_id: String,
pub text: String,
pub artifact: StageArtifact,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct ListEntitiesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub type_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mention_count_min: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_tombstoned: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_merged: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct EntityDetail {
pub entity_id: String,
pub entity_type_id: u32,
pub canonical_name: String,
pub aliases: Vec<String>,
pub mention_count: u32,
pub created_at_unix_nanos: u64,
pub updated_at_unix_nanos: u64,
pub merged_into: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ListEntitiesResult {
pub entities: Vec<EntityDetail>,
pub count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct CreateEntityInput {
pub entity_type_id: u32,
pub canonical_name: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct CreateEntityResult {
pub entity_id: String,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct ResolveEntityInput {
pub candidate_name: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub resolution_context: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub type_hint: Option<u32>,
#[serde(skip_serializing_if = "is_false")]
pub allow_create: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ResolveEntityResult {
pub outcome: String,
pub tier: u8,
pub confidence: f32,
pub entity_id: Option<String>,
pub candidate_ids: Vec<String>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct TraverseInput {
#[serde(skip_serializing_if = "String::is_empty")]
pub direction: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub relation_types: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_depth: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_nodes: Option<u32>,
#[serde(skip_serializing_if = "is_false")]
pub include_superseded: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TraversalStep {
pub relation_id: String,
pub from: String,
pub to: String,
pub relation_type: String,
pub depth: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub to_name: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TraversalPath {
pub steps: Vec<TraversalStep>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct TraverseResult {
pub paths: Vec<TraversalPath>,
pub total_paths: u32,
pub truncated: bool,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct ListRelationsQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub relation_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_superseded: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_tombstoned: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct RelationDetail {
pub relation_id: String,
pub relation_type: String,
pub from_entity: String,
pub to_entity: String,
pub confidence: f32,
pub valid_from_unix_nanos: u64,
pub valid_to_unix_nanos: u64,
pub is_symmetric: bool,
pub tombstoned: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub from_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub to_name: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ListRelationsResult {
pub relations: Vec<RelationDetail>,
pub count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct GetRelationQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub follow_supersession: Option<bool>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct ListStatementsQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub predicate: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_confidence: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub only_current: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_tombstoned: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
fn de_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: serde::Deserializer<'de>,
{
let number = serde_json::Number::deserialize(deserializer)?;
number
.as_f64()
.ok_or_else(|| serde::de::Error::custom(format!("{number} is not an f64")))
}
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum StatementValue {
Text {
value: String,
},
Integer {
value: i64,
},
Float {
#[serde(deserialize_with = "de_f64")]
value: f64,
},
Bool {
value: bool,
},
UnixNanos {
value: u64,
},
Blob {
value: Vec<u8>,
},
}
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum StatementObject {
Entity { id: String },
Value { value: StatementValue },
Memory { id: String },
Statement { id: String },
}
#[derive(Clone, Debug, Deserialize)]
pub struct StatementDetail {
pub statement_id: String,
pub kind: String,
pub subject: String,
pub predicate: String,
pub object: StatementObject,
pub confidence: f32,
pub event_at_unix_nanos: u64,
pub valid_from_unix_nanos: u64,
pub valid_to_unix_nanos: u64,
pub tombstoned: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subject_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub object_name: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct ListStatementsResult {
pub statements: Vec<StatementDetail>,
pub count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct GetStatementQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub follow_supersession: Option<bool>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct GraphFetchQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_statements: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_memories: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_memory_edges: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_tombstoned: Option<bool>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct GraphNode {
pub id: String,
pub kind: String,
pub label: String,
pub type_qname: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct GraphEdge {
pub from_id: String,
pub to_id: String,
pub kind: String,
pub label: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct GraphPage {
pub nodes: Vec<GraphNode>,
pub edges: Vec<GraphEdge>,
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct SchemaGetQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<u32>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Schema {
pub namespace: String,
pub schema_version: u32,
pub schema_document: String,
pub uploaded_at_unix_nanos: u64,
pub validator_version: u32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SchemaError {
pub code: String,
pub message: String,
pub line: u32,
pub column: u32,
pub length: u32,
pub severity: u8,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct SchemaUploadInput {
pub schema_document: String,
#[serde(skip_serializing_if = "is_false")]
pub dry_run: bool,
#[serde(skip_serializing_if = "is_false")]
pub allow_breaking: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SchemaUploadResult {
pub namespace: String,
pub schema_version: u32,
pub backward_compatible: bool,
pub validation_errors: Vec<SchemaError>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct SchemaValidateInput {
pub schema_document: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SchemaValidateResult {
pub namespace: String,
pub would_be_version: u32,
pub validation_errors: Vec<SchemaError>,
}
#[derive(Clone, Debug, Serialize)]
pub struct SchemaReplaceInput {
pub schema_document: String,
pub force_drop_existing: bool,
}
#[derive(Clone, Debug, Deserialize)]
pub struct SchemaReplaceResult {
pub namespace: String,
pub schema_version: u32,
pub dropped_count: u32,
pub validation_errors: Vec<SchemaError>,
}
#[cfg(test)]
mod recall_trace_tests {
use super::{RecallInput, RecallResult};
#[test]
fn include_trace_is_omitted_unless_set() {
let body = serde_json::to_string(&RecallInput {
query: "x".into(),
max_results: Some(5),
..Default::default()
})
.expect("serialize");
assert!(!body.contains("include_trace"), "{body}");
}
#[test]
fn include_trace_is_sent_when_set() {
let body = serde_json::to_string(&RecallInput {
query: "x".into(),
include_trace: Some(true),
..Default::default()
})
.expect("serialize");
assert!(body.contains(r#""include_trace":true"#), "{body}");
}
#[test]
fn trace_round_trips_as_an_opaque_string() {
let r: RecallResult = serde_json::from_str(
r#"{"answer_kind":"single","memories":[],"trace":"{\"stages\":[]}"}"#,
)
.expect("deserialize");
assert_eq!(r.trace.as_deref(), Some(r#"{"stages":[]}"#));
}
#[test]
fn a_missing_trace_is_none_not_an_error() {
let r: RecallResult =
serde_json::from_str(r#"{"answer_kind":"none","memories":[]}"#).expect("deserialize");
assert!(r.trace.is_none());
}
}