noteconnection 1.8.0 → 1.9.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/README.md +257 -1803
- package/dist/src/agent_workspace.contract.parity.test.js +105 -421
- package/dist/src/agent_workspace.frontend.test.js +410 -11
- package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
- package/dist/src/appimage.patchelf.test.js +113 -0
- package/dist/src/appimage.portability.test.js +156 -0
- package/dist/src/backend/FileLoader.js +18 -4
- package/dist/src/backend/FileLoader.test.js +125 -0
- package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
- package/dist/src/backend/GraphBuilder.js +66 -18
- package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
- package/dist/src/backend/GraphMetrics.js +0 -3
- package/dist/src/backend/ResourceIdentity.js +155 -0
- package/dist/src/backend/ResourceIdentity.test.js +113 -0
- package/dist/src/backend/algorithms/CycleDetection.js +0 -1
- package/dist/src/backend/algorithms/HybridEngine.js +0 -3
- package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
- package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
- package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
- package/dist/src/backend/build_dag.js +0 -1
- package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
- package/dist/src/capacitor.runtime.contract.test.js +11 -2
- package/dist/src/core/Graph.js +138 -42
- package/dist/src/core/Graph.test.js +74 -0
- package/dist/src/core/NoteConnection.js +2 -2
- package/dist/src/core/PathBridge.js +265 -2
- package/dist/src/core/ResourceReference.js +17 -0
- package/dist/src/desktop.godot.pack.test.js +142 -0
- package/dist/src/desktop.runtime.graph.test.js +66 -0
- package/dist/src/export/WorkspaceExportBundle.js +44 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
- package/dist/src/foundation.release.evidence.contract.test.js +127 -9
- package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
- package/dist/src/frontend/README.md +257 -1803
- package/dist/src/frontend/agent_workspace.js +665 -7
- package/dist/src/frontend/index.html +31 -2
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +32 -0
- package/dist/src/frontend/locales/zh.json +32 -0
- package/dist/src/frontend/mobile_budget_runtime.js +33 -0
- package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
- package/dist/src/frontend/mobile_identity_contract.js +221 -0
- package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
- package/dist/src/frontend/path.html +6 -2
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +110 -4
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +107 -13
- package/dist/src/frontend/workspace_panes.js +111 -7
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
- package/dist/src/graph.answer.operator.contract.test.js +72 -0
- package/dist/src/index.js +6 -2
- package/dist/src/index.knowledge-root.test.js +118 -0
- package/dist/src/indexing/IndexLifecycle.js +22 -0
- package/dist/src/indexing/IndexLifecycle.test.js +24 -0
- package/dist/src/knowledge.api.contract.test.js +4 -2
- package/dist/src/knowledge_projection.contract.test.js +134 -0
- package/dist/src/knowledge_projection_store.test.js +200 -0
- package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
- package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
- package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
- package/dist/src/learning/PublicAnswerContract.test.js +85 -0
- package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
- package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
- package/dist/src/learning/agentConversationExecution.js +211 -0
- package/dist/src/learning/agentConversationExecution.test.js +209 -0
- package/dist/src/learning/agentConversationSerialization.js +266 -0
- package/dist/src/learning/agentConversationSerialization.test.js +165 -0
- package/dist/src/learning/agentResponseBudget.js +179 -0
- package/dist/src/learning/agentResponseBudget.test.js +97 -0
- package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
- package/dist/src/learning/answerReleaseReview.js +1191 -250
- package/dist/src/learning/answerReleaseReview.test.js +1293 -7
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/conversationComposer.js +627 -386
- package/dist/src/learning/conversationComposer.test.js +1403 -0
- package/dist/src/learning/evidenceContextAssembler.js +312 -120
- package/dist/src/learning/graphAnswerCoverage.js +37 -0
- package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
- package/dist/src/learning/graphAnswerFacts.js +94 -0
- package/dist/src/learning/graphAnswerFacts.test.js +39 -0
- package/dist/src/learning/graphAnswerPlan.js +908 -0
- package/dist/src/learning/graphAnswerPlan.test.js +907 -0
- package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
- package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
- package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
- package/dist/src/learning/graphClaimMatcher.js +104 -0
- package/dist/src/learning/graphConditionedContext.js +109 -0
- package/dist/src/learning/graphConditionedContext.test.js +99 -0
- package/dist/src/learning/graphContextAssembler.js +3 -13
- package/dist/src/learning/graphContextAssembler.test.js +2 -2
- package/dist/src/learning/graphExpansionPolicy.js +22 -0
- package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
- package/dist/src/learning/identityMetadata.test.js +26 -0
- package/dist/src/learning/index.js +1 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +34 -10
- package/dist/src/learning/queryBackend.test.js +21 -0
- package/dist/src/learning/ragContextPack.js +115 -16
- package/dist/src/learning/ragContextPack.test.js +151 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +67 -4
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +11 -1
- package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
- package/dist/src/learning/requestNormalization.js +106 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/store.js +116 -12
- package/dist/src/learning/store.test.js +299 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
- package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/license.policy.contract.test.js +9 -4
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +28 -7
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/index.js +2 -1
- package/dist/src/mobile.android.evidence.contract.test.js +146 -0
- package/dist/src/mobile.android.signing.contract.test.js +66 -0
- package/dist/src/mobile.artifact.contract.test.js +144 -0
- package/dist/src/mobile.budget.contract.test.js +90 -0
- package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
- package/dist/src/mobile.graph.parity.contract.test.js +207 -0
- package/dist/src/mobile.identity.contract.test.js +66 -0
- package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
- package/dist/src/mobile.identity.corpus.test.js +40 -0
- package/dist/src/mobile.native.recovery.contract.test.js +64 -0
- package/dist/src/mobile.pipeline.test.js +11 -1
- package/dist/src/mobile.profile.contract.test.js +225 -0
- package/dist/src/mobile.semantic.parity.test.js +92 -0
- package/dist/src/mobile_exact_analyzer.test.js +113 -0
- package/dist/src/notemd/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/NotemdService.js +12 -17
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd.server.integration.test.js +119 -4
- package/dist/src/notemd.workflow.pipeline.test.js +1 -1
- package/dist/src/pathbridge.handshake.contract.test.js +29 -0
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/platform/ExportProfile.js +28 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +14 -0
- package/dist/src/platform/PlatformCapabilities.test.js +6 -0
- package/dist/src/query_backend.external_http.integration.test.js +2 -2
- package/dist/src/release.godot.mirror.contract.test.js +28 -4
- package/dist/src/resources/ResourceRegistry.js +40 -0
- package/dist/src/resources/ResourceRegistry.test.js +36 -0
- package/dist/src/routes/index.js +0 -4
- package/dist/src/routes/knowledge.js +46 -33
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +8 -0
- package/dist/src/routes/notemd.js +990 -234
- package/dist/src/routes/registry.contract.test.js +5 -2
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +14 -51
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/server.js +566 -1090
- package/dist/src/server.migration.test.js +417 -15
- package/dist/src/sidecar.build.lock.contract.test.js +201 -0
- package/dist/src/sidecar.freshness.contract.test.js +184 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
- package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
- package/dist/src/simulation.worker.offline.test.js +104 -0
- package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
- package/dist/src/storage.provider.contract.test.js +152 -1
- package/dist/src/tauri.frontend.build.contract.test.js +8 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +6 -0
- package/dist/src/utils/RuntimePaths.test.js +27 -0
- package/dist/src/windows.installer.build.test.js +109 -0
- package/dist/src/windows.installer.payload.test.js +76 -0
- package/dist/src/windows.installer.runtime.test.js +69 -0
- package/dist/src/workspace/WorkspaceRegistry.js +13 -0
- package/package.json +32 -10
- package/dist/src/routes/data.js +0 -267
- package/dist/src/routes/diagnostics.js +0 -51
|
@@ -5,6 +5,8 @@ exports.normalizeRagContextBudget = normalizeRagContextBudget;
|
|
|
5
5
|
exports.estimateRagTokenCount = estimateRagTokenCount;
|
|
6
6
|
exports.buildRagContextPack = buildRagContextPack;
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
|
+
const graphClaimMatcher_1 = require("./graphClaimMatcher");
|
|
9
|
+
const ragPublicText_1 = require("./ragPublicText");
|
|
8
10
|
exports.DEFAULT_RAG_CONTEXT_BUDGET = {
|
|
9
11
|
maxFragments: 16,
|
|
10
12
|
maxCharsPerFragment: 1600,
|
|
@@ -19,6 +21,9 @@ const ROLE_PRIORITY = {
|
|
|
19
21
|
background: 5,
|
|
20
22
|
};
|
|
21
23
|
function normalizeRagContextBudget(input) {
|
|
24
|
+
const productCapDisabled = input?.productCapDisabled === true;
|
|
25
|
+
const maxFragmentsLimit = productCapDisabled ? 4096 : 512;
|
|
26
|
+
const maxTotalCharsLimit = productCapDisabled ? 512000 : 512000;
|
|
22
27
|
const numberOrDefault = (value, fallback, min, max) => {
|
|
23
28
|
const numeric = Number(value);
|
|
24
29
|
if (!Number.isFinite(numeric)) {
|
|
@@ -27,9 +32,15 @@ function normalizeRagContextBudget(input) {
|
|
|
27
32
|
return Math.floor(Math.max(min, Math.min(max, numeric)));
|
|
28
33
|
};
|
|
29
34
|
return {
|
|
30
|
-
maxFragments: numberOrDefault(input?.maxFragments, exports.DEFAULT_RAG_CONTEXT_BUDGET.maxFragments, 1,
|
|
35
|
+
maxFragments: numberOrDefault(input?.maxFragments, exports.DEFAULT_RAG_CONTEXT_BUDGET.maxFragments, 1, maxFragmentsLimit),
|
|
31
36
|
maxCharsPerFragment: numberOrDefault(input?.maxCharsPerFragment, exports.DEFAULT_RAG_CONTEXT_BUDGET.maxCharsPerFragment, 80, 20000),
|
|
32
|
-
maxTotalChars: numberOrDefault(input?.maxTotalChars, exports.DEFAULT_RAG_CONTEXT_BUDGET.maxTotalChars, 120,
|
|
37
|
+
maxTotalChars: numberOrDefault(input?.maxTotalChars, exports.DEFAULT_RAG_CONTEXT_BUDGET.maxTotalChars, 120, maxTotalCharsLimit),
|
|
38
|
+
...(productCapDisabled ? {
|
|
39
|
+
productCapDisabled: true,
|
|
40
|
+
runtimeMaxFragments: numberOrDefault(input?.runtimeMaxFragments, input?.maxFragments ?? exports.DEFAULT_RAG_CONTEXT_BUDGET.maxFragments, 1, 4096),
|
|
41
|
+
runtimeMaxCharsPerFragment: numberOrDefault(input?.runtimeMaxCharsPerFragment, input?.maxCharsPerFragment ?? exports.DEFAULT_RAG_CONTEXT_BUDGET.maxCharsPerFragment, 80, 20000),
|
|
42
|
+
runtimeMaxTotalChars: numberOrDefault(input?.runtimeMaxTotalChars, input?.maxTotalChars ?? exports.DEFAULT_RAG_CONTEXT_BUDGET.maxTotalChars, 120, 64 * 1024 * 1024),
|
|
43
|
+
} : {}),
|
|
33
44
|
};
|
|
34
45
|
}
|
|
35
46
|
function estimateRagTokenCount(text) {
|
|
@@ -39,7 +50,75 @@ function estimateRagTokenCount(text) {
|
|
|
39
50
|
}
|
|
40
51
|
return Math.max(1, Math.ceil(normalized.length / 4));
|
|
41
52
|
}
|
|
42
|
-
|
|
53
|
+
const QUERY_WINDOW_STOPWORDS = new Set([
|
|
54
|
+
'and', 'compare', 'contrast', 'difference', 'the', 'versus', 'with',
|
|
55
|
+
]);
|
|
56
|
+
function queryWindowTerms(query) {
|
|
57
|
+
return Array.from(new Set((String(query || '').toLowerCase().match(/[a-z0-9][a-z0-9_-]{2,}|[\u3400-\u9fff]{2,}/gu) || [])
|
|
58
|
+
.filter((term) => !QUERY_WINDOW_STOPWORDS.has(term))));
|
|
59
|
+
}
|
|
60
|
+
function queryTermCoverage(text, terms) {
|
|
61
|
+
const normalized = text.toLowerCase();
|
|
62
|
+
return terms.reduce((count, term) => count + (normalized.includes(term) ? 1 : 0), 0);
|
|
63
|
+
}
|
|
64
|
+
function queryFeatureCoverage(text, queryFeatures) {
|
|
65
|
+
return (0, graphClaimMatcher_1.semanticFeatures)(text).reduce((count, feature) => count + (queryFeatures.has(feature) ? 1 : 0), 0);
|
|
66
|
+
}
|
|
67
|
+
function queryWindowAnchors(source, terms, queryFeatures) {
|
|
68
|
+
const anchors = new Set();
|
|
69
|
+
const normalizedSource = source.toLowerCase();
|
|
70
|
+
terms.forEach((term) => {
|
|
71
|
+
let matchIndex = normalizedSource.indexOf(term);
|
|
72
|
+
while (matchIndex >= 0) {
|
|
73
|
+
anchors.add(matchIndex);
|
|
74
|
+
matchIndex = normalizedSource.indexOf(term, matchIndex + term.length);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const clausePattern = /[^\n.!?。!?;;]+(?:[.!?。!?;;]+|$)/gu;
|
|
78
|
+
let clauseMatch;
|
|
79
|
+
while ((clauseMatch = clausePattern.exec(source)) !== null) {
|
|
80
|
+
if (queryFeatureCoverage(clauseMatch[0], queryFeatures) > 0) {
|
|
81
|
+
anchors.add(clauseMatch.index + Math.floor(clauseMatch[0].length / 2));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return Array.from(anchors).sort((left, right) => left - right);
|
|
85
|
+
}
|
|
86
|
+
function queryCenteredWindow(source, maxChars, query, baseline) {
|
|
87
|
+
const terms = queryWindowTerms(query);
|
|
88
|
+
const queryFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(query));
|
|
89
|
+
if (queryFeatures.size < 2) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const visibleSource = (0, ragPublicText_1.omitFencedMarkdownPayload)(source);
|
|
93
|
+
const markerLength = '\n[...]\n'.length;
|
|
94
|
+
const windowLength = Math.max(16, maxChars - markerLength * 2);
|
|
95
|
+
const visibleBaseline = (0, ragPublicText_1.omitFencedMarkdownPayload)(baseline);
|
|
96
|
+
const baselineFeatureCoverage = queryFeatureCoverage(visibleBaseline, queryFeatures);
|
|
97
|
+
const baselineTermCoverage = queryTermCoverage(visibleBaseline, terms);
|
|
98
|
+
let selected = null;
|
|
99
|
+
for (const anchor of queryWindowAnchors(visibleSource, terms, queryFeatures)) {
|
|
100
|
+
const start = Math.max(0, Math.min(visibleSource.length - windowLength, anchor - Math.floor(windowLength * 0.35)));
|
|
101
|
+
const candidate = visibleSource.slice(start, start + windowLength);
|
|
102
|
+
const featureCoverage = queryFeatureCoverage(candidate, queryFeatures);
|
|
103
|
+
const termCoverage = queryTermCoverage(candidate, terms);
|
|
104
|
+
if (!selected
|
|
105
|
+
|| featureCoverage > selected.featureCoverage
|
|
106
|
+
|| (featureCoverage === selected.featureCoverage
|
|
107
|
+
&& termCoverage > selected.termCoverage)) {
|
|
108
|
+
selected = { start, text: candidate, featureCoverage, termCoverage };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!selected
|
|
112
|
+
|| selected.featureCoverage < baselineFeatureCoverage
|
|
113
|
+
|| (selected.featureCoverage === baselineFeatureCoverage
|
|
114
|
+
&& selected.termCoverage <= baselineTermCoverage)) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const prefix = selected.start > 0 ? '[...]\n' : '';
|
|
118
|
+
const suffix = selected.start + selected.text.length < visibleSource.length ? '\n[...]' : '';
|
|
119
|
+
return `${prefix}${selected.text.trim()}${suffix}`;
|
|
120
|
+
}
|
|
121
|
+
function truncateMiddle(text, maxChars, query) {
|
|
43
122
|
const source = String(text || '');
|
|
44
123
|
if (source.length <= maxChars) {
|
|
45
124
|
return { text: source, truncated: false };
|
|
@@ -51,13 +130,14 @@ function truncateMiddle(text, maxChars) {
|
|
|
51
130
|
const available = Math.max(0, maxChars - marker.length);
|
|
52
131
|
const headLength = Math.ceil(available * 0.6);
|
|
53
132
|
const tailLength = Math.floor(available * 0.4);
|
|
133
|
+
const baseline = `${source.slice(0, headLength).trimEnd()}${marker}${source.slice(source.length - tailLength).trimStart()}`;
|
|
54
134
|
return {
|
|
55
|
-
text:
|
|
135
|
+
text: queryCenteredWindow(source, maxChars, query, baseline) || baseline,
|
|
56
136
|
truncated: true,
|
|
57
137
|
};
|
|
58
138
|
}
|
|
59
|
-
function withBudgetedText(fragment, maxChars, reason) {
|
|
60
|
-
const truncated = truncateMiddle(fragment.text, maxChars);
|
|
139
|
+
function withBudgetedText(fragment, maxChars, reason, query) {
|
|
140
|
+
const truncated = truncateMiddle(fragment.text, maxChars, query);
|
|
61
141
|
const text = truncated.text;
|
|
62
142
|
return {
|
|
63
143
|
...fragment,
|
|
@@ -71,7 +151,8 @@ function withBudgetedText(fragment, maxChars, reason) {
|
|
|
71
151
|
function rolePriority(fragment) {
|
|
72
152
|
return ROLE_PRIORITY[fragment.role] ?? ROLE_PRIORITY.background;
|
|
73
153
|
}
|
|
74
|
-
function sortFragmentsForBudget(fragments) {
|
|
154
|
+
function sortFragmentsForBudget(fragments, fragmentOrder) {
|
|
155
|
+
const order = new Map((fragmentOrder || []).map((fragmentId, index) => [String(fragmentId), index]));
|
|
75
156
|
return fragments
|
|
76
157
|
.map((fragment, index) => ({ fragment, index }))
|
|
77
158
|
.sort((a, b) => {
|
|
@@ -83,33 +164,48 @@ function sortFragmentsForBudget(fragments) {
|
|
|
83
164
|
if (Math.abs(scoreDelta) > 0.0001) {
|
|
84
165
|
return scoreDelta;
|
|
85
166
|
}
|
|
167
|
+
const conditionedOrderDelta = (order.get(a.fragment.fragmentId) ?? Number.MAX_SAFE_INTEGER)
|
|
168
|
+
- (order.get(b.fragment.fragmentId) ?? Number.MAX_SAFE_INTEGER);
|
|
169
|
+
if (conditionedOrderDelta !== 0) {
|
|
170
|
+
return conditionedOrderDelta;
|
|
171
|
+
}
|
|
86
172
|
return a.index - b.index;
|
|
87
173
|
})
|
|
88
174
|
.map((entry) => entry.fragment);
|
|
89
175
|
}
|
|
90
|
-
function applyContextBudget(fragments, budget, decisions) {
|
|
176
|
+
function applyContextBudget(fragments, budget, decisions, query, fragmentOrder) {
|
|
91
177
|
const selected = [];
|
|
92
178
|
let usedChars = 0;
|
|
93
|
-
|
|
94
|
-
|
|
179
|
+
const productCapDisabled = budget.productCapDisabled === true;
|
|
180
|
+
const maxFragments = productCapDisabled
|
|
181
|
+
? budget.runtimeMaxFragments || budget.maxFragments
|
|
182
|
+
: budget.maxFragments;
|
|
183
|
+
const maxCharsPerFragment = productCapDisabled
|
|
184
|
+
? budget.runtimeMaxCharsPerFragment || budget.maxCharsPerFragment
|
|
185
|
+
: budget.maxCharsPerFragment;
|
|
186
|
+
const maxTotalChars = productCapDisabled
|
|
187
|
+
? budget.runtimeMaxTotalChars || budget.maxTotalChars
|
|
188
|
+
: budget.maxTotalChars;
|
|
189
|
+
sortFragmentsForBudget(fragments, fragmentOrder).forEach((fragment) => {
|
|
190
|
+
if (selected.length >= maxFragments) {
|
|
95
191
|
decisions.push({
|
|
96
192
|
documentId: fragment.documentId,
|
|
97
193
|
sourcePath: fragment.sourcePath,
|
|
98
194
|
sourceBoundary: fragment.sourceBoundary,
|
|
99
195
|
status: 'fragment_dropped',
|
|
100
|
-
reason: 'max_fragments_exceeded',
|
|
196
|
+
reason: productCapDisabled ? 'runtime_fragment_limit_exceeded' : 'max_fragments_exceeded',
|
|
101
197
|
});
|
|
102
198
|
return;
|
|
103
199
|
}
|
|
104
|
-
let candidate = withBudgetedText(fragment,
|
|
105
|
-
const remainingChars =
|
|
200
|
+
let candidate = withBudgetedText(fragment, maxCharsPerFragment, productCapDisabled ? 'runtime_max_chars_per_fragment_exceeded' : 'max_chars_per_fragment_exceeded', query);
|
|
201
|
+
const remainingChars = maxTotalChars - usedChars;
|
|
106
202
|
if (remainingChars <= 0) {
|
|
107
203
|
decisions.push({
|
|
108
204
|
documentId: fragment.documentId,
|
|
109
205
|
sourcePath: fragment.sourcePath,
|
|
110
206
|
sourceBoundary: fragment.sourceBoundary,
|
|
111
207
|
status: 'fragment_dropped',
|
|
112
|
-
reason: 'max_total_chars_exceeded',
|
|
208
|
+
reason: productCapDisabled ? 'runtime_total_chars_exceeded' : 'max_total_chars_exceeded',
|
|
113
209
|
});
|
|
114
210
|
return;
|
|
115
211
|
}
|
|
@@ -124,7 +220,7 @@ function applyContextBudget(fragments, budget, decisions) {
|
|
|
124
220
|
});
|
|
125
221
|
return;
|
|
126
222
|
}
|
|
127
|
-
candidate = withBudgetedText(candidate, remainingChars, 'max_total_chars_exceeded');
|
|
223
|
+
candidate = withBudgetedText(candidate, remainingChars, productCapDisabled ? 'runtime_total_chars_exceeded' : 'max_total_chars_exceeded', query);
|
|
128
224
|
}
|
|
129
225
|
selected.push(candidate);
|
|
130
226
|
usedChars += candidate.charCount;
|
|
@@ -216,6 +312,7 @@ function buildRagContextReplayId(params) {
|
|
|
216
312
|
left.status.localeCompare(right.status),
|
|
217
313
|
left.reason.localeCompare(right.reason),
|
|
218
314
|
].find((delta) => delta !== 0) || 0),
|
|
315
|
+
graphConditioning: params.graphConditioning || null,
|
|
219
316
|
};
|
|
220
317
|
const digest = (0, crypto_1.createHash)('sha256')
|
|
221
318
|
.update(JSON.stringify(payload))
|
|
@@ -229,7 +326,7 @@ function buildRagContextPack(params) {
|
|
|
229
326
|
? params.sourceDecisions.map((decision) => ({ ...decision }))
|
|
230
327
|
: [];
|
|
231
328
|
const fragments = Array.isArray(params.fragments) ? params.fragments.filter(Boolean) : [];
|
|
232
|
-
const selectedFragments = applyContextBudget(fragments, budget, decisions);
|
|
329
|
+
const selectedFragments = applyContextBudget(fragments, budget, decisions, params.query, params.fragmentOrder);
|
|
233
330
|
annotateReadDecisions(decisions, selectedFragments);
|
|
234
331
|
const totalCharCount = selectedFragments.reduce((sum, fragment) => sum + fragment.charCount, 0);
|
|
235
332
|
const tokenEstimate = selectedFragments.reduce((sum, fragment) => sum + fragment.tokenEstimate, 0);
|
|
@@ -244,6 +341,7 @@ function buildRagContextPack(params) {
|
|
|
244
341
|
sourceDecisions: decisions,
|
|
245
342
|
totalCharCount,
|
|
246
343
|
tokenEstimate,
|
|
344
|
+
graphConditioning: params.graphConditioning,
|
|
247
345
|
}),
|
|
248
346
|
query,
|
|
249
347
|
generatedAt: String(params.generatedAt || new Date().toISOString()),
|
|
@@ -253,5 +351,6 @@ function buildRagContextPack(params) {
|
|
|
253
351
|
sourceDecisions: decisions,
|
|
254
352
|
totalCharCount,
|
|
255
353
|
tokenEstimate,
|
|
354
|
+
graphConditioning: params.graphConditioning,
|
|
256
355
|
};
|
|
257
356
|
}
|
|
@@ -117,6 +117,61 @@ describe('buildRagContextPack', () => {
|
|
|
117
117
|
}),
|
|
118
118
|
]));
|
|
119
119
|
});
|
|
120
|
+
test('centers the bounded window on query terms when a later comparison clause would otherwise be dropped', () => {
|
|
121
|
+
const longText = [
|
|
122
|
+
'Opening definition: a water glass is a transparent vessel.',
|
|
123
|
+
'Background context '.repeat(28),
|
|
124
|
+
'Comparison: a plastic cup is lightweight and less rigid than a water glass.',
|
|
125
|
+
'Terminal qualifier: the comparison remains limited to the scoped note.',
|
|
126
|
+
].join('\n');
|
|
127
|
+
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
128
|
+
query: 'compare water glass and plastic cup',
|
|
129
|
+
fragments: [
|
|
130
|
+
makeFragment({
|
|
131
|
+
role: 'parent_context',
|
|
132
|
+
text: longText,
|
|
133
|
+
sourceBoundary: 'full_document',
|
|
134
|
+
}),
|
|
135
|
+
],
|
|
136
|
+
sourceDecisions: [],
|
|
137
|
+
budget: {
|
|
138
|
+
maxFragments: 3,
|
|
139
|
+
maxCharsPerFragment: 180,
|
|
140
|
+
maxTotalChars: 220,
|
|
141
|
+
},
|
|
142
|
+
generatedAt: '2026-07-05T00:00:00.000Z',
|
|
143
|
+
});
|
|
144
|
+
expect(pack.fragments[0].text).toContain('plastic cup');
|
|
145
|
+
expect(pack.fragments[0].text).toContain('water glass');
|
|
146
|
+
});
|
|
147
|
+
test('prefers multilingual comparison prose outside fenced renderer payloads', () => {
|
|
148
|
+
const longText = [
|
|
149
|
+
'Opening definition: a water glass is a transparent vessel.',
|
|
150
|
+
'Background context '.repeat(20),
|
|
151
|
+
'```mermaid',
|
|
152
|
+
'graph LR',
|
|
153
|
+
'A[Water Glass] --> B[Plastic Cup PET]',
|
|
154
|
+
'```',
|
|
155
|
+
'相同尺寸下,PET塑料杯比玻璃水杯更轻、更有韧性,但刚度和透明度较低。',
|
|
156
|
+
'Terminal qualifier: the comparison remains limited to the scoped note.',
|
|
157
|
+
].join('\n');
|
|
158
|
+
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
159
|
+
query: 'compare water glass and plastic cup',
|
|
160
|
+
fragments: [makeFragment({
|
|
161
|
+
role: 'parent_context',
|
|
162
|
+
text: longText,
|
|
163
|
+
sourceBoundary: 'full_document',
|
|
164
|
+
})],
|
|
165
|
+
sourceDecisions: [],
|
|
166
|
+
budget: {
|
|
167
|
+
maxFragments: 3,
|
|
168
|
+
maxCharsPerFragment: 180,
|
|
169
|
+
maxTotalChars: 220,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
expect(pack.fragments[0].text).toContain('PET塑料杯');
|
|
173
|
+
expect(pack.fragments[0].text).not.toContain('graph LR');
|
|
174
|
+
});
|
|
120
175
|
test('uses role priority so weak background cannot displace direct or graph evidence', () => {
|
|
121
176
|
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
122
177
|
query: 'what is water glass?',
|
|
@@ -157,4 +212,100 @@ describe('buildRagContextPack', () => {
|
|
|
157
212
|
}),
|
|
158
213
|
]));
|
|
159
214
|
});
|
|
215
|
+
test('uses graph-conditioned order only as a tie-break inside the same role and score', () => {
|
|
216
|
+
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
217
|
+
query: 'what is water glass?',
|
|
218
|
+
fragments: [
|
|
219
|
+
makeFragment({ fragmentId: 'graph-unmatched', role: 'graph_neighbor_support', score: 0.6 }),
|
|
220
|
+
makeFragment({ fragmentId: 'graph-matched', role: 'graph_neighbor_support', score: 0.6 }),
|
|
221
|
+
makeFragment({ fragmentId: 'direct', role: 'direct_support', score: 0.4 }),
|
|
222
|
+
],
|
|
223
|
+
fragmentOrder: ['graph-matched', 'graph-unmatched', 'direct'],
|
|
224
|
+
sourceDecisions: [],
|
|
225
|
+
budget: {
|
|
226
|
+
maxFragments: 3,
|
|
227
|
+
maxCharsPerFragment: 200,
|
|
228
|
+
maxTotalChars: 600,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
expect(pack.fragments.map((fragment) => fragment.fragmentId)).toEqual([
|
|
232
|
+
'direct',
|
|
233
|
+
'graph-matched',
|
|
234
|
+
'graph-unmatched',
|
|
235
|
+
]);
|
|
236
|
+
});
|
|
237
|
+
test('unbounded product mode ignores product caps while retaining explicit runtime limits', () => {
|
|
238
|
+
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
239
|
+
query: 'what is water glass?',
|
|
240
|
+
fragments: Array.from({ length: 4 }, (_, index) => makeFragment({
|
|
241
|
+
fragmentId: `unbounded_${index}`,
|
|
242
|
+
role: 'parent_context',
|
|
243
|
+
text: `Section ${index}: ${'water glass evidence '.repeat(12)}`,
|
|
244
|
+
sourceBoundary: 'full_document',
|
|
245
|
+
})),
|
|
246
|
+
sourceDecisions: [],
|
|
247
|
+
budget: {
|
|
248
|
+
maxFragments: 1,
|
|
249
|
+
maxCharsPerFragment: 100,
|
|
250
|
+
maxTotalChars: 120,
|
|
251
|
+
productCapDisabled: true,
|
|
252
|
+
runtimeMaxFragments: 8,
|
|
253
|
+
runtimeMaxCharsPerFragment: 900,
|
|
254
|
+
runtimeMaxTotalChars: 10000,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
expect(pack.fragments).toHaveLength(4);
|
|
258
|
+
expect(pack.budget.productCapDisabled).toBe(true);
|
|
259
|
+
expect(pack.sourceDecisions).not.toEqual(expect.arrayContaining([
|
|
260
|
+
expect.objectContaining({ reason: 'max_fragments_exceeded' }),
|
|
261
|
+
]));
|
|
262
|
+
});
|
|
263
|
+
test('preserves the configured max tier instead of applying the legacy 200-fragment/100k cap', () => {
|
|
264
|
+
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
265
|
+
query: 'max budget probe',
|
|
266
|
+
fragments: Array.from({ length: 260 }, (_, index) => makeFragment({
|
|
267
|
+
fragmentId: `max_tier_${index}`,
|
|
268
|
+
role: 'direct_support',
|
|
269
|
+
text: `Max tier evidence ${index}.`,
|
|
270
|
+
sourceBoundary: 'full_document',
|
|
271
|
+
})),
|
|
272
|
+
sourceDecisions: [],
|
|
273
|
+
budget: {
|
|
274
|
+
maxFragments: 256,
|
|
275
|
+
maxCharsPerFragment: 16000,
|
|
276
|
+
maxTotalChars: 256000,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
expect(pack.budget.maxFragments).toBe(256);
|
|
280
|
+
expect(pack.budget.maxTotalChars).toBe(256000);
|
|
281
|
+
expect(pack.fragments).toHaveLength(256);
|
|
282
|
+
});
|
|
283
|
+
test('unbounded mode reports a runtime fragment stop instead of silently dropping content', () => {
|
|
284
|
+
const pack = (0, ragContextPack_1.buildRagContextPack)({
|
|
285
|
+
query: 'what is water glass?',
|
|
286
|
+
fragments: Array.from({ length: 4 }, (_, index) => makeFragment({
|
|
287
|
+
fragmentId: `runtime_limited_${index}`,
|
|
288
|
+
role: 'parent_context',
|
|
289
|
+
text: `Runtime section ${index}: water glass evidence.`,
|
|
290
|
+
sourceBoundary: 'full_document',
|
|
291
|
+
})),
|
|
292
|
+
sourceDecisions: [],
|
|
293
|
+
budget: {
|
|
294
|
+
maxFragments: 1,
|
|
295
|
+
maxCharsPerFragment: 100,
|
|
296
|
+
maxTotalChars: 120,
|
|
297
|
+
productCapDisabled: true,
|
|
298
|
+
runtimeMaxFragments: 2,
|
|
299
|
+
runtimeMaxCharsPerFragment: 900,
|
|
300
|
+
runtimeMaxTotalChars: 10000,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
expect(pack.fragments).toHaveLength(2);
|
|
304
|
+
expect(pack.sourceDecisions).toEqual(expect.arrayContaining([
|
|
305
|
+
expect.objectContaining({
|
|
306
|
+
status: 'fragment_dropped',
|
|
307
|
+
reason: 'runtime_fragment_limit_exceeded',
|
|
308
|
+
}),
|
|
309
|
+
]));
|
|
310
|
+
});
|
|
160
311
|
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.iterateRagEvidenceClauses = iterateRagEvidenceClauses;
|
|
4
|
+
exports.segmentRagEvidenceClauses = segmentRagEvidenceClauses;
|
|
5
|
+
exports.scoreRagEvidenceClause = scoreRagEvidenceClause;
|
|
6
|
+
function normalizeWhitespace(value) {
|
|
7
|
+
return String(value || '').replace(/\s+/gu, ' ').trim();
|
|
8
|
+
}
|
|
9
|
+
function isClauseBoundary(source, index) {
|
|
10
|
+
const character = source[index];
|
|
11
|
+
if (/\r|\n|;/u.test(character)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (character === '.' && /[\d\s=+\-(]/u.test(source[index - 1] || ' ') && /\d/u.test(source[index + 1] || '')) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return /[.!?。!?;]/u.test(character);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Splits source evidence at discourse boundaries while retaining decimal numbers and
|
|
21
|
+
* mathematical notation. The caller still owns filtering and public-text naturalization.
|
|
22
|
+
*/
|
|
23
|
+
function* iterateRagEvidenceClauses(value) {
|
|
24
|
+
const source = String(value || '');
|
|
25
|
+
if (!source) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
let start = 0;
|
|
29
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
30
|
+
if (!isClauseBoundary(source, index)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const includeBoundary = /[.!?。!?]/u.test(source[index]);
|
|
34
|
+
const clause = normalizeWhitespace(source.slice(start, includeBoundary ? index + 1 : index));
|
|
35
|
+
if (clause) {
|
|
36
|
+
yield clause;
|
|
37
|
+
}
|
|
38
|
+
start = index + 1;
|
|
39
|
+
}
|
|
40
|
+
const remainder = normalizeWhitespace(source.slice(start));
|
|
41
|
+
if (remainder) {
|
|
42
|
+
yield remainder;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function segmentRagEvidenceClauses(value) {
|
|
46
|
+
return Array.from(iterateRagEvidenceClauses(value));
|
|
47
|
+
}
|
|
48
|
+
function delimiterBalance(value) {
|
|
49
|
+
const pairs = { '(': ')', '[': ']', '{': '}', '(': ')', '[': ']', '{': '}' };
|
|
50
|
+
const closing = new Set(Object.values(pairs));
|
|
51
|
+
const stack = [];
|
|
52
|
+
for (const character of value) {
|
|
53
|
+
if (pairs[character]) {
|
|
54
|
+
stack.push(pairs[character]);
|
|
55
|
+
}
|
|
56
|
+
else if (closing.has(character) && stack.pop() !== character) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return stack.length === 0;
|
|
61
|
+
}
|
|
62
|
+
function mathDensity(value) {
|
|
63
|
+
const mathTokens = value.match(/(?:\b\d+(?:\.\d+)?\b|[=<>±×÷∑∫√]|\b(?:sin|cos|log|exp)\b)/giu) || [];
|
|
64
|
+
return Math.min(1, mathTokens.length / Math.max(6, value.length / 24));
|
|
65
|
+
}
|
|
66
|
+
function documentaryPenalty(value) {
|
|
67
|
+
return /\b(?:this (?:document|section|table)|the following|as requested|we will|must follow|ignore this)\b/iu.test(value)
|
|
68
|
+
|| /(?:本文档|下表|如下|按照您的要求|忽略本节)/u.test(value)
|
|
69
|
+
? 0.35
|
|
70
|
+
: 0;
|
|
71
|
+
}
|
|
72
|
+
function scoreRagEvidenceClause(value) {
|
|
73
|
+
const clause = normalizeWhitespace(value);
|
|
74
|
+
const hasTerminalPunctuation = /[.!?。!?]$/u.test(clause);
|
|
75
|
+
const hasBalancedDelimiters = delimiterBalance(clause);
|
|
76
|
+
const density = mathDensity(clause);
|
|
77
|
+
const penalty = documentaryPenalty(clause);
|
|
78
|
+
const lengthScore = clause.length < 8
|
|
79
|
+
? -0.3
|
|
80
|
+
: clause.length <= 420
|
|
81
|
+
? 0.25
|
|
82
|
+
: -Math.min(0.25, (clause.length - 420) / 2400);
|
|
83
|
+
const score = Math.max(0, Math.min(1, 0.35
|
|
84
|
+
+ (hasTerminalPunctuation ? 0.15 : -0.08)
|
|
85
|
+
+ (hasBalancedDelimiters ? 0.15 : -0.3)
|
|
86
|
+
+ Math.min(0.2, density * 0.2)
|
|
87
|
+
+ lengthScore
|
|
88
|
+
- penalty));
|
|
89
|
+
return {
|
|
90
|
+
score: Number(score.toFixed(4)),
|
|
91
|
+
hasTerminalPunctuation,
|
|
92
|
+
hasBalancedDelimiters,
|
|
93
|
+
mathDensity: Number(density.toFixed(4)),
|
|
94
|
+
documentaryPenalty: penalty,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ragEvidenceQuality_1 = require("./ragEvidenceQuality");
|
|
4
|
+
describe('ragEvidenceQuality', () => {
|
|
5
|
+
test('segments prose without splitting decimal mathematical values', () => {
|
|
6
|
+
expect((0, ragEvidenceQuality_1.segmentRagEvidenceClauses)('The coefficient is 0.75; heat transfer follows the wall.')).toEqual([
|
|
7
|
+
'The coefficient is 0.75',
|
|
8
|
+
'heat transfer follows the wall.',
|
|
9
|
+
]);
|
|
10
|
+
});
|
|
11
|
+
test('scores complete balanced mathematical evidence above documentary prose', () => {
|
|
12
|
+
const evidence = (0, ragEvidenceQuality_1.scoreRagEvidenceClause)('The thermal flux is q = kA(T1 - T2) / L, so thickness changes the exchange rate.');
|
|
13
|
+
const documentary = (0, ragEvidenceQuality_1.scoreRagEvidenceClause)('This document will explain the following table as requested.');
|
|
14
|
+
expect(evidence.hasBalancedDelimiters).toBe(true);
|
|
15
|
+
expect(evidence.mathDensity).toBeGreaterThan(0);
|
|
16
|
+
expect(evidence.score).toBeGreaterThan(documentary.score);
|
|
17
|
+
expect(documentary.documentaryPenalty).toBeGreaterThan(0);
|
|
18
|
+
});
|
|
19
|
+
test('preserves leading decimal measurements without joining numbered sentences', () => {
|
|
20
|
+
expect((0, ragEvidenceQuality_1.segmentRagEvidenceClauses)('The offset is -.25 ms. The duration is .5 s; 1. Next observation.')).toEqual([
|
|
21
|
+
'The offset is -.25 ms.', 'The duration is .5 s', '1.', 'Next observation.',
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
test('penalizes unbalanced mathematical fragments', () => {
|
|
25
|
+
const quality = (0, ragEvidenceQuality_1.scoreRagEvidenceClause)('The flux is q = kA(T1 - T2 / L.');
|
|
26
|
+
expect(quality.hasBalancedDelimiters).toBe(false);
|
|
27
|
+
expect(quality.score).toBeLessThan(0.5);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -1,14 +1,65 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.omitFencedMarkdownPayload = omitFencedMarkdownPayload;
|
|
4
|
+
exports.extractPublicMermaidEvidenceLabels = extractPublicMermaidEvidenceLabels;
|
|
3
5
|
exports.naturalizeRagPublicEvidenceClause = naturalizeRagPublicEvidenceClause;
|
|
6
|
+
exports.shouldRejectPublicEvidenceClause = shouldRejectPublicEvidenceClause;
|
|
4
7
|
exports.shouldRejectCompareProcedureEvidenceClause = shouldRejectCompareProcedureEvidenceClause;
|
|
8
|
+
const answerTaskPlan_1 = require("./answerTaskPlan");
|
|
5
9
|
function normalizeWhitespace(value) {
|
|
6
10
|
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
7
11
|
}
|
|
8
|
-
|
|
12
|
+
function omitFencedMarkdownPayload(value) {
|
|
13
|
+
return String(value || '')
|
|
14
|
+
.replace(/```[\s\S]*?```/gu, ' ')
|
|
15
|
+
.replace(/```[\s\S]*$/u, ' ');
|
|
16
|
+
}
|
|
17
|
+
function normalizeMermaidLabel(value) {
|
|
18
|
+
return normalizeWhitespace(String(value || '')
|
|
19
|
+
.replace(/<br\s*\/?>/giu, ' ')
|
|
20
|
+
.replace(/\\n/gu, ' ')
|
|
21
|
+
.replace(/["`*_~#|{}]/gu, ' '));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Converts diagram node labels into evidence text while keeping Mermaid syntax out of public answers.
|
|
25
|
+
*/
|
|
26
|
+
function extractPublicMermaidEvidenceLabels(value) {
|
|
27
|
+
const labels = [];
|
|
28
|
+
const append = (candidate) => {
|
|
29
|
+
const label = normalizeMermaidLabel(candidate);
|
|
30
|
+
if (label && !labels.includes(label)) {
|
|
31
|
+
labels.push(label);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const source = String(value || '');
|
|
35
|
+
const fences = source.matchAll(/```mermaid\s*([\s\S]*?)(?:```|$)/giu);
|
|
36
|
+
for (const fence of fences) {
|
|
37
|
+
const body = String(fence[1] || '');
|
|
38
|
+
body.replace(/\[([^\]]+)\]/gu, (_match, label) => {
|
|
39
|
+
append(label);
|
|
40
|
+
return '';
|
|
41
|
+
});
|
|
42
|
+
body.replace(/"([^"]+)"/gu, (_match, label) => {
|
|
43
|
+
append(label);
|
|
44
|
+
return '';
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return labels;
|
|
48
|
+
}
|
|
49
|
+
const LEADING_SOURCE_LABEL_PATTERN = /^(?:(?:prerequisites?|preconditions?|requirements?|background|context|mechanism|reasoning boundary|downstream checks?|downstream consequences?|failure modes?|mitigation(?: neighbor)?|graph caveat|caveat|evidence|summary|note|material science)\s*[:\uFF1A-]\s*|(?:核心概念(?:及其数学基础)?|材料科学|技术参数)(?:\s*[:\uFF1A-]\s*|\s+))/iu;
|
|
9
50
|
const PRESERVED_LEADING_LABEL_PATTERN = /^(?:step|phase|stage|section)\s*\d+\s*[:\uFF1A-]/iu;
|
|
10
51
|
function naturalizeRagPublicEvidenceClause(value) {
|
|
11
|
-
let normalized = normalizeWhitespace(value)
|
|
52
|
+
let normalized = normalizeWhitespace(omitFencedMarkdownPayload(value)
|
|
53
|
+
.replace(/^\s*[-+*]\s+/gmu, '')
|
|
54
|
+
.replace(/\s+\*\s+(?=\$|\*\*|[\u3400-\u9fff])/gu, ' ')
|
|
55
|
+
.replace(/\*\*([^*]+)\*\*/gu, '$1')
|
|
56
|
+
.replace(/([+\-]\/)\s+(?=\d)/gu, '$1')
|
|
57
|
+
.replace(/(\d)\.\s+(?=\d)/gu, '$1.')
|
|
58
|
+
.replace(/^(?:#{1,6})\s+/u, ''));
|
|
59
|
+
const tableStart = normalized.search(/\s\|\s*[^|]+\s*\|/u);
|
|
60
|
+
if (tableStart >= 0) {
|
|
61
|
+
normalized = normalizeWhitespace(normalized.slice(0, tableStart));
|
|
62
|
+
}
|
|
12
63
|
if (!normalized || PRESERVED_LEADING_LABEL_PATTERN.test(normalized)) {
|
|
13
64
|
return normalized;
|
|
14
65
|
}
|
|
@@ -24,15 +75,27 @@ function naturalizeRagPublicEvidenceClause(value) {
|
|
|
24
75
|
}
|
|
25
76
|
return normalized;
|
|
26
77
|
}
|
|
78
|
+
function shouldRejectPublicEvidenceClause(value) {
|
|
79
|
+
const normalized = normalizeWhitespace(value);
|
|
80
|
+
// A bare wiki link identifies a note, but supplies no factual statement for answer coverage.
|
|
81
|
+
return /^(?:(?:\d+[.)]|[-*+])?\s*\[\[[^\]]+\]\][\s,;、,;.!。]*)+$/u.test(normalized)
|
|
82
|
+
|| /\b(?:distractor|must not guide|do not use|ignore this (?:section|evidence)|must (?:compare|resolve)|before publishing)\b/iu.test(normalized)
|
|
83
|
+
|| /\b(?:this|the)\s+(?:clause|sentence|paragraph)\b[^.!?]{0,120}\bpublic\s+(?:definition|answer)\s+budget\b/iu.test(normalized)
|
|
84
|
+
|| /\b(?:the following table|the table below)\b/iu.test(normalized)
|
|
85
|
+
|| /(?:下表|以下表格|表中)(?:列出|展示|给出|包含)/u.test(normalized)
|
|
86
|
+
|| /(?:参数\s*\(Parameter\)|Parameter).{0,160}(?:单位\s*\(Unit\)|Unit).{0,160}:---/iu.test(normalized)
|
|
87
|
+
|| /(?:遵从您的指示|所有推理过程|最终输出|仅基于标题|根据您的要求生成|本技术文档旨在)/u.test(normalized);
|
|
88
|
+
}
|
|
27
89
|
function shouldRejectCompareProcedureEvidenceClause(value, query) {
|
|
28
90
|
const normalizedClause = normalizeWhitespace(value).toLowerCase();
|
|
29
91
|
const normalizedQuery = normalizeWhitespace(query).toLowerCase();
|
|
30
92
|
if (!normalizedClause) {
|
|
31
93
|
return false;
|
|
32
94
|
}
|
|
33
|
-
if (
|
|
95
|
+
if ((0, answerTaskPlan_1.isProcedureRequest)(normalizedQuery)) {
|
|
34
96
|
return false;
|
|
35
97
|
}
|
|
36
|
-
return
|
|
98
|
+
return /^(?:(?:procedure|workflow|runbook)\s*[:\uFF1A-]|(?:step\s*\d+|steps?|步骤\s*\d+)\b)/u.test(normalizedClause)
|
|
99
|
+
|| /\b(?:follow|execute|perform)\s+(?:this|the following)\s+(?:procedure|workflow|steps?)\b/u.test(normalizedClause)
|
|
37
100
|
|| /\bprocedural\b.{0,80}\bsequence\b/u.test(normalizedClause);
|
|
38
101
|
}
|