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}