Skip to main content

wenlan_types/
entities.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Knowledge graph types -- entities, observations, relations.
3
4use serde::{Deserialize, Serialize};
5
6/// A knowledge graph entity.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Entity {
9    pub id: String,
10    pub name: String,
11    pub entity_type: String,
12    #[serde(default, alias = "domain")]
13    pub space: Option<String>,
14    pub source_agent: Option<String>,
15    pub confidence: Option<f32>,
16    pub confirmed: bool,
17    pub created_at: i64,
18    pub updated_at: i64,
19    /// Former names this entity has absorbed via a merge or an explicit
20    /// alias declaration (lowercase). Empty for an entity with no aliases.
21    #[serde(default)]
22    pub aliases: Vec<String>,
23}
24
25/// An entity search result with distance score.
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct EntitySearchResult {
28    pub entity: Entity,
29    pub distance: f32,
30}
31
32/// Full entity detail including observations and relations.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct EntityDetail {
35    pub entity: Entity,
36    pub observations: Vec<Observation>,
37    pub relations: Vec<RelationWithEntity>,
38}
39
40/// An observation attached to an entity.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Observation {
43    pub id: String,
44    pub entity_id: String,
45    pub content: String,
46    pub source_agent: Option<String>,
47    pub confidence: Option<f32>,
48    pub confirmed: bool,
49    pub created_at: i64,
50}
51
52/// A relation between two entities.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct Relation {
55    pub id: String,
56    pub from_entity: String,
57    pub to_entity: String,
58    pub relation_type: String,
59    pub source_agent: Option<String>,
60    pub created_at: i64,
61}
62
63/// A relation with resolved entity info (for detail views).
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct RelationWithEntity {
66    pub id: String,
67    pub relation_type: String,
68    pub direction: String,
69    pub entity_id: String,
70    pub entity_name: String,
71    pub entity_type: String,
72    pub source_agent: Option<String>,
73    pub created_at: i64,
74}
75
76/// One bulk read of the whole knowledge graph for a read scope: every entity
77/// the scope can see, every live relation whose BOTH endpoints are in that
78/// entity set, and the memories linked to at least one of those entities.
79///
80/// Exists so the desktop Graph view can draw the complete graph from ONE
81/// request instead of fanning out per-entity detail fetches (which capped the
82/// drawn graph at the first 20 entities and rendered every other connected
83/// entity as an isolate).
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct KnowledgeGraphResponse {
86    /// Same rows `/api/memory/entities/list` returns for this scope.
87    pub entities: Vec<Entity>,
88    /// Every live entity<->entity relation with both endpoints in `entities`.
89    pub relations: Vec<GraphRelation>,
90    /// Memories linked to at least one entity in `entities`, plus the ones
91    /// cited by a page in `pages`.
92    pub memories: Vec<GraphMemoryNode>,
93    /// memory_id <-> entity_id; both endpoints are present above.
94    pub memory_links: Vec<GraphMemoryLink>,
95    /// Wiki pages: everything that is not an entity shadow page.
96    pub pages: Vec<GraphPageNode>,
97    /// Typed edges with a page on at least one end. Every endpoint id is
98    /// present in the collection its `kind` names.
99    pub page_links: Vec<GraphPageLink>,
100}
101
102/// A relation edge as the bulk graph read returns it: both endpoints by id,
103/// no resolved neighbour names (the caller already holds every entity row).
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct GraphRelation {
106    pub id: String,
107    pub from_entity: String,
108    pub to_entity: String,
109    pub relation_type: String,
110    pub source_agent: Option<String>,
111    pub created_at: i64,
112}
113
114/// A memory drawn as a graph node.
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct GraphMemoryNode {
117    pub source_id: String,
118    pub title: String,
119    pub memory_type: Option<String>,
120    pub space: Option<String>,
121    pub confirmed: bool,
122    pub last_modified: i64,
123}
124
125/// A memory-to-entity link, drawn as an edge.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct GraphMemoryLink {
128    pub memory_id: String,
129    pub entity_id: String,
130}
131
132/// A wiki page drawn as a graph node.
133///
134/// "Wiki page" is every page that is not an entity's `kind='entity'` dual-write
135/// shadow: the distilled/authored/research/source pages a human would call a
136/// page. Entity shadows stay out because the entity itself is already a node.
137///
138/// No `community_id`: neither store can answer it for a wiki page.
139/// `pages.community_id` is written only onto `kind='entity'` shadows by
140/// `detect_communities`, and `page_community_assignments` is a fenced routing
141/// table whose only trusted reader (`list_community_page_assignments`) gates
142/// every row behind five freshness joins and a `state` check. Reading either
143/// one raw would report a stale, held or dropped assignment as a fact, so the
144/// map derives a page's region from what it links to instead.
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct GraphPageNode {
147    pub id: String,
148    pub title: String,
149    /// The page's space NAME, matching the vocabulary `Entity.space` and
150    /// `GraphMemoryNode.space` speak (scoping itself keys on `workspace`).
151    pub space: Option<String>,
152    pub creation_kind: String,
153    /// Set when the page is *about* an entity, which is a different thing from
154    /// being that entity's shadow page.
155    pub entity_id: Option<String>,
156    /// RFC 3339, as `pages.last_modified` stores it.
157    pub last_modified: String,
158}
159
160/// One endpoint of a [`GraphPageLink`]: which collection to look the id up in.
161///
162/// `kind` is `"page"`, `"entity"` or `"memory"`.
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct GraphRef {
165    pub kind: String,
166    pub id: String,
167}
168
169/// A typed edge with a page on at least one end.
170///
171/// `link_type` is `"wikilink"` (a resolved `[[link]]`, page->page or
172/// page->entity), `"about"` (`pages.entity_id`), or `"cites"` (a page->memory
173/// citation edge).
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct GraphPageLink {
176    pub from: GraphRef,
177    pub to: GraphRef,
178    pub link_type: String,
179}