knowdit_kg_model/extracted_semantic.rs
1//! Domain value types for a project-specific semantic and the functions
2//! attached to it. Shared between the LLM extraction pipeline (which produces
3//! these via tool calls) and the persistence layers in `knowdit-kg` and
4//! `knowdit-repo-model`. Derives `JsonSchema` so the type can serve directly
5//! as a tool's `ARGUMENTS` shape — no duplicate "Args" struct needed.
6use schemars::JsonSchema;
7use sea_orm::ActiveValue::Set;
8use serde::{Deserialize, Serialize};
9
10use crate::category::DeFiCategory;
11use crate::db::{semantic_function, semantic_node};
12
13/// One semantic interaction extracted from a project's source. Field shapes
14/// match the LLM tool-call contract; persistence-only metadata (row ids,
15/// etc.) is not modelled here — the database assigns those at insert time.
16#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema)]
17pub struct ExtractedSemantic {
18 /// Short canonical name (project-agnostic), e.g. "Constant Product AMM Swap".
19 pub name: String,
20 /// Which DeFi category this semantic belongs to.
21 pub category: DeFiCategory,
22 /// One-sentence formal definition.
23 pub definition: String,
24 /// Abstract description of user interaction, value flow, and outcome.
25 pub description: String,
26 /// Functions in the source that implement this semantic. At least one
27 /// entry is required (validated post-deserialization by the tool).
28 pub functions: Vec<ExtractedFunction>,
29}
30
31/// Function reference attached to an [`ExtractedSemantic`].
32#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema)]
33pub struct ExtractedFunction {
34 /// Function or entry-point name as it appears in the source.
35 pub name: String,
36 /// Containing file or module path (e.g. `src/pool/Swap.sol`).
37 pub contract: String,
38 /// Optional Solidity-style signature.
39 pub signature: Option<String>,
40}
41
42impl ExtractedSemantic {
43 /// Build an `ActiveModel` ready to insert into `semantic_node`. Functions
44 /// are stored separately via [`Self::function_active_models`], and the
45 /// project-provenance link MUST be written into `project_semantic` by the
46 /// caller — there is no longer a scalar `project_id` column on
47 /// `semantic_node`.
48 pub fn to_active_model(&self) -> semantic_node::ActiveModel {
49 semantic_node::ActiveModel {
50 name: Set(self.name.clone()),
51 category: Set(self.category),
52 definition: Set(self.definition.clone()),
53 description: Set(self.description.clone()),
54 ..Default::default()
55 }
56 }
57
58 /// Build the `semantic_function` rows that link a parent semantic node to
59 /// each of its `functions`. Caller supplies the parent id obtained after
60 /// inserting the semantic node.
61 pub fn function_active_models(
62 &self,
63 semantic_node_id: i32,
64 ) -> Vec<semantic_function::ActiveModel> {
65 self.functions
66 .iter()
67 .map(|f| f.to_active_model(semantic_node_id))
68 .collect()
69 }
70
71 /// Reconstruct from a stored `semantic_node` row plus its `semantic_function`
72 /// children. Function `signature` is left `None` because that column is not
73 /// persisted.
74 pub fn from_model(
75 node: semantic_node::Model,
76 functions: Vec<semantic_function::Model>,
77 ) -> Self {
78 Self {
79 name: node.name,
80 category: node.category,
81 definition: node.definition,
82 description: node.description,
83 functions: functions
84 .into_iter()
85 .map(ExtractedFunction::from_model)
86 .collect(),
87 }
88 }
89}
90
91impl ExtractedFunction {
92 pub fn to_active_model(&self, semantic_node_id: i32) -> semantic_function::ActiveModel {
93 semantic_function::ActiveModel {
94 semantic_node_id: Set(semantic_node_id),
95 function_name: Set(self.name.clone()),
96 contract_path: Set(self.contract.clone()),
97 ..Default::default()
98 }
99 }
100
101 /// Reconstruct from a stored `semantic_function` row. `signature` is left
102 /// `None` because the column is not persisted.
103 pub fn from_model(model: semantic_function::Model) -> Self {
104 Self {
105 name: model.function_name,
106 contract: model.contract_path,
107 signature: None,
108 }
109 }
110}