feat(ai): switch page ai to hermes panel
This commit is contained in:
@@ -7376,10 +7376,10 @@ fn build_sidebar_kernel_nodes(
|
||||
}
|
||||
|
||||
fn build_sidebar_kernel_edges(nodes: &[KernelNode]) -> Vec<KernelEdge> {
|
||||
nodes
|
||||
.iter()
|
||||
.filter_map(|node| {
|
||||
node.parent_id.as_ref().map(|parent_id| KernelEdge {
|
||||
let mut edges = Vec::new();
|
||||
for node in nodes {
|
||||
if let Some(parent_id) = node.parent_id.as_ref() {
|
||||
edges.push(KernelEdge {
|
||||
id: format!("edge_parent_of_{}_{}", parent_id, node.id),
|
||||
edge_type: KernelEdgeType::ParentOf,
|
||||
workspace_id: node.workspace_id.clone(),
|
||||
@@ -7387,9 +7387,58 @@ fn build_sidebar_kernel_edges(nodes: &[KernelNode]) -> Vec<KernelEdge> {
|
||||
to_node_id: node.id.clone(),
|
||||
metadata: BTreeMap::new(),
|
||||
audit: KernelAuditStamp::default(),
|
||||
})
|
||||
});
|
||||
if let Some(artifact_type) = ai_artifact_type_for_node(node) {
|
||||
edges.push(KernelEdge {
|
||||
id: format!("edge_ai_artifact_reference_{}_{}", parent_id, node.id),
|
||||
edge_type: KernelEdgeType::SourceOf,
|
||||
workspace_id: node.workspace_id.clone(),
|
||||
from_node_id: parent_id.clone(),
|
||||
to_node_id: node.id.clone(),
|
||||
metadata: BTreeMap::from([
|
||||
("kind".into(), json!("ai_artifact_reference")),
|
||||
("artifactType".into(), json!(artifact_type)),
|
||||
("projectionOnlyGroup".into(), json!("AI Artifacts")),
|
||||
]),
|
||||
audit: KernelAuditStamp::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
edges
|
||||
}
|
||||
|
||||
fn ai_artifact_type_for_node(node: &KernelNode) -> Option<&'static str> {
|
||||
let parent_id = node.parent_id.as_ref()?;
|
||||
if node.id == format!("summary_{parent_id}") {
|
||||
return Some("summary");
|
||||
}
|
||||
if node.id.starts_with(&format!("ai_note_{parent_id}_")) {
|
||||
return Some("ai_note");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn ai_artifacts_projection_group_meta(edges: &[KernelEdge]) -> Value {
|
||||
let artifact_document_ids = edges
|
||||
.iter()
|
||||
.filter(|edge| {
|
||||
edge.metadata
|
||||
.get("kind")
|
||||
.and_then(Value::as_str)
|
||||
.map(|kind| kind == "ai_artifact_reference")
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.collect()
|
||||
.map(|edge| edge.to_node_id.clone())
|
||||
.collect::<Vec<_>>();
|
||||
json!({
|
||||
"title": "AI Artifacts",
|
||||
"projectionOnly": true,
|
||||
"source": "kernel.project_view.synthetic_group",
|
||||
"kernelNodeId": Value::Null,
|
||||
"edgeKind": "ai_artifact_reference",
|
||||
"artifactDocumentIds": artifact_document_ids,
|
||||
})
|
||||
}
|
||||
|
||||
fn kernel_node_sort_order(node: &KernelNode) -> i64 {
|
||||
@@ -7979,6 +8028,36 @@ fn build_file_tree_projection_result(
|
||||
let visible_rows = items.len();
|
||||
let visible_edges = edges.len();
|
||||
|
||||
let mut meta = BTreeMap::from([(
|
||||
"search".into(),
|
||||
json!({
|
||||
"query": requested_query.clone(),
|
||||
"maxResults": requested_max_results,
|
||||
"maxResultsRule": "matches_only_before_ancestor_completion",
|
||||
"ancestorCompletion": "include_all_ancestors_after_match_truncation",
|
||||
"ordering": "kernel_file_tree_preorder",
|
||||
"indexingVisibility": {
|
||||
"schema": "mnote.file_tree.indexing_visibility",
|
||||
"schemaVersion": 1,
|
||||
"source": "kernel.project_view",
|
||||
"status": "visible",
|
||||
"requestKey": requested_query
|
||||
.as_ref()
|
||||
.map(|query| format!("{}:{query}", root_node_id.unwrap_or("root"))),
|
||||
"indexedResourceKinds": ["document", "index", "asset", "asset_folder", "mindmap", "book", "pdf"],
|
||||
"visibleResourceKinds": ["document", "index", "asset", "asset_folder", "mindmap", "book", "pdf"],
|
||||
"metrics": {
|
||||
"visibleRows": visible_rows,
|
||||
"visibleEdges": visible_edges,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)]);
|
||||
meta.insert(
|
||||
"aiArtifacts".into(),
|
||||
ai_artifacts_projection_group_meta(&edges),
|
||||
);
|
||||
|
||||
KernelProjectionResult {
|
||||
projection_id: format!(
|
||||
"kernel_projection:file_tree:{}",
|
||||
@@ -7988,31 +8067,7 @@ fn build_file_tree_projection_result(
|
||||
root_node_id: root_node_id.map(ToOwned::to_owned),
|
||||
items,
|
||||
edges,
|
||||
meta: BTreeMap::from([(
|
||||
"search".into(),
|
||||
json!({
|
||||
"query": requested_query.clone(),
|
||||
"maxResults": requested_max_results,
|
||||
"maxResultsRule": "matches_only_before_ancestor_completion",
|
||||
"ancestorCompletion": "include_all_ancestors_after_match_truncation",
|
||||
"ordering": "kernel_file_tree_preorder",
|
||||
"indexingVisibility": {
|
||||
"schema": "mnote.file_tree.indexing_visibility",
|
||||
"schemaVersion": 1,
|
||||
"source": "kernel.project_view",
|
||||
"status": "visible",
|
||||
"requestKey": requested_query
|
||||
.as_ref()
|
||||
.map(|query| format!("{}:{query}", root_node_id.unwrap_or("root"))),
|
||||
"indexedResourceKinds": ["document", "index", "asset", "asset_folder", "mindmap", "book", "pdf"],
|
||||
"visibleResourceKinds": ["document", "index", "asset", "asset_folder", "mindmap", "book", "pdf"],
|
||||
"metrics": {
|
||||
"visibleRows": visible_rows,
|
||||
"visibleEdges": visible_edges,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)]),
|
||||
meta,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17443,6 +17498,138 @@ mod tests {
|
||||
assert_eq!(result["edges"].as_array().map(Vec::len), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kernel_edges_list_exposes_ai_artifact_reference_edges() {
|
||||
let result = execute_runtime_query(RuntimeInput::Query {
|
||||
context: demo_context(),
|
||||
query: RuntimeQueryEnvelopeWire {
|
||||
name: "kernel.edges.list".into(),
|
||||
payload: json!({
|
||||
"workspaceId": "ws_1",
|
||||
"nodeId": "page_root",
|
||||
"direction": "both",
|
||||
}),
|
||||
},
|
||||
data: Some(json!({
|
||||
"documents": [
|
||||
{
|
||||
"id": "page_root",
|
||||
"workspace_id": "ws_1",
|
||||
"title": "根页面",
|
||||
"parent_id": null,
|
||||
"sort_order": 0,
|
||||
"is_starred": true,
|
||||
"is_template": false,
|
||||
"created_at": "2026-04-16T00:00:00Z",
|
||||
"updated_at": "2026-04-16T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "summary_page_root",
|
||||
"workspace_id": "ws_1",
|
||||
"title": "AI Summary",
|
||||
"parent_id": "page_root",
|
||||
"sort_order": 1,
|
||||
"is_starred": false,
|
||||
"is_template": false,
|
||||
"created_at": "2026-04-16T00:00:00Z",
|
||||
"updated_at": "2026-04-16T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "ai_note_page_root_req_1",
|
||||
"workspace_id": "ws_1",
|
||||
"title": "AI Note",
|
||||
"parent_id": "page_root",
|
||||
"sort_order": 2,
|
||||
"is_starred": false,
|
||||
"is_template": false,
|
||||
"created_at": "2026-04-16T00:00:00Z",
|
||||
"updated_at": "2026-04-16T00:00:00Z"
|
||||
}
|
||||
]
|
||||
})),
|
||||
})
|
||||
.expect("kernel edge list should build");
|
||||
|
||||
let edges = result["edges"].as_array().expect("edges should be array");
|
||||
let artifact_edges = edges
|
||||
.iter()
|
||||
.filter(|edge| edge["metadata"]["kind"] == json!("ai_artifact_reference"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(artifact_edges.len(), 2);
|
||||
assert_eq!(artifact_edges[0]["fromNodeId"], json!("page_root"));
|
||||
assert_eq!(artifact_edges[0]["toNodeId"], json!("summary_page_root"));
|
||||
assert_eq!(artifact_edges[0]["edgeType"], json!("source_of"));
|
||||
assert_eq!(artifact_edges[0]["metadata"]["artifactType"], json!("summary"));
|
||||
assert_eq!(artifact_edges[1]["toNodeId"], json!("ai_note_page_root_req_1"));
|
||||
assert_eq!(artifact_edges[1]["metadata"]["artifactType"], json!("ai_note"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_tree_projection_marks_ai_artifacts_group_as_projection_only() {
|
||||
let result = execute_runtime_query(RuntimeInput::Query {
|
||||
context: demo_context(),
|
||||
query: RuntimeQueryEnvelopeWire {
|
||||
name: "kernel.project_view".into(),
|
||||
payload: json!({
|
||||
"projection": "file_tree",
|
||||
"workspaceId": "ws_1",
|
||||
"rootNodeId": "page_root",
|
||||
"depth": 2,
|
||||
"includeEdges": true,
|
||||
"nodeTypes": ["page"],
|
||||
}),
|
||||
},
|
||||
data: Some(json!({
|
||||
"documents": [
|
||||
{
|
||||
"id": "page_root",
|
||||
"workspace_id": "ws_1",
|
||||
"title": "根页面",
|
||||
"parent_id": null,
|
||||
"sort_order": 0,
|
||||
"is_starred": true,
|
||||
"is_template": false,
|
||||
"created_at": "2026-04-16T00:00:00Z",
|
||||
"updated_at": "2026-04-16T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "summary_page_root",
|
||||
"workspace_id": "ws_1",
|
||||
"title": "AI Summary",
|
||||
"parent_id": "page_root",
|
||||
"sort_order": 1,
|
||||
"is_starred": false,
|
||||
"is_template": false,
|
||||
"created_at": "2026-04-16T00:00:00Z",
|
||||
"updated_at": "2026-04-16T00:00:00Z"
|
||||
}
|
||||
],
|
||||
"media_assets": [],
|
||||
"mindmap_assets": [],
|
||||
"table_assets": [],
|
||||
"mindmap_asset_children": {}
|
||||
})),
|
||||
})
|
||||
.expect("file tree projection should build");
|
||||
|
||||
let items = result["items"].as_array().expect("items should be array");
|
||||
assert!(!items
|
||||
.iter()
|
||||
.any(|item| item["nodeId"] == json!("AI Artifacts")));
|
||||
assert_eq!(result["meta"]["aiArtifacts"]["title"], json!("AI Artifacts"));
|
||||
assert_eq!(result["meta"]["aiArtifacts"]["projectionOnly"], json!(true));
|
||||
assert_eq!(
|
||||
result["meta"]["aiArtifacts"]["source"],
|
||||
json!("kernel.project_view.synthetic_group")
|
||||
);
|
||||
assert_eq!(result["meta"]["aiArtifacts"]["kernelNodeId"], Value::Null);
|
||||
assert_eq!(
|
||||
result["meta"]["aiArtifacts"]["artifactDocumentIds"],
|
||||
json!(["summary_page_root"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_rebuild_tool_executes_in_rust_runtime() {
|
||||
let result = execute_runtime_query(RuntimeInput::Tool {
|
||||
|
||||
Reference in New Issue
Block a user