noteconnection 1.6.8 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -21
- package/README.md +258 -64
- package/dist/src/agent_workspace.contract.parity.test.js +475 -0
- package/dist/src/agent_workspace.frontend.test.js +9989 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/PathBridge.js +28 -3
- package/dist/src/export/WorkspaceExportBundle.js +1130 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +406 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
- package/dist/src/frontend/README.md +258 -64
- package/dist/src/frontend/agent_workspace.js +5353 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +3597 -141
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +641 -266
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/locales/en.json +721 -5
- package/dist/src/frontend/locales/zh.json +721 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +107 -0
- package/dist/src/frontend/path_app.js +2189 -150
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +522 -27
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/simulationWorker.js +241 -6
- package/dist/src/frontend/source_manager.js +190 -21
- package/dist/src/frontend/styles.css +2853 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10168 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/indexing/IndexLifecycle.js +195 -0
- package/dist/src/indexing/IndexLifecycle.test.js +49 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +170 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
- package/dist/src/learning/answerReleaseReview.js +4319 -0
- package/dist/src/learning/answerReleaseReview.test.js +2888 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1480 -0
- package/dist/src/learning/conversationComposer.test.js +1817 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1176 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphContextAssembler.js +870 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/index.js +28 -0
- package/dist/src/learning/queryBackend.js +1898 -0
- package/dist/src/learning/queryBackend.test.js +955 -0
- package/dist/src/learning/ragContextPack.js +257 -0
- package/dist/src/learning/ragContextPack.test.js +160 -0
- package/dist/src/learning/ragPublicText.js +38 -0
- package/dist/src/learning/ragSufficiencyJudge.js +161 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
- package/dist/src/learning/requestNormalization.js +198 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1240 -0
- package/dist/src/learning/store.test.js +1126 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +66 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/auth.js +17 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +21 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +498 -13
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +143 -35
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +16 -2
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +58 -0
- package/dist/src/platform/PlatformCapabilities.js +45 -0
- package/dist/src/platform/PlatformCapabilities.test.js +30 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +73 -0
- package/dist/src/resources/ResourceRegistry.js +223 -0
- package/dist/src/resources/ResourceRegistry.test.js +61 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/data.js +267 -0
- package/dist/src/routes/diagnostics.js +51 -0
- package/dist/src/routes/index.js +23 -0
- package/dist/src/routes/knowledge.js +968 -0
- package/dist/src/routes/markdown.js +287 -0
- package/dist/src/routes/notemd.js +565 -0
- package/dist/src/routes/registry.contract.test.js +130 -0
- package/dist/src/routes/render.js +285 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +11090 -1549
- package/dist/src/server.migration.test.js +193 -21
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/tauri.frontend.build.contract.test.js +60 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/utils/RuntimePaths.js +4 -13
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +122 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +61 -10
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assembleAgentConversationGraphContext = assembleAgentConversationGraphContext;
|
|
4
|
+
exports.buildAgentConversationGraphContextFromKnowledgePoints = buildAgentConversationGraphContextFromKnowledgePoints;
|
|
5
|
+
const store_1 = require("./store");
|
|
6
|
+
function normalizeGraphComparableTitle(value) {
|
|
7
|
+
return normalizeWhitespace(String(value || '').toLowerCase());
|
|
8
|
+
}
|
|
9
|
+
function buildAnchorNodeExclusion(anchorPoint, graphContext) {
|
|
10
|
+
const atomIds = new Set([
|
|
11
|
+
graphContext.anchorAtomId,
|
|
12
|
+
...pointAtomIds(anchorPoint),
|
|
13
|
+
].map((atomId) => String(atomId || '').trim()).filter(Boolean));
|
|
14
|
+
const titles = new Set([
|
|
15
|
+
graphContext.anchorTitle,
|
|
16
|
+
anchorPoint.title,
|
|
17
|
+
].map(normalizeGraphComparableTitle).filter(Boolean));
|
|
18
|
+
return { atomIds, titles };
|
|
19
|
+
}
|
|
20
|
+
function isAnchorEquivalentNode(atomId, title, exclusion) {
|
|
21
|
+
const normalizedAtomId = String(atomId || '').trim();
|
|
22
|
+
if (normalizedAtomId && exclusion.atomIds.has(normalizedAtomId)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const normalizedTitle = normalizeGraphComparableTitle(title);
|
|
26
|
+
return Boolean(normalizedTitle && exclusion.titles.has(normalizedTitle));
|
|
27
|
+
}
|
|
28
|
+
function relationEdgeKey(edge) {
|
|
29
|
+
const edgeId = String(edge.edgeId || edge.id || '').trim();
|
|
30
|
+
if (edgeId) {
|
|
31
|
+
return `id:${edgeId}`;
|
|
32
|
+
}
|
|
33
|
+
return [
|
|
34
|
+
String(edge.sourceAtomId || '').trim(),
|
|
35
|
+
String(edge.targetAtomId || '').trim(),
|
|
36
|
+
String(edge.relationKind || '').trim(),
|
|
37
|
+
String(edge.provenance || '').trim(),
|
|
38
|
+
].join('|');
|
|
39
|
+
}
|
|
40
|
+
async function queryAdjacentEdgesForAnchorAtoms(opsStore, anchorAtomIds, direction, edgeLimit) {
|
|
41
|
+
const edgesByKey = new Map();
|
|
42
|
+
const normalizedAnchorAtomIds = Array.from(new Set(anchorAtomIds
|
|
43
|
+
.map((atomId) => String(atomId || '').trim())
|
|
44
|
+
.filter(Boolean)));
|
|
45
|
+
for (const atomId of normalizedAnchorAtomIds) {
|
|
46
|
+
const edges = await opsStore.queryEdges({
|
|
47
|
+
...(direction === 'predecessor' ? { toNodeId: atomId } : { fromNodeId: atomId }),
|
|
48
|
+
...(Number.isFinite(Number(edgeLimit)) && Number(edgeLimit) > 0
|
|
49
|
+
? { limit: Math.max(1, Math.floor(Number(edgeLimit))) }
|
|
50
|
+
: {}),
|
|
51
|
+
});
|
|
52
|
+
edges.forEach((edge) => {
|
|
53
|
+
edgesByKey.set(relationEdgeKey(edge), edge);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return Array.from(edgesByKey.values());
|
|
57
|
+
}
|
|
58
|
+
async function countUsableAdjacentNodes(opsStore, edges, direction, exclusion, titleCache) {
|
|
59
|
+
const seenNodeKeys = new Set();
|
|
60
|
+
for (const edge of edges) {
|
|
61
|
+
const relatedAtomId = direction === 'predecessor'
|
|
62
|
+
? String(edge.sourceAtomId || '').trim()
|
|
63
|
+
: String(edge.targetAtomId || '').trim();
|
|
64
|
+
if (!relatedAtomId || exclusion.atomIds.has(relatedAtomId)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const title = await resolveNodeTitle(opsStore, relatedAtomId, titleCache);
|
|
68
|
+
if (!title || isAnchorEquivalentNode(relatedAtomId, title, exclusion)) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
seenNodeKeys.add(relatedAtomId + '|' + normalizeGraphComparableTitle(title));
|
|
72
|
+
}
|
|
73
|
+
return seenNodeKeys.size;
|
|
74
|
+
}
|
|
75
|
+
function normalizeWhitespace(value) {
|
|
76
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
77
|
+
}
|
|
78
|
+
function tokenize(value) {
|
|
79
|
+
return Array.from(new Set(normalizeWhitespace(String(value || '').toLowerCase())
|
|
80
|
+
.replace(/[^\p{L}\p{N}\s_-]+/gu, ' ')
|
|
81
|
+
.split(/\s+/)
|
|
82
|
+
.map((token) => token.trim())
|
|
83
|
+
.filter((token) => token.length >= 2)));
|
|
84
|
+
}
|
|
85
|
+
function computeJaccard(left, right) {
|
|
86
|
+
if (left.length <= 0 || right.length <= 0) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
const leftSet = new Set(left);
|
|
90
|
+
const rightSet = new Set(right);
|
|
91
|
+
let intersection = 0;
|
|
92
|
+
leftSet.forEach((token) => {
|
|
93
|
+
if (rightSet.has(token)) {
|
|
94
|
+
intersection += 1;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const union = leftSet.size + rightSet.size - intersection;
|
|
98
|
+
if (union <= 0) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
return intersection / union;
|
|
102
|
+
}
|
|
103
|
+
function classifyConversationIntent(message) {
|
|
104
|
+
const normalized = normalizeWhitespace(String(message || '').toLowerCase());
|
|
105
|
+
if (!normalized) {
|
|
106
|
+
return 'generic';
|
|
107
|
+
}
|
|
108
|
+
if (normalized.includes('compare')
|
|
109
|
+
|| normalized.includes('difference')
|
|
110
|
+
|| normalized.includes('vs')
|
|
111
|
+
|| normalized.includes('区分')
|
|
112
|
+
|| normalized.includes('对比')) {
|
|
113
|
+
return 'compare';
|
|
114
|
+
}
|
|
115
|
+
if (normalized.includes('how to')
|
|
116
|
+
|| normalized.includes('how do')
|
|
117
|
+
|| normalized.includes('steps')
|
|
118
|
+
|| normalized.includes('plan')
|
|
119
|
+
|| normalized.includes('如何')
|
|
120
|
+
|| normalized.includes('怎么')
|
|
121
|
+
|| normalized.includes('步骤')
|
|
122
|
+
|| normalized.includes('方案')) {
|
|
123
|
+
return 'how_to';
|
|
124
|
+
}
|
|
125
|
+
if (normalized.includes('what is')
|
|
126
|
+
|| normalized.includes('why')
|
|
127
|
+
|| normalized.includes('explain')
|
|
128
|
+
|| normalized.includes('解释')
|
|
129
|
+
|| normalized.includes('什么是')
|
|
130
|
+
|| normalized.includes('为什么')) {
|
|
131
|
+
return 'explain';
|
|
132
|
+
}
|
|
133
|
+
return 'generic';
|
|
134
|
+
}
|
|
135
|
+
function clampInteger(value, minValue, maxValue) {
|
|
136
|
+
if (!Number.isFinite(value)) {
|
|
137
|
+
return minValue;
|
|
138
|
+
}
|
|
139
|
+
return Math.max(minValue, Math.min(maxValue, Math.floor(value)));
|
|
140
|
+
}
|
|
141
|
+
function resolveBudget(intent, budget) {
|
|
142
|
+
const defaultSupportNodes = intent === 'compare' || intent === 'how_to' ? 3 : 2;
|
|
143
|
+
const defaultSuccessors = intent === 'how_to' ? 3 : intent === 'compare' ? 2 : 1;
|
|
144
|
+
return {
|
|
145
|
+
maxSupportNodes: clampInteger(Number(budget?.maxSupportNodes ?? defaultSupportNodes), 1, 6),
|
|
146
|
+
maxConnectionPaths: clampInteger(Number(budget?.maxConnectionPaths ?? defaultSupportNodes), 1, 6),
|
|
147
|
+
maxPathDepth: clampInteger(Number(budget?.maxPathDepth ?? 6), 2, 8),
|
|
148
|
+
maxPredecessors: clampInteger(Number(budget?.maxPredecessors ?? 3), 0, 6),
|
|
149
|
+
maxSuccessors: clampInteger(Number(budget?.maxSuccessors ?? defaultSuccessors), 0, 6),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function pointAtomIds(point) {
|
|
153
|
+
const groupedAtomIds = Array.isArray(point.atomIds) && point.atomIds.length > 0
|
|
154
|
+
? point.atomIds
|
|
155
|
+
: [point.atomId];
|
|
156
|
+
return groupedAtomIds
|
|
157
|
+
.map((atomId) => String(atomId || '').trim())
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
}
|
|
160
|
+
function pointTitleMentionScore(message, point) {
|
|
161
|
+
const normalizedMessage = normalizeWhitespace(String(message || '').toLowerCase());
|
|
162
|
+
const normalizedTitle = normalizeWhitespace(String(point.title || '').toLowerCase());
|
|
163
|
+
if (!normalizedMessage || !normalizedTitle) {
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
if (normalizedMessage.includes(normalizedTitle)) {
|
|
167
|
+
return 0.24;
|
|
168
|
+
}
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
function selectAnchorPoint(message, knowledgePoints) {
|
|
172
|
+
const queryTokens = tokenize(message);
|
|
173
|
+
const ranked = knowledgePoints.map((point, index) => {
|
|
174
|
+
const titleTokens = tokenize(point.title);
|
|
175
|
+
const evidenceTokens = tokenize(`${point.summary || ''} ${point.evidenceSnippet || ''}`);
|
|
176
|
+
const titleMentionScore = pointTitleMentionScore(message, point);
|
|
177
|
+
const titleOverlapScore = computeJaccard(queryTokens, titleTokens) * 0.18;
|
|
178
|
+
const evidenceOverlapScore = computeJaccard(queryTokens, evidenceTokens) * 0.08;
|
|
179
|
+
const relationDensityScore = Math.min(Array.isArray(point.relationPath) ? point.relationPath.length : 0, 4) * 0.02;
|
|
180
|
+
const temporalPenalty = point.temporalValidity?.isValid === false ? 0.18 : 0;
|
|
181
|
+
const totalScore = Number((Number(point.score || 0)
|
|
182
|
+
+ titleMentionScore
|
|
183
|
+
+ titleOverlapScore
|
|
184
|
+
+ evidenceOverlapScore
|
|
185
|
+
+ relationDensityScore
|
|
186
|
+
- temporalPenalty).toFixed(6));
|
|
187
|
+
const reason = titleMentionScore > 0
|
|
188
|
+
? 'title_mention'
|
|
189
|
+
: titleOverlapScore > evidenceOverlapScore
|
|
190
|
+
? 'title_overlap'
|
|
191
|
+
: temporalPenalty > 0
|
|
192
|
+
? 'retrieval_score_with_temporal_penalty'
|
|
193
|
+
: 'retrieval_score';
|
|
194
|
+
return {
|
|
195
|
+
point,
|
|
196
|
+
index,
|
|
197
|
+
totalScore,
|
|
198
|
+
reason,
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
ranked.sort((left, right) => (right.totalScore - left.totalScore
|
|
202
|
+
|| left.index - right.index));
|
|
203
|
+
const best = ranked[0];
|
|
204
|
+
return {
|
|
205
|
+
point: best.point,
|
|
206
|
+
index: best.index,
|
|
207
|
+
reason: best.reason,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function isDirectlyLinkedToAnchor(point, anchorAtomIds) {
|
|
211
|
+
if (Array.isArray(point.relationPathAtomIds)) {
|
|
212
|
+
for (const atomId of point.relationPathAtomIds) {
|
|
213
|
+
if (anchorAtomIds.has(String(atomId || '').trim())) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (Array.isArray(point.relationPath)) {
|
|
219
|
+
for (const edge of point.relationPath) {
|
|
220
|
+
const sourceAtomId = String(edge?.sourceAtomId || '').trim();
|
|
221
|
+
const targetAtomId = String(edge?.targetAtomId || '').trim();
|
|
222
|
+
if (anchorAtomIds.has(sourceAtomId) || anchorAtomIds.has(targetAtomId)) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
function rankSupportPoints(message, intent, anchorPoint, knowledgePoints, budget) {
|
|
230
|
+
const queryTokens = tokenize(message);
|
|
231
|
+
const anchorAtomIdSet = new Set(pointAtomIds(anchorPoint));
|
|
232
|
+
const ranked = knowledgePoints
|
|
233
|
+
.filter((point) => point !== anchorPoint)
|
|
234
|
+
.map((point, index) => {
|
|
235
|
+
const titleTokens = tokenize(point.title);
|
|
236
|
+
const evidenceTokens = tokenize(`${point.summary || ''} ${point.evidenceSnippet || ''}`);
|
|
237
|
+
const directLinkScore = isDirectlyLinkedToAnchor(point, anchorAtomIdSet) ? 0.18 : 0;
|
|
238
|
+
const titleOverlapScore = computeJaccard(queryTokens, titleTokens) * 0.08;
|
|
239
|
+
const evidenceOverlapScore = computeJaccard(queryTokens, evidenceTokens) * 0.05;
|
|
240
|
+
const compareBonus = intent === 'compare' ? 0.04 : 0;
|
|
241
|
+
const temporalPenalty = point.temporalValidity?.isValid === false ? 0.12 : 0;
|
|
242
|
+
const totalScore = Number((Number(point.score || 0)
|
|
243
|
+
+ directLinkScore
|
|
244
|
+
+ titleOverlapScore
|
|
245
|
+
+ evidenceOverlapScore
|
|
246
|
+
+ compareBonus
|
|
247
|
+
- temporalPenalty).toFixed(6));
|
|
248
|
+
return {
|
|
249
|
+
point,
|
|
250
|
+
index,
|
|
251
|
+
totalScore,
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
ranked.sort((left, right) => (right.totalScore - left.totalScore
|
|
255
|
+
|| left.index - right.index));
|
|
256
|
+
return ranked
|
|
257
|
+
.slice(0, budget.maxSupportNodes)
|
|
258
|
+
.map((entry) => entry.point);
|
|
259
|
+
}
|
|
260
|
+
function buildBaseGraphContext(knowledgePoints) {
|
|
261
|
+
const anchorPoint = knowledgePoints[0];
|
|
262
|
+
if (!anchorPoint) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const relationPath = knowledgePoints.flatMap((point) => (Array.isArray(point.relationPath)
|
|
266
|
+
? point.relationPath
|
|
267
|
+
: []));
|
|
268
|
+
const relationKinds = Array.from(new Set(relationPath
|
|
269
|
+
.map((edge) => edge.relationKind)
|
|
270
|
+
.filter(Boolean)));
|
|
271
|
+
const relationSummaryMap = new Map();
|
|
272
|
+
const anchorAtomId = String(anchorPoint.atomId || '').trim();
|
|
273
|
+
const atomToKnowledgePoint = new Map();
|
|
274
|
+
const knowledgePointAtomIds = new Set(knowledgePoints
|
|
275
|
+
.flatMap((point) => pointAtomIds(point)));
|
|
276
|
+
knowledgePoints.forEach((point) => {
|
|
277
|
+
const pointTitle = normalizeWhitespace(String(point.title || '').trim());
|
|
278
|
+
pointAtomIds(point).forEach((atomId) => {
|
|
279
|
+
atomToKnowledgePoint.set(atomId, {
|
|
280
|
+
pointAtomId: String(point.atomId || '').trim(),
|
|
281
|
+
title: pointTitle || atomId,
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
const knowledgePointRelationMap = new Map();
|
|
286
|
+
relationPath.forEach((edge) => {
|
|
287
|
+
if (!edge || !edge.relationKind) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const edgeId = String(edge.edgeId || edge.id || '').trim();
|
|
291
|
+
const summary = relationSummaryMap.get(edge.relationKind) || {
|
|
292
|
+
edgeIds: new Set(),
|
|
293
|
+
sourceAtomIds: new Set(),
|
|
294
|
+
targetAtomIds: new Set(),
|
|
295
|
+
confidenceValues: [],
|
|
296
|
+
};
|
|
297
|
+
if (edgeId) {
|
|
298
|
+
summary.edgeIds.add(edgeId);
|
|
299
|
+
}
|
|
300
|
+
const sourceAtomId = String(edge.sourceAtomId || '').trim();
|
|
301
|
+
const targetAtomId = String(edge.targetAtomId || '').trim();
|
|
302
|
+
if (sourceAtomId) {
|
|
303
|
+
summary.sourceAtomIds.add(sourceAtomId);
|
|
304
|
+
}
|
|
305
|
+
const targetAtomIds = [
|
|
306
|
+
sourceAtomId,
|
|
307
|
+
targetAtomId,
|
|
308
|
+
]
|
|
309
|
+
.filter(Boolean)
|
|
310
|
+
.filter((atomId) => atomId !== anchorAtomId && !knowledgePointAtomIds.has(atomId));
|
|
311
|
+
targetAtomIds.forEach((atomId) => summary.targetAtomIds.add(atomId));
|
|
312
|
+
if (Number.isFinite(Number(edge.confidence))) {
|
|
313
|
+
summary.confidenceValues.push(Number(edge.confidence));
|
|
314
|
+
}
|
|
315
|
+
if (sourceAtomId && targetAtomId) {
|
|
316
|
+
const sourcePoint = atomToKnowledgePoint.get(sourceAtomId);
|
|
317
|
+
const targetPoint = atomToKnowledgePoint.get(targetAtomId);
|
|
318
|
+
if (sourcePoint
|
|
319
|
+
&& targetPoint
|
|
320
|
+
&& sourcePoint.pointAtomId
|
|
321
|
+
&& targetPoint.pointAtomId
|
|
322
|
+
&& sourcePoint.pointAtomId !== targetPoint.pointAtomId) {
|
|
323
|
+
const relationKey = [
|
|
324
|
+
edgeId,
|
|
325
|
+
sourcePoint.pointAtomId,
|
|
326
|
+
targetPoint.pointAtomId,
|
|
327
|
+
edge.relationKind,
|
|
328
|
+
].join('|');
|
|
329
|
+
knowledgePointRelationMap.set(relationKey, {
|
|
330
|
+
edgeId,
|
|
331
|
+
relationKind: edge.relationKind,
|
|
332
|
+
sourceAtomId: sourcePoint.pointAtomId,
|
|
333
|
+
sourceTitle: sourcePoint.title,
|
|
334
|
+
targetAtomId: targetPoint.pointAtomId,
|
|
335
|
+
targetTitle: targetPoint.title,
|
|
336
|
+
confidence: Number(Number(edge.confidence || 0).toFixed(4)),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
relationSummaryMap.set(edge.relationKind, summary);
|
|
341
|
+
});
|
|
342
|
+
const relationSummaries = Array.from(relationSummaryMap.entries()).map(([relationKind, summary]) => ({
|
|
343
|
+
relationKind,
|
|
344
|
+
edgeIds: Array.from(summary.edgeIds.values()),
|
|
345
|
+
sourceAtomIds: Array.from(summary.sourceAtomIds.values()),
|
|
346
|
+
targetAtomIds: Array.from(summary.targetAtomIds.values()),
|
|
347
|
+
averageConfidence: summary.confidenceValues.length > 0
|
|
348
|
+
? Number((summary.confidenceValues.reduce((sum, value) => sum + value, 0) / summary.confidenceValues.length).toFixed(4))
|
|
349
|
+
: 0,
|
|
350
|
+
}));
|
|
351
|
+
const supportingAtomIds = Array.from(new Set(knowledgePoints.flatMap((point) => (Array.isArray(point.relationPathAtomIds)
|
|
352
|
+
? point.relationPathAtomIds
|
|
353
|
+
: []))
|
|
354
|
+
.map((atomId) => String(atomId || '').trim())
|
|
355
|
+
.filter(Boolean)
|
|
356
|
+
.concat(relationSummaries.flatMap((summary) => summary.targetAtomIds))));
|
|
357
|
+
const supportingAtomIdSet = new Set(supportingAtomIds);
|
|
358
|
+
const supportingTitles = knowledgePoints
|
|
359
|
+
.filter((point, index) => index > 0 || supportingAtomIdSet.has(String(point.atomId || '').trim()))
|
|
360
|
+
.map((point) => normalizeWhitespace(String(point.title || '').trim()))
|
|
361
|
+
.filter(Boolean);
|
|
362
|
+
const temporalCheckedAt = knowledgePoints
|
|
363
|
+
.map((point) => String(point.temporalValidity && point.temporalValidity.checkedAt || '').trim())
|
|
364
|
+
.filter(Boolean)
|
|
365
|
+
.sort()
|
|
366
|
+
.pop() || '';
|
|
367
|
+
const invalidKnowledgePoints = knowledgePoints.filter((point) => point.temporalValidity && point.temporalValidity.isValid === false);
|
|
368
|
+
const temporalDetails = knowledgePoints.flatMap((point) => (Array.isArray(point.temporalValidity && point.temporalValidity.details)
|
|
369
|
+
? point.temporalValidity.details
|
|
370
|
+
: []));
|
|
371
|
+
const warningReasons = Array.from(new Set(invalidKnowledgePoints.flatMap((point) => (Array.isArray(point.temporalValidity && point.temporalValidity.reasons)
|
|
372
|
+
? point.temporalValidity.reasons
|
|
373
|
+
: []))
|
|
374
|
+
.map((reason) => String(reason || '').trim())
|
|
375
|
+
.filter(Boolean)));
|
|
376
|
+
const normalizedTemporalDetails = temporalDetails
|
|
377
|
+
.filter((detail) => Boolean(detail && typeof detail === 'object'));
|
|
378
|
+
const temporalEdgeKinds = Array.from(new Set(normalizedTemporalDetails
|
|
379
|
+
.map((detail) => detail.edgeKind)
|
|
380
|
+
.filter(Boolean)));
|
|
381
|
+
const dedupedTemporalDetails = Array.from(new Map(normalizedTemporalDetails.map((detail) => [
|
|
382
|
+
[
|
|
383
|
+
String(detail.edgeId || '').trim(),
|
|
384
|
+
String(detail.edgeKind || '').trim(),
|
|
385
|
+
String(detail.sourceAtomId || '').trim(),
|
|
386
|
+
String(detail.targetAtomId || '').trim(),
|
|
387
|
+
String(detail.validFrom || '').trim(),
|
|
388
|
+
String(detail.validTo || '').trim(),
|
|
389
|
+
].join('|'),
|
|
390
|
+
{
|
|
391
|
+
edgeId: String(detail.edgeId || '').trim(),
|
|
392
|
+
edgeKind: detail.edgeKind,
|
|
393
|
+
sourceAtomId: String(detail.sourceAtomId || '').trim(),
|
|
394
|
+
targetAtomId: String(detail.targetAtomId || '').trim(),
|
|
395
|
+
validFrom: String(detail.validFrom || '').trim(),
|
|
396
|
+
validTo: detail.validTo ? String(detail.validTo).trim() : undefined,
|
|
397
|
+
isActive: detail.isActive !== false,
|
|
398
|
+
},
|
|
399
|
+
])).values());
|
|
400
|
+
const temporalValidity = {
|
|
401
|
+
checkedAt: temporalCheckedAt,
|
|
402
|
+
allPointsValid: invalidKnowledgePoints.length <= 0,
|
|
403
|
+
warningReasons,
|
|
404
|
+
invalidKnowledgePointTitles: invalidKnowledgePoints
|
|
405
|
+
.map((point) => normalizeWhitespace(String(point.title || '').trim()))
|
|
406
|
+
.filter(Boolean),
|
|
407
|
+
edgeKinds: temporalEdgeKinds,
|
|
408
|
+
details: dedupedTemporalDetails,
|
|
409
|
+
};
|
|
410
|
+
return {
|
|
411
|
+
anchorAtomId,
|
|
412
|
+
anchorTitle: normalizeWhitespace(String(anchorPoint.title || '').trim()) || anchorAtomId,
|
|
413
|
+
anchorDocumentId: anchorPoint.documentId,
|
|
414
|
+
supportingAtomIds: Array.from(new Set(supportingAtomIds)),
|
|
415
|
+
supportingTitles: Array.from(new Set(supportingTitles)),
|
|
416
|
+
relationKinds,
|
|
417
|
+
relationSummaries,
|
|
418
|
+
knowledgePointRelations: Array.from(knowledgePointRelationMap.values()),
|
|
419
|
+
temporalValidity,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
async function resolveNodeTitle(opsStore, atomId, cache) {
|
|
423
|
+
const normalizedAtomId = String(atomId || '').trim();
|
|
424
|
+
if (!normalizedAtomId) {
|
|
425
|
+
return '';
|
|
426
|
+
}
|
|
427
|
+
if (cache.has(normalizedAtomId)) {
|
|
428
|
+
return String(cache.get(normalizedAtomId) || '').trim();
|
|
429
|
+
}
|
|
430
|
+
const atom = await opsStore.getNode(normalizedAtomId);
|
|
431
|
+
const resolvedTitle = normalizeWhitespace(String(atom?.title || '').trim()) || normalizedAtomId;
|
|
432
|
+
cache.set(normalizedAtomId, resolvedTitle);
|
|
433
|
+
return resolvedTitle;
|
|
434
|
+
}
|
|
435
|
+
async function resolveNodeMetrics(opsStore, atomId, cache) {
|
|
436
|
+
const normalizedAtomId = String(atomId || '').trim();
|
|
437
|
+
if (!normalizedAtomId) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
if (cache.has(normalizedAtomId)) {
|
|
441
|
+
return cache.get(normalizedAtomId) || null;
|
|
442
|
+
}
|
|
443
|
+
const atom = await opsStore.getNode(normalizedAtomId);
|
|
444
|
+
if (!atom) {
|
|
445
|
+
cache.set(normalizedAtomId, null);
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
const graphNode = atom;
|
|
449
|
+
const metrics = {
|
|
450
|
+
inDegree: Number.isFinite(Number(graphNode.inDegree)) ? Number(graphNode.inDegree) : undefined,
|
|
451
|
+
outDegree: Number.isFinite(Number(graphNode.outDegree)) ? Number(graphNode.outDegree) : undefined,
|
|
452
|
+
centrality: Number.isFinite(Number(graphNode.centrality)) ? Number(Number(graphNode.centrality).toFixed(4)) : undefined,
|
|
453
|
+
};
|
|
454
|
+
cache.set(normalizedAtomId, metrics);
|
|
455
|
+
return metrics;
|
|
456
|
+
}
|
|
457
|
+
async function buildAnchorGraphProfile(opsStore, anchorPoint, titleCache, metricsCache, incomingEdges, outgoingEdges, exclusion, useCompleteNeighborhoodDegree) {
|
|
458
|
+
const anchorAtomId = String(anchorPoint.atomId || '').trim();
|
|
459
|
+
if (!anchorAtomId) {
|
|
460
|
+
return undefined;
|
|
461
|
+
}
|
|
462
|
+
const title = normalizeWhitespace(String(anchorPoint.title || '').trim())
|
|
463
|
+
|| await resolveNodeTitle(opsStore, anchorAtomId, titleCache)
|
|
464
|
+
|| anchorAtomId;
|
|
465
|
+
const metrics = await resolveNodeMetrics(opsStore, anchorAtomId, metricsCache);
|
|
466
|
+
const derivedInDegree = useCompleteNeighborhoodDegree
|
|
467
|
+
? await countUsableAdjacentNodes(opsStore, incomingEdges, 'predecessor', exclusion, titleCache)
|
|
468
|
+
: undefined;
|
|
469
|
+
const derivedOutDegree = useCompleteNeighborhoodDegree
|
|
470
|
+
? await countUsableAdjacentNodes(opsStore, outgoingEdges, 'successor', exclusion, titleCache)
|
|
471
|
+
: undefined;
|
|
472
|
+
return {
|
|
473
|
+
atomId: anchorAtomId,
|
|
474
|
+
title,
|
|
475
|
+
inDegree: Number.isFinite(Number(derivedInDegree)) ? derivedInDegree : metrics?.inDegree,
|
|
476
|
+
outDegree: Number.isFinite(Number(derivedOutDegree)) ? derivedOutDegree : metrics?.outDegree,
|
|
477
|
+
centrality: metrics?.centrality,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function rankRelationEdges(edges) {
|
|
481
|
+
return edges
|
|
482
|
+
.slice()
|
|
483
|
+
.sort((left, right) => (Number(right.confidence || 0) - Number(left.confidence || 0)
|
|
484
|
+
|| (left.provenance === 'fact' ? -1 : 1)));
|
|
485
|
+
}
|
|
486
|
+
function resolveGraphWindowScoringPolicy(intent) {
|
|
487
|
+
const structuralPriorities = {
|
|
488
|
+
prerequisite: 90,
|
|
489
|
+
sequence: 82,
|
|
490
|
+
application: 74,
|
|
491
|
+
causal: 68,
|
|
492
|
+
contrast: 58,
|
|
493
|
+
analogy: 52,
|
|
494
|
+
reference: 18,
|
|
495
|
+
};
|
|
496
|
+
if (intent === 'compare') {
|
|
497
|
+
return {
|
|
498
|
+
relationPriorities: {
|
|
499
|
+
contrast: 104,
|
|
500
|
+
analogy: 92,
|
|
501
|
+
causal: 70,
|
|
502
|
+
application: 64,
|
|
503
|
+
sequence: 54,
|
|
504
|
+
prerequisite: 48,
|
|
505
|
+
reference: 18,
|
|
506
|
+
},
|
|
507
|
+
defaultRelationPriority: 0,
|
|
508
|
+
intentAlignedRelationKinds: new Set(['contrast', 'analogy']),
|
|
509
|
+
confidenceWeight: 10,
|
|
510
|
+
factProvenanceBonus: 2,
|
|
511
|
+
bibliographyPenalty: 1000,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
if (intent === 'how_to') {
|
|
515
|
+
return {
|
|
516
|
+
relationPriorities: {
|
|
517
|
+
prerequisite: 104,
|
|
518
|
+
sequence: 96,
|
|
519
|
+
application: 82,
|
|
520
|
+
causal: 70,
|
|
521
|
+
contrast: 44,
|
|
522
|
+
analogy: 38,
|
|
523
|
+
reference: 18,
|
|
524
|
+
},
|
|
525
|
+
defaultRelationPriority: 0,
|
|
526
|
+
intentAlignedRelationKinds: new Set(['prerequisite', 'sequence', 'application']),
|
|
527
|
+
confidenceWeight: 10,
|
|
528
|
+
factProvenanceBonus: 2,
|
|
529
|
+
bibliographyPenalty: 1000,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
if (intent === 'explain') {
|
|
533
|
+
return {
|
|
534
|
+
relationPriorities: {
|
|
535
|
+
prerequisite: 94,
|
|
536
|
+
causal: 88,
|
|
537
|
+
sequence: 76,
|
|
538
|
+
application: 70,
|
|
539
|
+
analogy: 58,
|
|
540
|
+
contrast: 54,
|
|
541
|
+
reference: 18,
|
|
542
|
+
},
|
|
543
|
+
defaultRelationPriority: 0,
|
|
544
|
+
intentAlignedRelationKinds: new Set(['prerequisite', 'causal', 'application', 'sequence']),
|
|
545
|
+
confidenceWeight: 10,
|
|
546
|
+
factProvenanceBonus: 2,
|
|
547
|
+
bibliographyPenalty: 1000,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
relationPriorities: structuralPriorities,
|
|
552
|
+
defaultRelationPriority: 0,
|
|
553
|
+
intentAlignedRelationKinds: new Set(),
|
|
554
|
+
confidenceWeight: 10,
|
|
555
|
+
factProvenanceBonus: 2,
|
|
556
|
+
bibliographyPenalty: 1000,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function isBibliographyLikeGraphWindowTitle(title) {
|
|
560
|
+
const normalized = normalizeGraphComparableTitle(title);
|
|
561
|
+
if (!normalized) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
return normalized === 'reference'
|
|
565
|
+
|| normalized === 'references'
|
|
566
|
+
|| normalized === 'bibliography'
|
|
567
|
+
|| normalized === 'works cited'
|
|
568
|
+
|| normalized === 'citations'
|
|
569
|
+
|| normalized.includes('参考文献')
|
|
570
|
+
|| normalized.includes('参考资料')
|
|
571
|
+
|| normalized.includes('引用文献')
|
|
572
|
+
|| normalized.includes('文献引用');
|
|
573
|
+
}
|
|
574
|
+
function scoreGraphWindowCandidate(edge, title, scoringPolicy) {
|
|
575
|
+
const confidenceScore = Math.max(0, Math.min(1, Number(edge.confidence || 0))) * scoringPolicy.confidenceWeight;
|
|
576
|
+
const provenanceScore = edge.provenance === 'fact' ? scoringPolicy.factProvenanceBonus : 0;
|
|
577
|
+
const bibliographyPenalty = isBibliographyLikeGraphWindowTitle(title) ? scoringPolicy.bibliographyPenalty : 0;
|
|
578
|
+
const relationPriority = edge.relationKind
|
|
579
|
+
? scoringPolicy.relationPriorities[edge.relationKind] ?? scoringPolicy.defaultRelationPriority
|
|
580
|
+
: scoringPolicy.defaultRelationPriority;
|
|
581
|
+
return relationPriority + confidenceScore + provenanceScore - bibliographyPenalty;
|
|
582
|
+
}
|
|
583
|
+
async function buildWindowNodes(opsStore, edges, direction, titleCache, metricsCache, exclusion, limit, missingIds, scoringPolicy) {
|
|
584
|
+
const rankedEdges = rankRelationEdges(edges);
|
|
585
|
+
const candidates = [];
|
|
586
|
+
const seenNodeKeys = new Set();
|
|
587
|
+
for (const edge of rankedEdges) {
|
|
588
|
+
const relatedAtomId = direction === 'predecessor'
|
|
589
|
+
? String(edge.sourceAtomId || '').trim()
|
|
590
|
+
: String(edge.targetAtomId || '').trim();
|
|
591
|
+
if (!relatedAtomId || exclusion.atomIds.has(relatedAtomId)) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
const title = await resolveNodeTitle(opsStore, relatedAtomId, titleCache);
|
|
595
|
+
if (!title) {
|
|
596
|
+
missingIds.add(relatedAtomId);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (isAnchorEquivalentNode(relatedAtomId, title, exclusion)) {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
if (isBibliographyLikeGraphWindowTitle(title)) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
const nodeKey = relatedAtomId + '|' + normalizeGraphComparableTitle(title);
|
|
606
|
+
if (seenNodeKeys.has(nodeKey)) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
seenNodeKeys.add(nodeKey);
|
|
610
|
+
const metrics = await resolveNodeMetrics(opsStore, relatedAtomId, metricsCache);
|
|
611
|
+
const confidence = Number(Number(edge.confidence || 0).toFixed(4));
|
|
612
|
+
const intentAligned = scoringPolicy.intentAlignedRelationKinds.size <= 0
|
|
613
|
+
|| Boolean(edge.relationKind && scoringPolicy.intentAlignedRelationKinds.has(edge.relationKind));
|
|
614
|
+
candidates.push({
|
|
615
|
+
score: scoreGraphWindowCandidate(edge, title, scoringPolicy),
|
|
616
|
+
confidence,
|
|
617
|
+
intentAligned,
|
|
618
|
+
node: {
|
|
619
|
+
atomId: relatedAtomId,
|
|
620
|
+
title,
|
|
621
|
+
relationKind: edge.relationKind,
|
|
622
|
+
confidence,
|
|
623
|
+
inDegree: metrics?.inDegree,
|
|
624
|
+
outDegree: metrics?.outDegree,
|
|
625
|
+
centrality: metrics?.centrality,
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
const alignedCandidateCount = candidates.filter((candidate) => candidate.intentAligned).length;
|
|
630
|
+
const misalignedCandidateCount = candidates.length - alignedCandidateCount;
|
|
631
|
+
const candidatePool = alignedCandidateCount > 0
|
|
632
|
+
? candidates.filter((candidate) => candidate.intentAligned)
|
|
633
|
+
: candidates;
|
|
634
|
+
return {
|
|
635
|
+
nodes: candidatePool
|
|
636
|
+
.sort((left, right) => (right.score - left.score
|
|
637
|
+
|| right.confidence - left.confidence
|
|
638
|
+
|| left.node.title.localeCompare(right.node.title)))
|
|
639
|
+
.slice(0, limit)
|
|
640
|
+
.map((candidate) => candidate.node),
|
|
641
|
+
alignedCandidateCount,
|
|
642
|
+
misalignedCandidateCount,
|
|
643
|
+
usedMisalignedFallback: alignedCandidateCount <= 0 && misalignedCandidateCount > 0,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async function buildConnectionPaths(opsStore, anchorPoint, supportPoints, intent, budget, titleCache, missingSourceAtomIds) {
|
|
647
|
+
const anchorAtomId = String(anchorPoint.atomId || '').trim();
|
|
648
|
+
if (!anchorAtomId) {
|
|
649
|
+
return [];
|
|
650
|
+
}
|
|
651
|
+
const connectionPaths = [];
|
|
652
|
+
for (const point of supportPoints.slice(0, budget.maxConnectionPaths)) {
|
|
653
|
+
let pathResult = null;
|
|
654
|
+
let resolvedSourceAtomId = '';
|
|
655
|
+
for (const sourceAtomId of pointAtomIds(point)) {
|
|
656
|
+
if (!sourceAtomId || sourceAtomId === anchorAtomId) {
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const nextPathResult = await opsStore.findPath(sourceAtomId, anchorAtomId, budget.maxPathDepth);
|
|
660
|
+
if (nextPathResult.found && Array.isArray(nextPathResult.path) && nextPathResult.path.length > 1) {
|
|
661
|
+
pathResult = nextPathResult;
|
|
662
|
+
resolvedSourceAtomId = sourceAtomId;
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (!pathResult || !resolvedSourceAtomId) {
|
|
667
|
+
missingSourceAtomIds.add(String(point.atomId || '').trim());
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
const pathAtomIds = pathResult.path
|
|
671
|
+
.map((atomId) => String(atomId || '').trim())
|
|
672
|
+
.filter(Boolean);
|
|
673
|
+
const pathTitles = [];
|
|
674
|
+
for (const atomId of pathAtomIds) {
|
|
675
|
+
pathTitles.push(await resolveNodeTitle(opsStore, atomId, titleCache));
|
|
676
|
+
}
|
|
677
|
+
connectionPaths.push({
|
|
678
|
+
sourceAtomId: resolvedSourceAtomId,
|
|
679
|
+
sourceTitle: normalizeWhitespace(String(point.title || '').trim()) || resolvedSourceAtomId,
|
|
680
|
+
targetAtomId: anchorAtomId,
|
|
681
|
+
targetTitle: normalizeWhitespace(String(anchorPoint.title || '').trim()) || anchorAtomId,
|
|
682
|
+
pathAtomIds,
|
|
683
|
+
pathTitles,
|
|
684
|
+
pathEdges: Array.isArray(pathResult.edges)
|
|
685
|
+
? pathResult.edges.map((edge) => ({
|
|
686
|
+
fromAtomId: String(edge.from || '').trim(),
|
|
687
|
+
toAtomId: String(edge.to || '').trim(),
|
|
688
|
+
relationKind: edge.relation,
|
|
689
|
+
}))
|
|
690
|
+
: [],
|
|
691
|
+
length: Math.max(0, Math.floor(Number(pathResult.length || 0))),
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
return rankConnectionPathsForIntent(connectionPaths, intent);
|
|
695
|
+
}
|
|
696
|
+
function scoreConnectionPathForIntent(path, intent) {
|
|
697
|
+
const scoringPolicy = resolveGraphWindowScoringPolicy(intent);
|
|
698
|
+
const relationScores = (Array.isArray(path.pathEdges) ? path.pathEdges : [])
|
|
699
|
+
.map((edge) => (edge.relationKind
|
|
700
|
+
? scoringPolicy.relationPriorities[edge.relationKind] ?? scoringPolicy.defaultRelationPriority
|
|
701
|
+
: scoringPolicy.defaultRelationPriority));
|
|
702
|
+
const bestRelationScore = relationScores.length > 0 ? Math.max(...relationScores) : 0;
|
|
703
|
+
const averageRelationScore = relationScores.length > 0
|
|
704
|
+
? relationScores.reduce((sum, score) => sum + score, 0) / relationScores.length
|
|
705
|
+
: 0;
|
|
706
|
+
const shorterPathScore = Math.max(0, 8 - Math.max(0, Number(path.length || 0))) * 0.01;
|
|
707
|
+
return Number((bestRelationScore + averageRelationScore * 0.001 + shorterPathScore).toFixed(6));
|
|
708
|
+
}
|
|
709
|
+
function rankConnectionPathsForIntent(connectionPaths, intent) {
|
|
710
|
+
return connectionPaths
|
|
711
|
+
.map((path, index) => ({
|
|
712
|
+
path,
|
|
713
|
+
index,
|
|
714
|
+
score: scoreConnectionPathForIntent(path, intent),
|
|
715
|
+
}))
|
|
716
|
+
.sort((left, right) => (right.score - left.score
|
|
717
|
+
|| left.path.length - right.path.length
|
|
718
|
+
|| left.index - right.index))
|
|
719
|
+
.map((entry) => entry.path);
|
|
720
|
+
}
|
|
721
|
+
function buildEvidenceSourceRefs(points) {
|
|
722
|
+
const refs = new Set();
|
|
723
|
+
const appendCitation = (citation) => {
|
|
724
|
+
const sourcePath = normalizeWhitespace(String(citation?.sourcePath || '').trim());
|
|
725
|
+
if (!sourcePath) {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const startLine = Number(citation?.startLine);
|
|
729
|
+
refs.add(Number.isFinite(startLine) && startLine > 0 ? `${sourcePath}:${startLine}` : sourcePath);
|
|
730
|
+
};
|
|
731
|
+
points.forEach((point) => {
|
|
732
|
+
appendCitation(point.citation || null);
|
|
733
|
+
if (Array.isArray(point.citations)) {
|
|
734
|
+
point.citations.forEach((citation) => appendCitation(citation));
|
|
735
|
+
}
|
|
736
|
+
if (Array.isArray(point.matchedSpans)) {
|
|
737
|
+
point.matchedSpans.forEach((span) => {
|
|
738
|
+
const sourcePath = normalizeWhitespace(String(span.sourcePath || '').trim());
|
|
739
|
+
if (!sourcePath) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
const startLine = Number(span.startLine);
|
|
743
|
+
refs.add(Number.isFinite(startLine) && startLine > 0 ? `${sourcePath}:${startLine}` : sourcePath);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
return Array.from(refs.values()).slice(0, 6);
|
|
748
|
+
}
|
|
749
|
+
function buildDiagnostics(graphOpsAvailable, anchorReason, candidateCount, supportNodeCount, budget, missingConnectionPathSourceAtomIds, missingPredecessorAtomIds, missingSuccessorAtomIds, predecessorWindowResult, successorWindowResult) {
|
|
750
|
+
return {
|
|
751
|
+
graphOpsAvailable,
|
|
752
|
+
usedFallback: !graphOpsAvailable,
|
|
753
|
+
selectedAnchorReason: anchorReason,
|
|
754
|
+
candidateCount,
|
|
755
|
+
supportNodeCount,
|
|
756
|
+
supportNodeLimit: budget.maxSupportNodes,
|
|
757
|
+
pathDepthLimit: budget.maxPathDepth,
|
|
758
|
+
intentAlignedPredecessorCandidateCount: predecessorWindowResult?.alignedCandidateCount ?? 0,
|
|
759
|
+
intentAlignedSuccessorCandidateCount: successorWindowResult?.alignedCandidateCount ?? 0,
|
|
760
|
+
intentMisalignedPredecessorCandidateCount: predecessorWindowResult?.misalignedCandidateCount ?? 0,
|
|
761
|
+
intentMisalignedSuccessorCandidateCount: successorWindowResult?.misalignedCandidateCount ?? 0,
|
|
762
|
+
usedIntentMisalignedPredecessorFallback: predecessorWindowResult?.usedMisalignedFallback === true,
|
|
763
|
+
usedIntentMisalignedSuccessorFallback: successorWindowResult?.usedMisalignedFallback === true,
|
|
764
|
+
missingConnectionPathSourceAtomIds: Array.from(missingConnectionPathSourceAtomIds.values()),
|
|
765
|
+
missingPredecessorAtomIds: Array.from(missingPredecessorAtomIds.values()),
|
|
766
|
+
missingSuccessorAtomIds: Array.from(missingSuccessorAtomIds.values()),
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
async function assembleAgentConversationGraphContext(params) {
|
|
770
|
+
const knowledgePoints = Array.isArray(params.knowledgePoints)
|
|
771
|
+
? params.knowledgePoints.filter((point) => Boolean(point && typeof point === 'object'))
|
|
772
|
+
: [];
|
|
773
|
+
if (knowledgePoints.length <= 0) {
|
|
774
|
+
return {
|
|
775
|
+
knowledgePoints: [],
|
|
776
|
+
graphContext: null,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const intent = classifyConversationIntent(params.message);
|
|
780
|
+
const budget = resolveBudget(intent, params.budget);
|
|
781
|
+
const graphWindowScoringPolicy = resolveGraphWindowScoringPolicy(intent);
|
|
782
|
+
const anchorSelection = selectAnchorPoint(params.message, knowledgePoints);
|
|
783
|
+
const anchorPoint = anchorSelection.point;
|
|
784
|
+
const supportPoints = rankSupportPoints(params.message, intent, anchorPoint, knowledgePoints, budget);
|
|
785
|
+
const supportAtomIdSet = new Set(supportPoints.map((point) => String(point.atomId || '').trim()).filter(Boolean));
|
|
786
|
+
const orderedKnowledgePoints = [
|
|
787
|
+
anchorPoint,
|
|
788
|
+
...knowledgePoints.filter((point) => supportAtomIdSet.has(String(point.atomId || '').trim()) && point !== anchorPoint),
|
|
789
|
+
...knowledgePoints.filter((point) => point !== anchorPoint && !supportAtomIdSet.has(String(point.atomId || '').trim())),
|
|
790
|
+
];
|
|
791
|
+
const contextPoints = orderedKnowledgePoints.slice(0, 1 + budget.maxSupportNodes);
|
|
792
|
+
const baseGraphContext = buildBaseGraphContext(contextPoints);
|
|
793
|
+
if (!baseGraphContext) {
|
|
794
|
+
return {
|
|
795
|
+
knowledgePoints: orderedKnowledgePoints,
|
|
796
|
+
graphContext: null,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
const evidenceSourceRefs = buildEvidenceSourceRefs(contextPoints);
|
|
800
|
+
if (!params.store || !(0, store_1.isOpsAdapter)(params.store)) {
|
|
801
|
+
return {
|
|
802
|
+
knowledgePoints: orderedKnowledgePoints,
|
|
803
|
+
graphContext: {
|
|
804
|
+
...baseGraphContext,
|
|
805
|
+
evidenceSourceRefs,
|
|
806
|
+
diagnostics: buildDiagnostics(false, anchorSelection.reason, knowledgePoints.length, supportPoints.length, budget, new Set(), new Set(), new Set()),
|
|
807
|
+
},
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
const opsStore = params.store;
|
|
811
|
+
const titleCache = new Map();
|
|
812
|
+
const metricsCache = new Map();
|
|
813
|
+
orderedKnowledgePoints.forEach((point) => {
|
|
814
|
+
pointAtomIds(point).forEach((atomId) => {
|
|
815
|
+
titleCache.set(atomId, normalizeWhitespace(String(point.title || '').trim()) || atomId);
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
const missingConnectionPathSourceAtomIds = new Set();
|
|
819
|
+
const missingPredecessorAtomIds = new Set();
|
|
820
|
+
const missingSuccessorAtomIds = new Set();
|
|
821
|
+
const connectionPaths = await buildConnectionPaths(opsStore, anchorPoint, supportPoints, intent, budget, titleCache, missingConnectionPathSourceAtomIds);
|
|
822
|
+
const anchorExclusion = buildAnchorNodeExclusion(anchorPoint, baseGraphContext);
|
|
823
|
+
const capabilities = opsStore.getCapabilities();
|
|
824
|
+
const useCompleteNeighborhoodDegree = capabilities.serverSideQuery !== true;
|
|
825
|
+
const predecessorEdgeLimit = Math.max(budget.maxPredecessors * 8, 24);
|
|
826
|
+
const successorEdgeLimit = Math.max(budget.maxSuccessors * 8, 24);
|
|
827
|
+
const anchorAtomIds = pointAtomIds(anchorPoint);
|
|
828
|
+
const predecessorEdges = useCompleteNeighborhoodDegree || budget.maxPredecessors > 0
|
|
829
|
+
? await queryAdjacentEdgesForAnchorAtoms(opsStore, anchorAtomIds, 'predecessor', useCompleteNeighborhoodDegree ? undefined : predecessorEdgeLimit)
|
|
830
|
+
: [];
|
|
831
|
+
const successorEdges = useCompleteNeighborhoodDegree || budget.maxSuccessors > 0
|
|
832
|
+
? await queryAdjacentEdgesForAnchorAtoms(opsStore, anchorAtomIds, 'successor', useCompleteNeighborhoodDegree ? undefined : successorEdgeLimit)
|
|
833
|
+
: [];
|
|
834
|
+
const emptyPredecessorWindowResult = {
|
|
835
|
+
nodes: [],
|
|
836
|
+
alignedCandidateCount: 0,
|
|
837
|
+
misalignedCandidateCount: 0,
|
|
838
|
+
usedMisalignedFallback: false,
|
|
839
|
+
};
|
|
840
|
+
const emptySuccessorWindowResult = {
|
|
841
|
+
nodes: [],
|
|
842
|
+
alignedCandidateCount: 0,
|
|
843
|
+
misalignedCandidateCount: 0,
|
|
844
|
+
usedMisalignedFallback: false,
|
|
845
|
+
};
|
|
846
|
+
const predecessorWindowResult = budget.maxPredecessors > 0
|
|
847
|
+
? await buildWindowNodes(opsStore, predecessorEdges, 'predecessor', titleCache, metricsCache, anchorExclusion, budget.maxPredecessors, missingPredecessorAtomIds, graphWindowScoringPolicy)
|
|
848
|
+
: emptyPredecessorWindowResult;
|
|
849
|
+
const successorWindowResult = budget.maxSuccessors > 0
|
|
850
|
+
? await buildWindowNodes(opsStore, successorEdges, 'successor', titleCache, metricsCache, anchorExclusion, budget.maxSuccessors, missingSuccessorAtomIds, graphWindowScoringPolicy)
|
|
851
|
+
: emptySuccessorWindowResult;
|
|
852
|
+
const predecessorWindow = predecessorWindowResult.nodes;
|
|
853
|
+
const successorWindow = successorWindowResult.nodes;
|
|
854
|
+
const anchorGraphProfile = await buildAnchorGraphProfile(opsStore, anchorPoint, titleCache, metricsCache, predecessorEdges, successorEdges, anchorExclusion, useCompleteNeighborhoodDegree);
|
|
855
|
+
return {
|
|
856
|
+
knowledgePoints: orderedKnowledgePoints,
|
|
857
|
+
graphContext: {
|
|
858
|
+
...baseGraphContext,
|
|
859
|
+
anchorGraphProfile,
|
|
860
|
+
connectionPaths,
|
|
861
|
+
predecessorWindow,
|
|
862
|
+
successorWindow,
|
|
863
|
+
evidenceSourceRefs,
|
|
864
|
+
diagnostics: buildDiagnostics(true, anchorSelection.reason, knowledgePoints.length, supportPoints.length, budget, missingConnectionPathSourceAtomIds, missingPredecessorAtomIds, missingSuccessorAtomIds, predecessorWindowResult, successorWindowResult),
|
|
865
|
+
},
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
function buildAgentConversationGraphContextFromKnowledgePoints(knowledgePoints) {
|
|
869
|
+
return buildBaseGraphContext(knowledgePoints);
|
|
870
|
+
}
|