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,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ragSufficiencyJudge_1 = require("./ragSufficiencyJudge");
|
|
4
|
+
function makeFragment(overrides = {}) {
|
|
5
|
+
const text = overrides.text || 'A water glass is a transparent drinking vessel that contains water.';
|
|
6
|
+
return {
|
|
7
|
+
fragmentId: overrides.fragmentId || `fragment_${overrides.role || 'direct_support'}`,
|
|
8
|
+
role: overrides.role || 'direct_support',
|
|
9
|
+
text,
|
|
10
|
+
atomId: overrides.atomId || 'atom_water_glass',
|
|
11
|
+
documentId: overrides.documentId || 'doc_water_glass',
|
|
12
|
+
sourcePath: overrides.sourcePath || 'Knowledge_Base/test/water-glass.md',
|
|
13
|
+
title: overrides.title || 'Water Glass',
|
|
14
|
+
headingPath: Array.isArray(overrides.headingPath) ? overrides.headingPath : ['Water Glass'],
|
|
15
|
+
startOffset: overrides.startOffset,
|
|
16
|
+
endOffset: overrides.endOffset,
|
|
17
|
+
startLine: overrides.startLine,
|
|
18
|
+
endLine: overrides.endLine,
|
|
19
|
+
charCount: text.length,
|
|
20
|
+
tokenEstimate: Math.ceil(text.length / 4),
|
|
21
|
+
truncated: overrides.truncated === true,
|
|
22
|
+
truncationReason: overrides.truncationReason,
|
|
23
|
+
citationIds: Array.isArray(overrides.citationIds) ? overrides.citationIds : ['evidence_water_glass'],
|
|
24
|
+
relationEdgeIds: Array.isArray(overrides.relationEdgeIds) ? overrides.relationEdgeIds : [],
|
|
25
|
+
score: Number.isFinite(overrides.score) ? Number(overrides.score) : 0.8,
|
|
26
|
+
sourceBoundary: overrides.sourceBoundary || 'direct_span_only',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function makePack(fragments) {
|
|
30
|
+
return {
|
|
31
|
+
query: 'what is water glass?',
|
|
32
|
+
generatedAt: '2026-07-05T00:00:00.000Z',
|
|
33
|
+
sourceBoundary: fragments.some((fragment) => fragment.sourceBoundary === 'full_document')
|
|
34
|
+
? 'full_document'
|
|
35
|
+
: 'direct_span_only',
|
|
36
|
+
budget: {
|
|
37
|
+
maxFragments: 8,
|
|
38
|
+
maxCharsPerFragment: 800,
|
|
39
|
+
maxTotalChars: 2400,
|
|
40
|
+
},
|
|
41
|
+
fragments,
|
|
42
|
+
sourceDecisions: [],
|
|
43
|
+
totalCharCount: fragments.reduce((sum, fragment) => sum + fragment.charCount, 0),
|
|
44
|
+
tokenEstimate: fragments.reduce((sum, fragment) => sum + fragment.tokenEstimate, 0),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
describe('reviewRagContextSufficiency', () => {
|
|
48
|
+
test('marks direct citation plus document augmentation as sufficient without using LLM review', async () => {
|
|
49
|
+
const llmJudge = jest.fn();
|
|
50
|
+
const review = await (0, ragSufficiencyJudge_1.reviewRagContextSufficiency)({
|
|
51
|
+
query: 'what is water glass?',
|
|
52
|
+
contextPack: makePack([
|
|
53
|
+
makeFragment({
|
|
54
|
+
role: 'direct_support',
|
|
55
|
+
text: 'A water glass is a transparent drinking vessel that contains water.',
|
|
56
|
+
}),
|
|
57
|
+
makeFragment({
|
|
58
|
+
role: 'parent_context',
|
|
59
|
+
text: '## Definition\n\nA water glass is a transparent drinking vessel that contains water.\n\nThe vessel boundary and water surface define the observed optical behavior.',
|
|
60
|
+
sourceBoundary: 'full_document',
|
|
61
|
+
}),
|
|
62
|
+
]),
|
|
63
|
+
llmJudge,
|
|
64
|
+
});
|
|
65
|
+
expect(review.status).toBe('sufficient');
|
|
66
|
+
expect(review.score).toBeGreaterThanOrEqual(0.75);
|
|
67
|
+
expect(review.deterministic).toBe(true);
|
|
68
|
+
expect(review.llmJudgeUsed).toBe(false);
|
|
69
|
+
expect(llmJudge).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
test('marks missing direct support as insufficient instead of allowing fluent overreach', async () => {
|
|
72
|
+
const review = await (0, ragSufficiencyJudge_1.reviewRagContextSufficiency)({
|
|
73
|
+
query: 'what is water glass?',
|
|
74
|
+
contextPack: makePack([
|
|
75
|
+
makeFragment({
|
|
76
|
+
role: 'background',
|
|
77
|
+
text: 'Background note about liquids and containers.',
|
|
78
|
+
citationIds: [],
|
|
79
|
+
}),
|
|
80
|
+
]),
|
|
81
|
+
});
|
|
82
|
+
expect(review.status).toBe('insufficient');
|
|
83
|
+
expect(review.degradationState).toBe('insufficient_evidence');
|
|
84
|
+
expect(review.reasons).toContain('missing_direct_support');
|
|
85
|
+
});
|
|
86
|
+
test('keeps direct-span-only answers borderline so callers can recover once', async () => {
|
|
87
|
+
const review = await (0, ragSufficiencyJudge_1.reviewRagContextSufficiency)({
|
|
88
|
+
query: 'what is water glass?',
|
|
89
|
+
contextPack: makePack([
|
|
90
|
+
makeFragment({
|
|
91
|
+
role: 'direct_support',
|
|
92
|
+
text: 'A water glass is a transparent drinking vessel that contains water.',
|
|
93
|
+
}),
|
|
94
|
+
]),
|
|
95
|
+
});
|
|
96
|
+
expect(review.status).toBe('borderline');
|
|
97
|
+
expect(review.degradationState).toBe('partial_coverage');
|
|
98
|
+
expect(review.reasons).toEqual(expect.arrayContaining([
|
|
99
|
+
'document_augmentation_missing',
|
|
100
|
+
]));
|
|
101
|
+
});
|
|
102
|
+
test('degrades graph evidence when graph-neighbor source windows are unavailable', async () => {
|
|
103
|
+
const pack = makePack([
|
|
104
|
+
makeFragment({
|
|
105
|
+
role: 'direct_support',
|
|
106
|
+
text: 'A brittle glass vessel is stiff and transparent but has low impact tolerance.',
|
|
107
|
+
documentId: 'doc_brittle_glass',
|
|
108
|
+
sourcePath: 'Knowledge_Base/test/brittle-glass.md',
|
|
109
|
+
}),
|
|
110
|
+
makeFragment({
|
|
111
|
+
role: 'parent_context',
|
|
112
|
+
text: '# Brittle Glass Vessel\n\nA brittle glass vessel is stiff and transparent but has low impact tolerance.',
|
|
113
|
+
documentId: 'doc_brittle_glass',
|
|
114
|
+
sourcePath: 'Knowledge_Base/test/brittle-glass.md',
|
|
115
|
+
sourceBoundary: 'full_document',
|
|
116
|
+
}),
|
|
117
|
+
makeFragment({
|
|
118
|
+
role: 'graph_neighbor_support',
|
|
119
|
+
text: 'Missing neighbor evidence should not be treated as complete graph evidence.',
|
|
120
|
+
atomId: 'atom_missing_neighbor',
|
|
121
|
+
documentId: 'doc_missing_neighbor',
|
|
122
|
+
sourcePath: 'Knowledge_Base/test/missing-neighbor.md',
|
|
123
|
+
title: 'Missing Neighbor Evidence',
|
|
124
|
+
sourceBoundary: 'direct_span_only',
|
|
125
|
+
}),
|
|
126
|
+
]);
|
|
127
|
+
pack.sourceDecisions = [
|
|
128
|
+
{
|
|
129
|
+
documentId: 'doc_brittle_glass',
|
|
130
|
+
sourcePath: 'Knowledge_Base/test/brittle-glass.md',
|
|
131
|
+
sourceBoundary: 'full_document',
|
|
132
|
+
status: 'read',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
documentId: 'doc_missing_neighbor',
|
|
136
|
+
sourcePath: 'Knowledge_Base/test/missing-neighbor.md',
|
|
137
|
+
sourceBoundary: 'direct_span_only',
|
|
138
|
+
status: 'source_window_unavailable',
|
|
139
|
+
reason: 'source_resolver_returned_no_content:graph_neighbor_support',
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
const review = await (0, ragSufficiencyJudge_1.reviewRagContextSufficiency)({
|
|
143
|
+
query: 'compare brittle glass vessel with missing neighbor evidence',
|
|
144
|
+
contextPack: pack,
|
|
145
|
+
graphContext: {
|
|
146
|
+
anchorAtomId: 'atom_brittle_glass',
|
|
147
|
+
anchorTitle: 'Brittle Glass Vessel',
|
|
148
|
+
anchorDocumentId: 'doc_brittle_glass',
|
|
149
|
+
predecessorWindow: [],
|
|
150
|
+
successorWindow: [
|
|
151
|
+
{
|
|
152
|
+
atomId: 'atom_missing_neighbor',
|
|
153
|
+
title: 'Missing Neighbor Evidence',
|
|
154
|
+
relationKind: 'analogy',
|
|
155
|
+
confidence: 0.91,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
supportingAtomIds: ['atom_missing_neighbor'],
|
|
159
|
+
supportingTitles: ['Missing Neighbor Evidence'],
|
|
160
|
+
relationKinds: ['analogy'],
|
|
161
|
+
relationSummaries: [],
|
|
162
|
+
knowledgePointRelations: [],
|
|
163
|
+
temporalValidity: {
|
|
164
|
+
checkedAt: '2026-07-05T00:00:00.000Z',
|
|
165
|
+
allPointsValid: true,
|
|
166
|
+
warningReasons: [],
|
|
167
|
+
invalidKnowledgePointTitles: [],
|
|
168
|
+
edgeKinds: [],
|
|
169
|
+
details: [],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
expect(review.status).toBe('borderline');
|
|
174
|
+
expect(review.degradationState).toBe('partial_coverage');
|
|
175
|
+
expect(review.reasons).toContain('graph_neighbor_evidence_missing');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseRagSufficiencyProviderJudgeReview = parseRagSufficiencyProviderJudgeReview;
|
|
4
|
+
exports.createRagSufficiencyProviderJudge = createRagSufficiencyProviderJudge;
|
|
5
|
+
const LlmProvider_1 = require("../notemd/LlmProvider");
|
|
6
|
+
const RAG_SUFFICIENCY_TASK_KEY = 'ragSufficiencyJudge';
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 4000;
|
|
8
|
+
const DEFAULT_MAX_TOKENS = 320;
|
|
9
|
+
const MIN_TIMEOUT_MS = 250;
|
|
10
|
+
const MAX_TIMEOUT_MS = 20000;
|
|
11
|
+
const MIN_MAX_TOKENS = 32;
|
|
12
|
+
const MAX_MAX_TOKENS = 1200;
|
|
13
|
+
const VALID_STATUSES = new Set([
|
|
14
|
+
'sufficient',
|
|
15
|
+
'borderline',
|
|
16
|
+
'insufficient',
|
|
17
|
+
]);
|
|
18
|
+
const VALID_DEGRADATION_STATES = new Set([
|
|
19
|
+
'none',
|
|
20
|
+
'partial_coverage',
|
|
21
|
+
'conflict',
|
|
22
|
+
'stale_evidence',
|
|
23
|
+
'insufficient_evidence',
|
|
24
|
+
]);
|
|
25
|
+
const DEFAULT_REVIEW_PROMPT = [
|
|
26
|
+
'You are a bounded RAG sufficiency reviewer.',
|
|
27
|
+
'Decide whether the supplied RAG context is sufficient to answer the user query without unsupported claims.',
|
|
28
|
+
'Use only the supplied context. Do not produce the final answer.',
|
|
29
|
+
'Return only one JSON object with keys: status, score, reasons, degradationState.',
|
|
30
|
+
'status must be one of: sufficient, borderline, insufficient.',
|
|
31
|
+
'score must be a number from 0 to 1.',
|
|
32
|
+
'reasons must be a short string array using stable snake_case reason codes.',
|
|
33
|
+
'degradationState must be one of: none, partial_coverage, conflict, stale_evidence, insufficient_evidence.',
|
|
34
|
+
].join('\n');
|
|
35
|
+
function clamp(value, min, max) {
|
|
36
|
+
if (!Number.isFinite(value)) {
|
|
37
|
+
return min;
|
|
38
|
+
}
|
|
39
|
+
return Math.max(min, Math.min(max, value));
|
|
40
|
+
}
|
|
41
|
+
function clampUnit(value) {
|
|
42
|
+
return Number(clamp(value, 0, 1).toFixed(4));
|
|
43
|
+
}
|
|
44
|
+
function resolveBoundedInteger(value, fallback, min, max) {
|
|
45
|
+
const normalized = Math.floor(Number(value));
|
|
46
|
+
if (!Number.isFinite(normalized)) {
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
return Math.floor(clamp(normalized, min, max));
|
|
50
|
+
}
|
|
51
|
+
function cleanReason(value) {
|
|
52
|
+
return String(value || '')
|
|
53
|
+
.trim()
|
|
54
|
+
.replace(/\s+/g, '_')
|
|
55
|
+
.replace(/[^a-zA-Z0-9_:-]/g, '')
|
|
56
|
+
.slice(0, 96);
|
|
57
|
+
}
|
|
58
|
+
function extractJsonObjectText(text) {
|
|
59
|
+
const trimmed = String(text || '').trim();
|
|
60
|
+
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
61
|
+
const candidate = fenced ? fenced[1].trim() : trimmed;
|
|
62
|
+
if (candidate.startsWith('{') && candidate.endsWith('}')) {
|
|
63
|
+
return candidate;
|
|
64
|
+
}
|
|
65
|
+
const startIndex = candidate.indexOf('{');
|
|
66
|
+
const endIndex = candidate.lastIndexOf('}');
|
|
67
|
+
if (startIndex >= 0 && endIndex > startIndex) {
|
|
68
|
+
return candidate.slice(startIndex, endIndex + 1);
|
|
69
|
+
}
|
|
70
|
+
return candidate;
|
|
71
|
+
}
|
|
72
|
+
function toRecord(value) {
|
|
73
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
function parseRagSufficiencyProviderJudgeReview(text) {
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(extractJsonObjectText(text));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const record = toRecord(parsed);
|
|
87
|
+
if (!record) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const review = {};
|
|
91
|
+
if (VALID_STATUSES.has(record.status)) {
|
|
92
|
+
review.status = record.status;
|
|
93
|
+
}
|
|
94
|
+
if (Number.isFinite(Number(record.score))) {
|
|
95
|
+
review.score = clampUnit(Number(record.score));
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(record.reasons)) {
|
|
98
|
+
const reasons = record.reasons
|
|
99
|
+
.map(cleanReason)
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.slice(0, 8);
|
|
102
|
+
if (reasons.length > 0) {
|
|
103
|
+
review.reasons = reasons;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (VALID_DEGRADATION_STATES.has(record.degradationState)) {
|
|
107
|
+
review.degradationState = record.degradationState;
|
|
108
|
+
}
|
|
109
|
+
return Object.keys(review).length > 0 ? review : null;
|
|
110
|
+
}
|
|
111
|
+
function buildFragmentPayload(fragment) {
|
|
112
|
+
return {
|
|
113
|
+
role: fragment.role,
|
|
114
|
+
title: fragment.title,
|
|
115
|
+
documentId: fragment.documentId,
|
|
116
|
+
sourcePath: fragment.sourcePath,
|
|
117
|
+
headingPath: fragment.headingPath,
|
|
118
|
+
sourceBoundary: fragment.sourceBoundary,
|
|
119
|
+
citationIds: fragment.citationIds,
|
|
120
|
+
relationEdgeIds: fragment.relationEdgeIds || [],
|
|
121
|
+
score: fragment.score,
|
|
122
|
+
truncated: fragment.truncated,
|
|
123
|
+
truncationReason: fragment.truncationReason,
|
|
124
|
+
text: fragment.text,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function buildGraphPayload(graphContext) {
|
|
128
|
+
if (!graphContext) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
anchorAtomId: graphContext.anchorAtomId,
|
|
133
|
+
anchorTitle: graphContext.anchorTitle,
|
|
134
|
+
supportingAtomIds: graphContext.supportingAtomIds || [],
|
|
135
|
+
supportingTitles: graphContext.supportingTitles || [],
|
|
136
|
+
relationKinds: graphContext.relationKinds || [],
|
|
137
|
+
relationSummaries: graphContext.relationSummaries || [],
|
|
138
|
+
predecessorWindow: graphContext.predecessorWindow || [],
|
|
139
|
+
successorWindow: graphContext.successorWindow || [],
|
|
140
|
+
temporalValidity: graphContext.temporalValidity,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function buildReviewContent(input) {
|
|
144
|
+
const pack = input.contextPack;
|
|
145
|
+
return JSON.stringify({
|
|
146
|
+
query: input.query,
|
|
147
|
+
generatedAt: pack.generatedAt,
|
|
148
|
+
sourceBoundary: pack.sourceBoundary,
|
|
149
|
+
budget: pack.budget,
|
|
150
|
+
totalCharCount: pack.totalCharCount,
|
|
151
|
+
tokenEstimate: pack.tokenEstimate,
|
|
152
|
+
sourceDecisions: pack.sourceDecisions,
|
|
153
|
+
fragments: pack.fragments.map(buildFragmentPayload),
|
|
154
|
+
graphContext: buildGraphPayload(input.graphContext),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async function completeWithTimeout(params) {
|
|
158
|
+
const controller = new AbortController();
|
|
159
|
+
let didTimeout = false;
|
|
160
|
+
let timer = null;
|
|
161
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
162
|
+
timer = setTimeout(() => {
|
|
163
|
+
didTimeout = true;
|
|
164
|
+
controller.abort();
|
|
165
|
+
reject(new Error('RAG sufficiency judge timed out.'));
|
|
166
|
+
}, params.timeoutMs);
|
|
167
|
+
params.request.signal?.addEventListener('abort', () => {
|
|
168
|
+
if (timer) {
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
timer = null;
|
|
171
|
+
}
|
|
172
|
+
controller.abort();
|
|
173
|
+
reject(new Error('RAG sufficiency judge cancelled.'));
|
|
174
|
+
}, { once: true });
|
|
175
|
+
});
|
|
176
|
+
try {
|
|
177
|
+
return await Promise.race([
|
|
178
|
+
params.client.complete({
|
|
179
|
+
...params.request,
|
|
180
|
+
signal: controller.signal,
|
|
181
|
+
}),
|
|
182
|
+
timeoutPromise,
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (didTimeout) {
|
|
187
|
+
throw new Error('RAG sufficiency judge timed out.');
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
if (timer) {
|
|
193
|
+
clearTimeout(timer);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function createRagSufficiencyProviderJudge(options) {
|
|
198
|
+
const client = options.llmClient || new LlmProvider_1.LlmProviderClient();
|
|
199
|
+
const timeoutMs = resolveBoundedInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
200
|
+
const maxTokens = resolveBoundedInteger(options.maxTokens, DEFAULT_MAX_TOKENS, MIN_MAX_TOKENS, MAX_MAX_TOKENS);
|
|
201
|
+
return async (input) => {
|
|
202
|
+
const settings = await options.settingsProvider();
|
|
203
|
+
const provider = (0, LlmProvider_1.selectProviderForTask)(settings, RAG_SUFFICIENCY_TASK_KEY);
|
|
204
|
+
const model = (0, LlmProvider_1.selectModelForTask)(settings, RAG_SUFFICIENCY_TASK_KEY, provider);
|
|
205
|
+
const configuredPrompt = settings.enableGlobalCustomPrompts
|
|
206
|
+
? String(settings.customPrompts?.[RAG_SUFFICIENCY_TASK_KEY] || '').trim()
|
|
207
|
+
: '';
|
|
208
|
+
const result = await completeWithTimeout({
|
|
209
|
+
client,
|
|
210
|
+
timeoutMs,
|
|
211
|
+
request: {
|
|
212
|
+
provider,
|
|
213
|
+
model,
|
|
214
|
+
prompt: configuredPrompt || DEFAULT_REVIEW_PROMPT,
|
|
215
|
+
content: buildReviewContent(input),
|
|
216
|
+
maxTokens,
|
|
217
|
+
maxRetries: 0,
|
|
218
|
+
retryDelayMs: 0,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
const review = parseRagSufficiencyProviderJudgeReview(result.text);
|
|
222
|
+
if (!review) {
|
|
223
|
+
throw new Error('RAG sufficiency judge returned invalid JSON.');
|
|
224
|
+
}
|
|
225
|
+
return review;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ragSufficiencyProviderJudge_1 = require("./ragSufficiencyProviderJudge");
|
|
4
|
+
function makeSettings() {
|
|
5
|
+
return {
|
|
6
|
+
providers: [
|
|
7
|
+
{
|
|
8
|
+
name: 'OpenAI',
|
|
9
|
+
apiKey: 'test-key',
|
|
10
|
+
baseUrl: 'https://example.test/v1',
|
|
11
|
+
model: 'gpt-test',
|
|
12
|
+
temperature: 0,
|
|
13
|
+
enabled: true,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
activeProvider: 'OpenAI',
|
|
17
|
+
useMultiModelSettings: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function makeFragment(overrides = {}) {
|
|
21
|
+
const text = overrides.text || 'A water glass is a transparent drinking vessel that contains water.';
|
|
22
|
+
return {
|
|
23
|
+
fragmentId: overrides.fragmentId || 'fragment_direct',
|
|
24
|
+
role: overrides.role || 'direct_support',
|
|
25
|
+
text,
|
|
26
|
+
atomId: overrides.atomId || 'atom_water_glass',
|
|
27
|
+
documentId: overrides.documentId || 'doc_water_glass',
|
|
28
|
+
sourcePath: overrides.sourcePath || 'Knowledge_Base/waterglass/water-glass.md',
|
|
29
|
+
title: overrides.title || 'Water Glass',
|
|
30
|
+
headingPath: overrides.headingPath || ['Water Glass', 'Definition'],
|
|
31
|
+
startOffset: overrides.startOffset,
|
|
32
|
+
endOffset: overrides.endOffset,
|
|
33
|
+
startLine: overrides.startLine,
|
|
34
|
+
endLine: overrides.endLine,
|
|
35
|
+
charCount: text.length,
|
|
36
|
+
tokenEstimate: Math.ceil(text.length / 4),
|
|
37
|
+
truncated: overrides.truncated === true,
|
|
38
|
+
truncationReason: overrides.truncationReason,
|
|
39
|
+
citationIds: overrides.citationIds || ['evidence_water_glass'],
|
|
40
|
+
relationEdgeIds: overrides.relationEdgeIds || [],
|
|
41
|
+
score: overrides.score ?? 0.91,
|
|
42
|
+
sourceBoundary: overrides.sourceBoundary || 'direct_span_only',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function makePack() {
|
|
46
|
+
const fragments = [
|
|
47
|
+
makeFragment(),
|
|
48
|
+
makeFragment({
|
|
49
|
+
fragmentId: 'fragment_parent',
|
|
50
|
+
role: 'parent_context',
|
|
51
|
+
sourceBoundary: 'full_document',
|
|
52
|
+
text: '## Definition\n\nA water glass is a transparent drinking vessel that contains water.',
|
|
53
|
+
}),
|
|
54
|
+
];
|
|
55
|
+
return {
|
|
56
|
+
query: 'what is water glass?',
|
|
57
|
+
generatedAt: '2026-07-05T00:00:00.000Z',
|
|
58
|
+
sourceBoundary: 'full_document',
|
|
59
|
+
budget: {
|
|
60
|
+
maxFragments: 8,
|
|
61
|
+
maxCharsPerFragment: 800,
|
|
62
|
+
maxTotalChars: 2400,
|
|
63
|
+
},
|
|
64
|
+
fragments,
|
|
65
|
+
sourceDecisions: [],
|
|
66
|
+
totalCharCount: fragments.reduce((sum, fragment) => sum + fragment.charCount, 0),
|
|
67
|
+
tokenEstimate: fragments.reduce((sum, fragment) => sum + fragment.tokenEstimate, 0),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
describe('createRagSufficiencyProviderJudge', () => {
|
|
71
|
+
test('calls the configured NoteMD provider with bounded JSON-only review input', async () => {
|
|
72
|
+
const complete = jest.fn().mockResolvedValue({
|
|
73
|
+
text: '```json\n{"status":"sufficient","score":0.84,"reasons":["answerable_from_context"],"degradationState":"none"}\n```',
|
|
74
|
+
provider: 'OpenAI',
|
|
75
|
+
model: 'gpt-test',
|
|
76
|
+
});
|
|
77
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({
|
|
78
|
+
settingsProvider: makeSettings,
|
|
79
|
+
llmClient: { complete },
|
|
80
|
+
timeoutMs: 250,
|
|
81
|
+
maxTokens: 96,
|
|
82
|
+
});
|
|
83
|
+
const review = await judge({
|
|
84
|
+
query: 'what is water glass?',
|
|
85
|
+
contextPack: makePack(),
|
|
86
|
+
graphContext: null,
|
|
87
|
+
});
|
|
88
|
+
expect(review).toEqual(expect.objectContaining({
|
|
89
|
+
status: 'sufficient',
|
|
90
|
+
score: 0.84,
|
|
91
|
+
reasons: ['answerable_from_context'],
|
|
92
|
+
degradationState: 'none',
|
|
93
|
+
}));
|
|
94
|
+
expect(complete).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(complete).toHaveBeenCalledWith(expect.objectContaining({
|
|
96
|
+
provider: expect.objectContaining({ name: 'OpenAI' }),
|
|
97
|
+
model: 'gpt-test',
|
|
98
|
+
maxTokens: 96,
|
|
99
|
+
maxRetries: 0,
|
|
100
|
+
retryDelayMs: 0,
|
|
101
|
+
signal: expect.any(Object),
|
|
102
|
+
}));
|
|
103
|
+
expect(String(complete.mock.calls[0]?.[0]?.content || '')).toContain('"role":"direct_support"');
|
|
104
|
+
});
|
|
105
|
+
test('rejects on timeout so the deterministic reviewer can record a fallback reason', async () => {
|
|
106
|
+
const complete = jest.fn((request) => new Promise((_resolve, reject) => {
|
|
107
|
+
request.signal.addEventListener('abort', () => {
|
|
108
|
+
reject(new Error('Operation cancelled.'));
|
|
109
|
+
}, { once: true });
|
|
110
|
+
}));
|
|
111
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({
|
|
112
|
+
settingsProvider: makeSettings,
|
|
113
|
+
llmClient: { complete },
|
|
114
|
+
timeoutMs: 5,
|
|
115
|
+
maxTokens: 48,
|
|
116
|
+
});
|
|
117
|
+
await expect(judge({
|
|
118
|
+
query: 'what is water glass?',
|
|
119
|
+
contextPack: makePack(),
|
|
120
|
+
graphContext: null,
|
|
121
|
+
})).rejects.toThrow(/cancelled|timed out/i);
|
|
122
|
+
});
|
|
123
|
+
test('rejects malformed completion text so the deterministic reviewer can record a fallback reason', async () => {
|
|
124
|
+
const complete = jest.fn().mockResolvedValue({
|
|
125
|
+
text: 'not json',
|
|
126
|
+
provider: 'OpenAI',
|
|
127
|
+
model: 'gpt-test',
|
|
128
|
+
});
|
|
129
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({
|
|
130
|
+
settingsProvider: makeSettings,
|
|
131
|
+
llmClient: { complete },
|
|
132
|
+
timeoutMs: 250,
|
|
133
|
+
maxTokens: 96,
|
|
134
|
+
});
|
|
135
|
+
await expect(judge({
|
|
136
|
+
query: 'what is water glass?',
|
|
137
|
+
contextPack: makePack(),
|
|
138
|
+
graphContext: null,
|
|
139
|
+
})).rejects.toThrow(/invalid json/i);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('parseRagSufficiencyProviderJudgeReview', () => {
|
|
143
|
+
test('parses fenced JSON and discards invalid review fields', () => {
|
|
144
|
+
expect((0, ragSufficiencyProviderJudge_1.parseRagSufficiencyProviderJudgeReview)([
|
|
145
|
+
'```json',
|
|
146
|
+
'{"status":"sufficient","score":1.2,"reasons":[" complete ",""],"degradationState":"none","deterministic":true}',
|
|
147
|
+
'```',
|
|
148
|
+
].join('\n'))).toEqual({
|
|
149
|
+
status: 'sufficient',
|
|
150
|
+
score: 1,
|
|
151
|
+
reasons: ['complete'],
|
|
152
|
+
degradationState: 'none',
|
|
153
|
+
});
|
|
154
|
+
expect((0, ragSufficiencyProviderJudge_1.parseRagSufficiencyProviderJudgeReview)('not json')).toBeNull();
|
|
155
|
+
});
|
|
156
|
+
});
|