Skip to main content

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}