chore: checkpoint pi lab rust integration work
This commit is contained in:
@@ -33,7 +33,7 @@ pub async fn status(
|
||||
Query(body),
|
||||
)
|
||||
.await?;
|
||||
Ok(payload)
|
||||
Ok(compact_status_result_for_agent(payload))
|
||||
}
|
||||
|
||||
pub async fn search(
|
||||
@@ -235,18 +235,17 @@ fn ensure_weknora_scope(
|
||||
.with_context(context))
|
||||
}
|
||||
|
||||
fn compact_query_result_for_agent(payload: Value) -> Value {
|
||||
let references = payload
|
||||
pub(crate) fn compact_query_result_for_agent(payload: Value) -> Value {
|
||||
let payload_references = payload
|
||||
.get("references")
|
||||
.and_then(Value::as_array)
|
||||
.map(|items| {
|
||||
items
|
||||
.iter()
|
||||
.take(8)
|
||||
.map(compact_reference_for_agent)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let references = payload_references
|
||||
.iter()
|
||||
.take(8)
|
||||
.map(compact_reference_for_agent)
|
||||
.collect::<Vec<_>>();
|
||||
let payload_citations = payload
|
||||
.get("citations")
|
||||
.and_then(Value::as_array)
|
||||
@@ -255,16 +254,33 @@ fn compact_query_result_for_agent(payload: Value) -> Value {
|
||||
let citations = if payload_citations.is_empty() {
|
||||
citation_references_for_ui(&references)
|
||||
.into_iter()
|
||||
.take(8)
|
||||
.map(compact_citation_for_agent)
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
citation_values_for_ui(&payload_citations)
|
||||
.into_iter()
|
||||
.take(8)
|
||||
.map(compact_citation_for_agent)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let citation_markdowns = citations
|
||||
let ui_citations = if payload_citations.is_empty() {
|
||||
citation_references_for_ui(&payload_references)
|
||||
} else {
|
||||
citation_values_for_ui(&payload_citations)
|
||||
}
|
||||
.into_iter()
|
||||
.take(8)
|
||||
.map(compact_ui_citation_for_agent)
|
||||
.collect::<Vec<_>>();
|
||||
let citation_count = if payload_citations.is_empty() {
|
||||
payload_references.len()
|
||||
} else {
|
||||
payload_citations.len()
|
||||
};
|
||||
let citation_markdowns = ui_citations
|
||||
.iter()
|
||||
.take(2)
|
||||
.filter_map(|citation| {
|
||||
citation
|
||||
.get("citationMarkdown")
|
||||
@@ -273,7 +289,6 @@ fn compact_query_result_for_agent(payload: Value) -> Value {
|
||||
.map(ToOwned::to_owned)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let ui_citations = citations.clone();
|
||||
let document_structure_index = payload
|
||||
.get("documentStructureIndex")
|
||||
.cloned()
|
||||
@@ -285,8 +300,13 @@ fn compact_query_result_for_agent(payload: Value) -> Value {
|
||||
"provider": payload.get("provider").cloned().unwrap_or_else(|| json!("weknora")),
|
||||
"answerGuidance": "Final answers should answer the user in plain text. Do not copy citationMarkdown, citationUrl, /documents, mnote://, or search-engine wrapped local links into the answer. MNote UI will render citations[] / references[].citationMarkdown after the answer as clickable source locators. Treat rawMetadata.keywords and entity/relation counts only as query analysis, not proof that a source contains those words. Use references[].quote/contentDiagnostics as evidence. For book-like or skip-KG documents, call the tool with sourcePaths and includeDocumentStructureIndex=true; MNote fixes their effective retrieval mode to naive because they intentionally do not build KG. If documentStructureIndex is present, use it as a section map and call mnote.knowledge_rag.section_context with the section range when you need bounded chapter text for second-pass reading; do not cite the map itself unless the same claim appears in references[].quote or section_context text. If locatorDegraded is true, say the source location is degraded instead of inventing page or bbox. If contentDiagnostics.ocrTextExposed is false, say OCR text was not exposed in the returned reference instead of claiming OCR succeeded.",
|
||||
"references": references,
|
||||
"referenceCount": payload_references.len(),
|
||||
"referencesTruncated": payload_references.len() > 8,
|
||||
"citations": citations,
|
||||
"citationCount": citation_count,
|
||||
"citationsTruncated": citation_count > 8,
|
||||
"citationMarkdowns": citation_markdowns,
|
||||
"citationMarkdownsTruncated": ui_citations.len() > 2,
|
||||
"uiCitations": ui_citations,
|
||||
"documentStructureIndex": compact_document_structure_index_for_agent(&document_structure_index),
|
||||
"citationRendering": "MNote UI appends uiCitations/citations after the final answer; agent must not hand-write citation links.",
|
||||
@@ -303,6 +323,177 @@ fn compact_query_result_for_agent(payload: Value) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compact_status_result_for_agent(payload: Value) -> Value {
|
||||
let document_values = payload
|
||||
.pointer("/documents/documents")
|
||||
.and_then(Value::as_array)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let documents = document_values
|
||||
.iter()
|
||||
.take(12)
|
||||
.map(|document| {
|
||||
json!({
|
||||
"id": document.get("id").cloned().unwrap_or(Value::Null),
|
||||
"filePath": document.get("filePath").cloned().unwrap_or(Value::Null),
|
||||
"status": document.get("status").cloned().unwrap_or(Value::Null),
|
||||
"statusGroup": document.get("statusGroup").cloned().unwrap_or(Value::Null),
|
||||
"summary": compact_text_value(document.get("summary"), 240),
|
||||
"chunksCount": document.get("chunksCount").cloned().unwrap_or(Value::Null),
|
||||
"updatedAt": document.get("updatedAt").cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let registry_entries = payload
|
||||
.pointer("/registry/entries")
|
||||
.and_then(Value::as_array)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let sources = registry_entries
|
||||
.iter()
|
||||
.take(16)
|
||||
.map(|source| {
|
||||
json!({
|
||||
"sourceId": source.get("sourceId").cloned().unwrap_or(Value::Null),
|
||||
"sourceRootRelativePath": source.get("sourceRootRelativePath").cloned().unwrap_or(Value::Null),
|
||||
"sourceKind": source.get("sourceKind").cloned().unwrap_or(Value::Null),
|
||||
"status": source.get("status").cloned().unwrap_or(Value::Null),
|
||||
"provider": source.get("provider").cloned().unwrap_or(Value::Null),
|
||||
"providerStatus": source.get("providerStatus").cloned().unwrap_or(Value::Null),
|
||||
"lightRagStatus": source.get("lightRagStatus").cloned().unwrap_or(Value::Null),
|
||||
"stale": source.get("stale").cloned().unwrap_or(Value::Bool(false)),
|
||||
"retryRequired": source.get("retryRequired").cloned().unwrap_or(Value::Bool(false)),
|
||||
"lastError": compact_text_value(source.get("lastError"), 320),
|
||||
"updatedAtMs": source.get("updatedAtMs").cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let indexed_roots = payload
|
||||
.pointer("/registry/indexedRoots")
|
||||
.and_then(Value::as_array)
|
||||
.map(|items| {
|
||||
items
|
||||
.iter()
|
||||
.take(12)
|
||||
.map(|root| {
|
||||
json!({
|
||||
"rootRelativePath": root.get("rootRelativePath").cloned().unwrap_or(Value::Null),
|
||||
"recursive": root.get("recursive").cloned().unwrap_or(Value::Bool(false)),
|
||||
"runOnChange": root.get("runOnChange").cloned().unwrap_or(Value::Null),
|
||||
"updatedAtMs": root.get("updatedAtMs").cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let knowledge_base_values = payload
|
||||
.get("knowledgeBases")
|
||||
.and_then(|value| {
|
||||
value
|
||||
.get("bases")
|
||||
.and_then(Value::as_array)
|
||||
.or_else(|| value.as_array())
|
||||
})
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let knowledge_bases = knowledge_base_values
|
||||
.iter()
|
||||
.take(12)
|
||||
.map(|base| {
|
||||
json!({
|
||||
"baseId": base.get("baseId").cloned().unwrap_or(Value::Null),
|
||||
"name": base.get("name").cloned().unwrap_or(Value::Null),
|
||||
"provider": base.get("provider").cloned().unwrap_or(Value::Null),
|
||||
"providerKbId": base.get("providerKbId").cloned().unwrap_or(Value::Null),
|
||||
"defaultToolEnabled": base.get("defaultToolEnabled").cloned().unwrap_or(Value::Bool(false)),
|
||||
"canWrite": base.get("canWrite").cloned().unwrap_or(Value::Bool(false)),
|
||||
"sourceCount": base.get("sourceCount").cloned().unwrap_or(Value::Null),
|
||||
"chunkCount": base.get("chunkCount").cloned().unwrap_or(Value::Null),
|
||||
"status": base.get("status").cloned().unwrap_or(Value::Null),
|
||||
"updatedAtMs": base.get("updatedAtMs").cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pipeline = payload.get("pipeline").unwrap_or(&Value::Null);
|
||||
let rerank = payload.get("rerank").unwrap_or(&Value::Null);
|
||||
json!({
|
||||
"ok": payload.get("ok").cloned().unwrap_or_else(|| json!(true)),
|
||||
"schema": "mnote.knowledge_rag.agent_status_result.v1",
|
||||
"provider": payload.get("provider").cloned().unwrap_or(Value::Null),
|
||||
"endpoint": payload.get("endpoint").cloned().unwrap_or(Value::Null),
|
||||
"health": compact_health_for_agent(payload.get("health")),
|
||||
"rerank": {
|
||||
"enabled": rerank.get("enabled").cloned().unwrap_or(Value::Bool(false)),
|
||||
"available": rerank.get("available").cloned().unwrap_or(Value::Bool(false)),
|
||||
"status": rerank.get("status").cloned().unwrap_or(Value::Null),
|
||||
"binding": rerank.get("binding").cloned().unwrap_or(Value::Null),
|
||||
"model": rerank.get("model").or_else(|| rerank.get("rerankModel")).cloned().unwrap_or(Value::Null),
|
||||
"minScore": rerank.get("minScore").cloned().unwrap_or(Value::Null),
|
||||
},
|
||||
"documents": {
|
||||
"ok": payload.pointer("/documents/ok").cloned().unwrap_or(Value::Bool(false)),
|
||||
"total": document_values.len(),
|
||||
"statusGroups": payload.pointer("/documents/rawStatusGroups").cloned().unwrap_or_else(|| json!({})),
|
||||
"items": documents,
|
||||
"truncated": document_values.len() > 12,
|
||||
},
|
||||
"pipeline": {
|
||||
"ok": pipeline.get("ok").cloned().unwrap_or(Value::Bool(false)),
|
||||
"busy": pipeline.get("busy").cloned().unwrap_or(Value::Bool(false)),
|
||||
"scanning": pipeline.get("scanning").cloned().unwrap_or(Value::Bool(false)),
|
||||
"requestPending": pipeline.get("requestPending").cloned().unwrap_or(Value::Bool(false)),
|
||||
"pendingEnqueues": pipeline.get("pendingEnqueues").cloned().unwrap_or(Value::Null),
|
||||
"docs": pipeline.get("docs").cloned().unwrap_or(Value::Null),
|
||||
"batches": pipeline.get("batches").cloned().unwrap_or(Value::Null),
|
||||
"currentBatch": pipeline.get("currentBatch").cloned().unwrap_or(Value::Null),
|
||||
"jobName": pipeline.get("jobName").cloned().unwrap_or(Value::Null),
|
||||
"latestMessage": compact_text_value(pipeline.get("latestMessage"), 400),
|
||||
"progress": pipeline.get("progress").cloned().unwrap_or(Value::Null),
|
||||
},
|
||||
"sourceRegistry": {
|
||||
"workspaceId": payload.pointer("/registry/workspaceId").cloned().unwrap_or(Value::Null),
|
||||
"rootUri": payload.pointer("/registry/rootUri").cloned().unwrap_or(Value::Null),
|
||||
"updatedAtMs": payload.pointer("/registry/updatedAtMs").cloned().unwrap_or(Value::Null),
|
||||
"sourceCount": registry_entries.len(),
|
||||
"indexedRootCount": payload.pointer("/registry/indexedRoots").and_then(Value::as_array).map(Vec::len).unwrap_or(0),
|
||||
"indexedRoots": indexed_roots,
|
||||
"sources": sources,
|
||||
"truncated": registry_entries.len() > 16,
|
||||
},
|
||||
"knowledgeBases": {
|
||||
"total": knowledge_base_values.len(),
|
||||
"items": knowledge_bases,
|
||||
"truncated": knowledge_base_values.len() > 12,
|
||||
},
|
||||
"agentGuidance": "This is a compact provider/status summary for the agent. The full provider configuration, raw health payload, registry providerResponse, and complete document lists remain available to the MNote UI/API and are intentionally omitted from the model context.",
|
||||
})
|
||||
}
|
||||
|
||||
fn compact_health_for_agent(value: Option<&Value>) -> Value {
|
||||
let nested = value.and_then(|health| health.get("health"));
|
||||
json!({
|
||||
"ok": value.and_then(|health| health.get("ok")).cloned().unwrap_or(Value::Bool(false)),
|
||||
"status": nested.and_then(|health| health.get("status")).or_else(|| value.and_then(|health| health.get("status"))).cloned().unwrap_or(Value::Null),
|
||||
"healthy": nested.and_then(|health| health.get("healthy")).or_else(|| value.and_then(|health| health.get("healthy"))).cloned().unwrap_or(Value::Null),
|
||||
"provider": nested.and_then(|health| health.get("provider")).or_else(|| value.and_then(|health| health.get("provider"))).cloned().unwrap_or(Value::Null),
|
||||
"version": nested.and_then(|health| health.get("version")).or_else(|| value.and_then(|health| health.get("version"))).cloned().unwrap_or(Value::Null),
|
||||
"code": value.and_then(|health| health.get("code")).cloned().unwrap_or(Value::Null),
|
||||
"message": compact_text_value(value.and_then(|health| health.get("message")), 320),
|
||||
})
|
||||
}
|
||||
|
||||
fn compact_text_value(value: Option<&Value>, max_chars: usize) -> Value {
|
||||
value
|
||||
.and_then(Value::as_str)
|
||||
.map(|text| text.chars().take(max_chars).collect::<String>())
|
||||
.map(Value::String)
|
||||
.unwrap_or(Value::Null)
|
||||
}
|
||||
|
||||
fn compact_document_structure_index_for_agent(value: &Value) -> Value {
|
||||
if value.is_null() {
|
||||
return Value::Null;
|
||||
@@ -393,7 +584,7 @@ fn compact_structure_section_for_agent(section: &Value) -> Value {
|
||||
})
|
||||
}
|
||||
|
||||
fn compact_section_context_for_agent(payload: Value) -> Value {
|
||||
pub(crate) fn compact_section_context_for_agent(payload: Value) -> Value {
|
||||
let blocks = payload
|
||||
.get("blocks")
|
||||
.and_then(Value::as_array)
|
||||
@@ -470,12 +661,7 @@ fn compact_reference_for_agent(reference: &Value) -> Value {
|
||||
.get("displayQuote")
|
||||
.and_then(Value::as_str)
|
||||
.or_else(|| reference.get("quote").and_then(Value::as_str))
|
||||
.map(|value| value.chars().take(700).collect::<String>())
|
||||
.unwrap_or_default();
|
||||
let locator_evidence_text = reference
|
||||
.get("locatorEvidenceText")
|
||||
.and_then(Value::as_str)
|
||||
.map(|value| value.chars().take(700).collect::<String>())
|
||||
.map(|value| value.chars().take(560).collect::<String>())
|
||||
.unwrap_or_default();
|
||||
let quote_diagnostics = reference
|
||||
.get("contentDiagnostics")
|
||||
@@ -490,16 +676,11 @@ fn compact_reference_for_agent(reference: &Value) -> Value {
|
||||
"sourceRootRelativePath": reference.get("sourceRootRelativePath").cloned().unwrap_or(Value::Null),
|
||||
"chunkId": reference.get("chunkId").cloned().unwrap_or(Value::Null),
|
||||
"quote": quote,
|
||||
"displayQuote": reference.get("displayQuote").cloned().unwrap_or(Value::Null),
|
||||
"locatorEvidenceText": locator_evidence_text,
|
||||
"quoteSource": reference.get("quoteSource").cloned().unwrap_or(Value::Null),
|
||||
"locator": reference.get("locator").cloned().unwrap_or(Value::Null),
|
||||
"locatorPrecision": reference.get("locatorPrecision").cloned().unwrap_or(Value::Null),
|
||||
"locatorDegraded": reference.get("locatorDegraded").cloned().unwrap_or(Value::Bool(true)),
|
||||
"contentDiagnostics": quote_diagnostics,
|
||||
"citationDiagnostics": reference.get("citationDiagnostics").cloned().unwrap_or(Value::Null),
|
||||
"citationMarkdown": reference.get("citationMarkdown").cloned().unwrap_or(Value::Null),
|
||||
"citationUrl": reference.get("citationUrl").cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -508,12 +689,7 @@ fn compact_citation_for_agent(citation: &Value) -> Value {
|
||||
.get("displayQuote")
|
||||
.and_then(Value::as_str)
|
||||
.or_else(|| citation.get("quote").and_then(Value::as_str))
|
||||
.map(|value| value.chars().take(420).collect::<String>())
|
||||
.unwrap_or_default();
|
||||
let locator_evidence_text = citation
|
||||
.get("locatorEvidenceText")
|
||||
.and_then(Value::as_str)
|
||||
.map(|value| value.chars().take(420).collect::<String>())
|
||||
.map(|value| value.chars().take(360).collect::<String>())
|
||||
.unwrap_or_default();
|
||||
json!({
|
||||
"schema": citation.get("schema").cloned().unwrap_or_else(|| json!("mnote.knowledge_rag.citation.v1")),
|
||||
@@ -521,21 +697,25 @@ fn compact_citation_for_agent(citation: &Value) -> Value {
|
||||
"citationId": citation.get("citationId").cloned().unwrap_or(Value::Null),
|
||||
"citationLabel": citation.get("citationLabel").cloned().unwrap_or(Value::Null),
|
||||
"sourceId": citation.get("sourceId").cloned().unwrap_or(Value::Null),
|
||||
"sourcePath": citation.get("sourcePath").cloned().unwrap_or(Value::Null),
|
||||
"sourceRootRelativePath": citation.get("sourceRootRelativePath").cloned().unwrap_or(Value::Null),
|
||||
"filePath": citation.get("filePath").or_else(|| citation.get("lightRagFilePath")).cloned().unwrap_or(Value::Null),
|
||||
"chunkId": citation.get("chunkId").or_else(|| citation.get("lightRagChunkId")).cloned().unwrap_or(Value::Null),
|
||||
"blockId": citation.get("blockId").cloned().unwrap_or(Value::Null),
|
||||
"headingPath": citation.get("headingPath").cloned().unwrap_or_else(|| json!([])),
|
||||
"quote": display_quote,
|
||||
"displayQuote": citation.get("displayQuote").cloned().unwrap_or(Value::Null),
|
||||
"locatorEvidenceText": locator_evidence_text,
|
||||
"searchQuery": citation.get("searchQuery").cloned().unwrap_or(Value::Null),
|
||||
"locatorPrecision": citation.get("locatorPrecision").cloned().unwrap_or(Value::Null),
|
||||
"locatorDegraded": citation.get("locatorDegraded").cloned().unwrap_or(Value::Bool(true)),
|
||||
})
|
||||
}
|
||||
|
||||
fn compact_ui_citation_for_agent(citation: &Value) -> Value {
|
||||
json!({
|
||||
"citationId": citation.get("citationId").cloned().unwrap_or(Value::Null),
|
||||
"citationLabel": citation.get("citationLabel").cloned().unwrap_or(Value::Null),
|
||||
"sourceRootRelativePath": citation.get("sourceRootRelativePath").cloned().unwrap_or(Value::Null),
|
||||
"headingPath": citation.get("headingPath").cloned().unwrap_or_else(|| json!([])),
|
||||
"locatorPrecision": citation.get("locatorPrecision").cloned().unwrap_or(Value::Null),
|
||||
"locatorDegraded": citation.get("locatorDegraded").cloned().unwrap_or(Value::Bool(true)),
|
||||
"citationMarkdown": citation.get("citationMarkdown").cloned().unwrap_or(Value::Null),
|
||||
"citationUrl": citation.get("citationUrl").cloned().unwrap_or(Value::Null),
|
||||
"diagnostics": citation.get("diagnostics").or_else(|| citation.get("citationDiagnostics")).cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -669,8 +849,9 @@ mod tests {
|
||||
|
||||
assert_eq!(compact["citations"][0]["citationId"], "c0de");
|
||||
assert_eq!(compact["citations"][0]["headingPath"][0], "保护基");
|
||||
assert_eq!(compact["citations"][0]["displayQuote"], "吡咯烷,5 h,90%");
|
||||
assert_eq!(compact["citations"][0]["quote"], "吡咯烷,5 h,90%");
|
||||
assert!(compact["citations"][0].get("rawQuote").is_none());
|
||||
assert!(compact["citations"][0].get("citationMarkdown").is_none());
|
||||
assert_eq!(
|
||||
compact["uiCitations"][0]["citationMarkdown"].as_str(),
|
||||
Some("[docs/a.docx](/documents/a)")
|
||||
@@ -701,4 +882,132 @@ mod tests {
|
||||
Some("chunk")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_status_result_keeps_capability_summary_without_large_payloads() {
|
||||
let documents = (0..20)
|
||||
.map(|index| {
|
||||
json!({
|
||||
"id": format!("doc-{index}"),
|
||||
"filePath": format!("docs/{index}.md"),
|
||||
"status": "processed",
|
||||
"statusGroup": "processed",
|
||||
"summary": "x".repeat(2_000),
|
||||
"chunksCount": 10,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let sources = (0..24)
|
||||
.map(|index| {
|
||||
json!({
|
||||
"sourceId": format!("source-{index}"),
|
||||
"sourceRootRelativePath": format!("docs/{index}.md"),
|
||||
"providerStatus": "indexed",
|
||||
"providerResponse": {"raw": "y".repeat(4_000)},
|
||||
"lastError": "z".repeat(1_000),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let payload = json!({
|
||||
"ok": true,
|
||||
"provider": "lightrag",
|
||||
"providerConfig": {"apiKey": "must-not-reach-agent"},
|
||||
"endpoint": "http://127.0.0.1:9621",
|
||||
"health": {"ok": true, "health": {"status": "healthy", "raw": "h".repeat(8_000)}},
|
||||
"rerank": {"enabled": true, "available": true, "status": "ready", "model": "reranker"},
|
||||
"documents": {
|
||||
"ok": true,
|
||||
"rawStatusGroups": {"processed": 20},
|
||||
"documents": documents,
|
||||
},
|
||||
"pipeline": {
|
||||
"ok": true,
|
||||
"busy": false,
|
||||
"historyMessages": vec!["history".repeat(1_000); 100],
|
||||
"latestMessage": "latest".repeat(500),
|
||||
},
|
||||
"registry": {
|
||||
"workspaceId": "workspace",
|
||||
"rootUri": "file:///workspace",
|
||||
"indexedRoots": [{"rootRelativePath": "docs", "recursive": true}],
|
||||
"entries": sources,
|
||||
},
|
||||
});
|
||||
|
||||
let compact = compact_status_result_for_agent(payload);
|
||||
|
||||
assert_eq!(
|
||||
compact["schema"],
|
||||
"mnote.knowledge_rag.agent_status_result.v1"
|
||||
);
|
||||
assert_eq!(compact["documents"]["total"], 20);
|
||||
assert_eq!(compact["documents"]["items"].as_array().unwrap().len(), 12);
|
||||
assert_eq!(compact["sourceRegistry"]["sourceCount"], 24);
|
||||
assert_eq!(
|
||||
compact["sourceRegistry"]["sources"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.len(),
|
||||
16
|
||||
);
|
||||
assert!(compact.get("providerConfig").is_none());
|
||||
assert!(compact["health"].get("raw").is_none());
|
||||
assert!(compact["sourceRegistry"]["sources"][0]
|
||||
.get("providerResponse")
|
||||
.is_none());
|
||||
assert!(
|
||||
serde_json::to_vec(&compact)
|
||||
.expect("serialize compact status")
|
||||
.len()
|
||||
< 40_000
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_query_result_caps_repeated_evidence_and_ui_locators() {
|
||||
let references = (0..40)
|
||||
.map(|index| {
|
||||
json!({
|
||||
"citationId": format!("ref-{index}"),
|
||||
"sourceRootRelativePath": format!("docs/{index}.md"),
|
||||
"displayQuote": "evidence".repeat(500),
|
||||
"locatorEvidenceText": "duplicate evidence".repeat(500),
|
||||
"citationMarkdown": format!("[docs/{index}.md](/documents/{})", "x".repeat(4_000)),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let citations = (0..40)
|
||||
.map(|index| {
|
||||
json!({
|
||||
"citationId": format!("citation-{index}"),
|
||||
"citationLabel": format!("[{index}]"),
|
||||
"sourceRootRelativePath": format!("docs/{index}.md"),
|
||||
"displayQuote": "citation evidence".repeat(500),
|
||||
"sourcePath": format!("/very/long/{}", "path".repeat(1_000)),
|
||||
"citationMarkdown": format!("[docs/{index}.md](/documents/{})", "y".repeat(4_000)),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let compact = compact_query_result_for_agent(json!({
|
||||
"references": references,
|
||||
"citations": citations,
|
||||
}));
|
||||
|
||||
assert_eq!(compact["references"].as_array().unwrap().len(), 8);
|
||||
assert_eq!(compact["citations"].as_array().unwrap().len(), 8);
|
||||
assert_eq!(compact["uiCitations"].as_array().unwrap().len(), 8);
|
||||
assert_eq!(compact["referenceCount"], 40);
|
||||
assert_eq!(compact["citationCount"], 40);
|
||||
assert_eq!(compact["referencesTruncated"], true);
|
||||
assert_eq!(compact["citationsTruncated"], true);
|
||||
assert!(compact["references"][0].get("citationMarkdown").is_none());
|
||||
assert!(compact["citations"][0].get("citationMarkdown").is_none());
|
||||
assert!(
|
||||
serde_json::to_vec(&compact)
|
||||
.expect("serialize compact query")
|
||||
.len()
|
||||
< 60_000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1177,7 +1177,8 @@ pub async fn admin_put_user_settings(
|
||||
|
||||
// ─── Constants ──────────────────────────────────────────────────────────
|
||||
|
||||
const DEFAULT_PI_MODEL: &str = "omniroute/freefirst";
|
||||
const DEFAULT_PI_MODEL: &str = "omniroute/gpt-5.4-mini";
|
||||
const FREEFIRST_PI_MODEL: &str = "omniroute/freefirst";
|
||||
const LIGHTRAG_PROVIDER: &str = "lightrag";
|
||||
const LIGHTRAG_PROVIDER_DESCRIPTION: &str = "MNote 知识库默认提供者(LightRAG)";
|
||||
const SOURCE_OF_TRUTH: &str = "directory_grants";
|
||||
@@ -1904,6 +1905,12 @@ pub(crate) fn load_effective_ai_runtime_policy(
|
||||
.into_iter()
|
||||
.filter_map(|entry| normalize_model_ref(Some(&default_provider), &entry.id))
|
||||
.collect::<Vec<_>>();
|
||||
if !allowed_models.iter().any(|model| model == FREEFIRST_PI_MODEL) {
|
||||
allowed_models.push(FREEFIRST_PI_MODEL.to_string());
|
||||
}
|
||||
if !allowed_models.iter().any(|model| model == DEFAULT_PI_MODEL) {
|
||||
allowed_models.push(DEFAULT_PI_MODEL.to_string());
|
||||
}
|
||||
allowed_models.sort();
|
||||
allowed_models.dedup();
|
||||
|
||||
|
||||
@@ -601,6 +601,13 @@ pub fn build_router(state: AppState) -> Router {
|
||||
.route("/api/page-ai/pi/start", post(page_ai_pi::start))
|
||||
.route("/api/page-ai/pi/send", post(page_ai_pi::send))
|
||||
.route("/api/page-ai/pi/abort", post(page_ai_pi::abort))
|
||||
.route("/api/page-ai/pi/configure", post(page_ai_pi::configure))
|
||||
.route("/api/page-ai/pi/state", post(page_ai_pi::state))
|
||||
.route("/api/page-ai/pi/compact", post(page_ai_pi::compact))
|
||||
.route(
|
||||
"/api/page-ai/pi/queue-config",
|
||||
post(page_ai_pi::queue_config),
|
||||
)
|
||||
.route("/api/page-ai/pi/ui-response", post(page_ai_pi::ui_response))
|
||||
.route(
|
||||
"/api/page-ai/pi/ui-request-bridge",
|
||||
@@ -631,6 +638,15 @@ pub fn build_router(state: AppState) -> Router {
|
||||
post(page_ai_pi::mcp_call_bridge),
|
||||
)
|
||||
.route("/page-ai/pi", get(page_ai_pi::shell))
|
||||
.route(
|
||||
"/api/page-ai/pi/sessions/{session_id}/tree",
|
||||
get(page_ai_pi::session_tree),
|
||||
)
|
||||
.route("/api/page-ai/pi/fork", post(page_ai_pi::fork_pi_session))
|
||||
.route(
|
||||
"/api/page-ai/pi/artifacts/{tool_event_id}/diff",
|
||||
get(page_ai_pi::artifact_diff),
|
||||
)
|
||||
.route(
|
||||
"/api/sidebar/shortcuts",
|
||||
get(sidebar_shortcuts::list_shortcuts).post(sidebar_shortcuts::upsert_shortcut),
|
||||
|
||||
Reference in New Issue
Block a user