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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ragPublicText_1 = require("./ragPublicText");
|
|
4
|
+
const KnowledgeLearningPlatform_1 = require("./KnowledgeLearningPlatform");
|
|
5
|
+
describe('ragPublicText', () => {
|
|
6
|
+
test('retains numbered procedure evidence for a how-should request', () => {
|
|
7
|
+
expect((0, ragPublicText_1.shouldRejectCompareProcedureEvidenceClause)('Step 1: Validate the candidate.', 'How should the index be replaced?')).toBe(false);
|
|
8
|
+
expect((0, ragPublicText_1.shouldRejectCompareProcedureEvidenceClause)('Step 1: Validate the candidate.', 'Compare the two indexes.')).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
test.each(['[[Checkpoint]]', '[[Checkpoint]] [[Mutation log]]', '1. [[Checkpoint]] 2. [[Mutation log]]'])('rejects link-only navigation as factual evidence: %s', text => {
|
|
11
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)(text)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
test('preserves factual prose that contains a wiki link', () => {
|
|
14
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('A [[Mutation log]] records an ordered sequence of changes.')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
test('comparison composition selects source facts instead of bare wiki links', async () => {
|
|
17
|
+
const platform = new KnowledgeLearningPlatform_1.KnowledgeLearningPlatform({ autoPersist: false });
|
|
18
|
+
await platform.ingestKnowledge({ documents: [
|
|
19
|
+
{ documentId: 'checkpoint', sourcePath: 'test/checkpoint.md', content: '# Checkpoint\nA checkpoint stores the complete state at a checkpoint. [[Mutation log]]' },
|
|
20
|
+
{ documentId: 'mutation-log', sourcePath: 'test/mutation-log.md', content: '# Mutation Log\nA mutation log records an ordered sequence of state changes. [[Checkpoint]]' },
|
|
21
|
+
] });
|
|
22
|
+
const response = await platform.agentConversation({ message: 'Compare Checkpoint and Mutation Log', persistMemory: false });
|
|
23
|
+
expect(response.answer).toMatch(/complete state|ordered sequence/i);
|
|
24
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)(response.answer)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
test('removes Markdown list and bold-label scaffolding before public composition', () => {
|
|
27
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('* **Quantitative analysis**: pressure follows P = rho g h.'))
|
|
28
|
+
.toBe('Quantitative analysis: pressure follows P = rho g h.');
|
|
29
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('* $T$ is the temperature field.'))
|
|
30
|
+
.toBe('$T$ is the temperature field.');
|
|
31
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('A value * B value remains mathematical.'))
|
|
32
|
+
.toBe('A value * B value remains mathematical.');
|
|
33
|
+
});
|
|
34
|
+
test('removes markdown headings and table scaffolding while preserving the factual lead', () => {
|
|
35
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('### Technical specifications The glass density is about 2500 kg/m³. | Parameter | Value | | :--- | :--- | | density | 2500 |')).toBe('Technical specifications The glass density is about 2500 kg/m³.');
|
|
36
|
+
});
|
|
37
|
+
test('rejects authoring instructions and evidence-control prose in Chinese and English', () => {
|
|
38
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('所有推理过程以英文进行,最终输出为简体中文。')).toBe(true);
|
|
39
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('Operators must resolve the active owner before publishing.')).toBe(true);
|
|
40
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('The glass wall conducts heat into the environment.')).toBe(false);
|
|
41
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('This clause is intentionally beyond the public definition budget and should remain internal.')).toBe(true);
|
|
42
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('The response budget limits serialized bytes.')).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
test('removes fenced diagram payloads instead of exposing renderer source', () => {
|
|
45
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('Heat moves by conduction and convection. ```mermaid graph TD A --> B ```')).toBe('Heat moves by conduction and convection.');
|
|
46
|
+
});
|
|
47
|
+
test('rejects flattened table introductions after Markdown extraction', () => {
|
|
48
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('下表列出了标准温度和压力下的典型技术参数。参数 Parameter 密度 2500 999.8 kg/m³ 单位 Unit。')).toBe(true);
|
|
49
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('The following table lists typical parameters. Parameter Density Value Unit.')).toBe(true);
|
|
50
|
+
expect((0, ragPublicText_1.shouldRejectPublicEvidenceClause)('参数 (Parameter) 钠钙玻璃 水 单位 (Unit) :--- :--- 密度 2500 999.8 kg/m³。')).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
test('removes flattened section labels while retaining their factual sentence', () => {
|
|
53
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('核心概念及其数学基础:水杯系统通过传导、对流和辐射与环境交换热量。')).toBe('水杯系统通过传导、对流和辐射与环境交换热量。');
|
|
54
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('Material science: Glass is an amorphous solid without long-range order.')).toBe('Glass is an amorphous solid without long-range order.');
|
|
55
|
+
expect((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)('核心概念及其数学基础 水杯系统通过传导、对流和辐射与环境交换热量。')).toBe('水杯系统通过传导、对流和辐射与环境交换热量。');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -27,6 +27,10 @@ function hasGraphEvidence(fragments) {
|
|
|
27
27
|
function hasConflictEvidence(fragments) {
|
|
28
28
|
return fragments.some((fragment) => fragment.role === 'conflict' && textHasSubstance(fragment));
|
|
29
29
|
}
|
|
30
|
+
function hasIncompleteSourceScan(pack) {
|
|
31
|
+
return pack.sourceDecisions.some(decision => decision.status === 'source_window_unavailable'
|
|
32
|
+
&& String(decision.reason || '').startsWith('runtime_'));
|
|
33
|
+
}
|
|
30
34
|
function hasUnavailableGraphNeighborSourceWindow(pack) {
|
|
31
35
|
return pack.sourceDecisions.some((decision) => (decision.status === 'source_window_unavailable'
|
|
32
36
|
&& String(decision.reason || '').includes('graph_neighbor_support')));
|
|
@@ -61,6 +65,9 @@ function buildDeterministicReview(params) {
|
|
|
61
65
|
if (budgetViolation) {
|
|
62
66
|
reasons.push('context_budget_dropped_fragments');
|
|
63
67
|
}
|
|
68
|
+
const incompleteSourceScan = hasIncompleteSourceScan(params.contextPack);
|
|
69
|
+
if (incompleteSourceScan)
|
|
70
|
+
reasons.push('source_scan_incomplete');
|
|
64
71
|
if (!directSupport) {
|
|
65
72
|
return {
|
|
66
73
|
reviewedAt: String(params.reviewedAt || new Date().toISOString()),
|
|
@@ -91,6 +98,7 @@ function buildDeterministicReview(params) {
|
|
|
91
98
|
&& documentAugmentation
|
|
92
99
|
&& !conflictEvidence
|
|
93
100
|
&& !graphNeighborEvidenceMissing
|
|
101
|
+
&& !incompleteSourceScan
|
|
94
102
|
? 'sufficient'
|
|
95
103
|
: 'borderline';
|
|
96
104
|
return {
|
|
@@ -137,11 +145,13 @@ async function reviewRagContextSufficiency(params) {
|
|
|
137
145
|
const deterministicReview = buildDeterministicReview(params);
|
|
138
146
|
if (deterministicReview.status !== 'borderline'
|
|
139
147
|
|| params.allowLlmJudge !== true
|
|
140
|
-
|| typeof params.llmJudge !== 'function'
|
|
148
|
+
|| typeof params.llmJudge !== 'function'
|
|
149
|
+
|| hasIncompleteSourceScan(params.contextPack)) {
|
|
141
150
|
return deterministicReview;
|
|
142
151
|
}
|
|
143
152
|
try {
|
|
144
153
|
const judgeReview = await params.llmJudge({
|
|
154
|
+
signal: params.signal,
|
|
145
155
|
query: params.query,
|
|
146
156
|
contextPack: params.contextPack,
|
|
147
157
|
graphContext: params.graphContext,
|
|
@@ -155,43 +155,30 @@ function buildReviewContent(input) {
|
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
157
|
async function completeWithTimeout(params) {
|
|
158
|
+
params.request.signal?.throwIfAborted();
|
|
158
159
|
const controller = new AbortController();
|
|
159
160
|
let didTimeout = false;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
});
|
|
161
|
+
const timer = setTimeout(() => {
|
|
162
|
+
didTimeout = true;
|
|
163
|
+
controller.abort(new Error('RAG sufficiency judge timed out.'));
|
|
164
|
+
}, params.timeoutMs);
|
|
165
|
+
const onParentAbort = () => controller.abort(params.request.signal?.reason);
|
|
166
|
+
params.request.signal?.addEventListener('abort', onParentAbort, { once: true });
|
|
176
167
|
try {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
signal: controller.signal,
|
|
181
|
-
}),
|
|
182
|
-
timeoutPromise,
|
|
183
|
-
]);
|
|
168
|
+
const completion = await params.client.complete({ ...params.request, signal: controller.signal });
|
|
169
|
+
controller.signal.throwIfAborted();
|
|
170
|
+
return completion;
|
|
184
171
|
}
|
|
185
172
|
catch (error) {
|
|
186
173
|
if (didTimeout) {
|
|
187
174
|
throw new Error('RAG sufficiency judge timed out.');
|
|
188
175
|
}
|
|
176
|
+
params.request.signal?.throwIfAborted();
|
|
189
177
|
throw error;
|
|
190
178
|
}
|
|
191
179
|
finally {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
180
|
+
clearTimeout(timer);
|
|
181
|
+
params.request.signal?.removeEventListener('abort', onParentAbort);
|
|
195
182
|
}
|
|
196
183
|
}
|
|
197
184
|
function createRagSufficiencyProviderJudge(options) {
|
|
@@ -199,7 +186,9 @@ function createRagSufficiencyProviderJudge(options) {
|
|
|
199
186
|
const timeoutMs = resolveBoundedInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
200
187
|
const maxTokens = resolveBoundedInteger(options.maxTokens, DEFAULT_MAX_TOKENS, MIN_MAX_TOKENS, MAX_MAX_TOKENS);
|
|
201
188
|
return async (input) => {
|
|
189
|
+
input.signal?.throwIfAborted();
|
|
202
190
|
const settings = await options.settingsProvider();
|
|
191
|
+
input.signal?.throwIfAborted();
|
|
203
192
|
const provider = (0, LlmProvider_1.selectProviderForTask)(settings, RAG_SUFFICIENCY_TASK_KEY);
|
|
204
193
|
const model = (0, LlmProvider_1.selectModelForTask)(settings, RAG_SUFFICIENCY_TASK_KEY, provider);
|
|
205
194
|
const configuredPrompt = settings.enableGlobalCustomPrompts
|
|
@@ -209,6 +198,7 @@ function createRagSufficiencyProviderJudge(options) {
|
|
|
209
198
|
client,
|
|
210
199
|
timeoutMs,
|
|
211
200
|
request: {
|
|
201
|
+
signal: input.signal,
|
|
212
202
|
provider,
|
|
213
203
|
model,
|
|
214
204
|
prompt: configuredPrompt || DEFAULT_REVIEW_PROMPT,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ragSufficiencyProviderJudge_1 = require("./ragSufficiencyProviderJudge");
|
|
4
|
+
const events_1 = require("events");
|
|
4
5
|
function makeSettings() {
|
|
5
6
|
return {
|
|
6
7
|
providers: [
|
|
@@ -68,6 +69,56 @@ function makePack() {
|
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
describe('createRagSufficiencyProviderJudge', () => {
|
|
72
|
+
test('turn cancellation reaches provider I/O', async () => {
|
|
73
|
+
const controller = new AbortController();
|
|
74
|
+
const reason = new Error('turn_cancelled');
|
|
75
|
+
let entered;
|
|
76
|
+
const started = new Promise(resolve => { entered = resolve; });
|
|
77
|
+
const complete = jest.fn(request => new Promise((_resolve, reject) => {
|
|
78
|
+
request.signal.addEventListener('abort', () => reject(request.signal.reason), { once: true });
|
|
79
|
+
entered();
|
|
80
|
+
}));
|
|
81
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({ settingsProvider: makeSettings, llmClient: { complete } });
|
|
82
|
+
const pending = judge({ query: 'water glass', contextPack: makePack(), signal: controller.signal }).catch(error => error);
|
|
83
|
+
await started;
|
|
84
|
+
controller.abort(reason);
|
|
85
|
+
expect(await pending).toBe(reason);
|
|
86
|
+
expect(complete.mock.calls[0][0].signal.aborted).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
test('does not return while a cancelled provider callback can still run', async () => {
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
const reason = new Error('turn_cancelled');
|
|
91
|
+
let release;
|
|
92
|
+
let entered;
|
|
93
|
+
const started = new Promise(resolve => { entered = resolve; });
|
|
94
|
+
const held = new Promise(resolve => { release = resolve; });
|
|
95
|
+
const complete = jest.fn(async () => { entered(); await held; return { text: '{"status":"sufficient","score":1}', provider: 'OpenAI', model: 'gpt-test' }; });
|
|
96
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({ settingsProvider: makeSettings, llmClient: { complete } });
|
|
97
|
+
let finished = false;
|
|
98
|
+
const pending = judge({ query: 'water glass', contextPack: makePack(), signal: controller.signal }).catch(error => error).finally(() => { finished = true; });
|
|
99
|
+
await started;
|
|
100
|
+
controller.abort(reason);
|
|
101
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
102
|
+
const finishedBeforeProviderStopped = finished;
|
|
103
|
+
release();
|
|
104
|
+
expect(await pending).toBe(reason);
|
|
105
|
+
expect(finishedBeforeProviderStopped).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
test('a pre-cancelled turn does not start a provider request', async () => {
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
controller.abort(new Error('already_cancelled'));
|
|
110
|
+
const complete = jest.fn();
|
|
111
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({ settingsProvider: makeSettings, llmClient: { complete } });
|
|
112
|
+
await expect(judge({ query: 'water glass', contextPack: makePack(), signal: controller.signal })).rejects.toThrow('already_cancelled');
|
|
113
|
+
expect(complete).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
test('completed provider work releases the parent cancellation listener', async () => {
|
|
116
|
+
const controller = new AbortController();
|
|
117
|
+
const complete = jest.fn().mockResolvedValue({ text: '{"status":"sufficient","score":1}', provider: 'OpenAI', model: 'gpt-test' });
|
|
118
|
+
const judge = (0, ragSufficiencyProviderJudge_1.createRagSufficiencyProviderJudge)({ settingsProvider: makeSettings, llmClient: { complete } });
|
|
119
|
+
await judge({ query: 'water glass', contextPack: makePack(), signal: controller.signal });
|
|
120
|
+
expect((0, events_1.getEventListeners)(controller.signal, 'abort')).toHaveLength(0);
|
|
121
|
+
});
|
|
71
122
|
test('calls the configured NoteMD provider with bounded JSON-only review input', async () => {
|
|
72
123
|
const complete = jest.fn().mockResolvedValue({
|
|
73
124
|
text: '```json\n{"status":"sufficient","score":0.84,"reasons":["answerable_from_context"],"degradationState":"none"}\n```',
|
|
@@ -26,6 +26,92 @@ function parsePositiveIntegerValue(value) {
|
|
|
26
26
|
}
|
|
27
27
|
return Math.floor(numeric);
|
|
28
28
|
}
|
|
29
|
+
function normalizeAnswerLanguageValue(value) {
|
|
30
|
+
const normalized = String(value || '')
|
|
31
|
+
.trim()
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/_/gu, '-');
|
|
34
|
+
if (!normalized) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
if (normalized === 'auto') {
|
|
38
|
+
return 'auto';
|
|
39
|
+
}
|
|
40
|
+
if (normalized === 'zh'
|
|
41
|
+
|| normalized.startsWith('zh-')
|
|
42
|
+
|| ['chinese', 'mandarin', 'simplified-chinese', 'traditional-chinese'].includes(normalized)) {
|
|
43
|
+
return 'zh';
|
|
44
|
+
}
|
|
45
|
+
if (normalized === 'en'
|
|
46
|
+
|| normalized.startsWith('en-')
|
|
47
|
+
|| normalized === 'english') {
|
|
48
|
+
return 'en';
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function normalizeResponseProfileValue(value) {
|
|
53
|
+
const normalized = String(value || '').trim().toLowerCase().replace(/[-\s]+/gu, '_');
|
|
54
|
+
if (normalized === 'default') {
|
|
55
|
+
return 'default';
|
|
56
|
+
}
|
|
57
|
+
if (normalized === 'mobile_compact' || normalized === 'mobile') {
|
|
58
|
+
return 'mobile_compact';
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function normalizeResponseModeValue(value) {
|
|
63
|
+
const normalized = String(value || '').trim().toLowerCase().replace(/[-\s]+/gu, '_');
|
|
64
|
+
if (normalized === 'slim' || normalized === 'definition' || normalized === 'compact' || normalized === 'default') {
|
|
65
|
+
return 'slim';
|
|
66
|
+
}
|
|
67
|
+
if (normalized === 'full' || normalized === 'comprehensive' || normalized === 'report') {
|
|
68
|
+
return 'full';
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
function normalizeResponseBudgetModeValue(value) {
|
|
73
|
+
const normalized = String(value || '').trim().toLowerCase().replace(/[-\s]+/gu, '_');
|
|
74
|
+
if (!normalized || ['adaptive', 'default', 'standard', 'auto'].includes(normalized)) {
|
|
75
|
+
return 'adaptive';
|
|
76
|
+
}
|
|
77
|
+
if (['unbounded', 'no_cap', 'nocap', 'no_limit', 'nolimit', 'unlimited'].includes(normalized)) {
|
|
78
|
+
return 'unbounded';
|
|
79
|
+
}
|
|
80
|
+
return 'adaptive';
|
|
81
|
+
}
|
|
82
|
+
function normalizeResponseBudgetCapabilityValue(value) {
|
|
83
|
+
if (!isObjectRecord(value)) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const memoryClass = String(value.memoryClass || value.memory_class || '')
|
|
87
|
+
.trim()
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.replace(/[-\s]+/gu, '_');
|
|
90
|
+
const workload = String(value.workload || value.workload_class || '')
|
|
91
|
+
.trim()
|
|
92
|
+
.toLowerCase()
|
|
93
|
+
.replace(/[-\s]+/gu, '_');
|
|
94
|
+
const normalizePositiveHint = (hint) => {
|
|
95
|
+
const numeric = Number(hint);
|
|
96
|
+
return Number.isFinite(numeric) && numeric > 0 ? Math.floor(numeric) : undefined;
|
|
97
|
+
};
|
|
98
|
+
const normalized = {};
|
|
99
|
+
if (['low', 'standard', 'high'].includes(memoryClass)) {
|
|
100
|
+
normalized.memoryClass = memoryClass;
|
|
101
|
+
}
|
|
102
|
+
if (['normal', 'large', 'max'].includes(workload)) {
|
|
103
|
+
normalized.workload = workload;
|
|
104
|
+
}
|
|
105
|
+
const maxReportCharsHint = normalizePositiveHint(value.maxReportCharsHint ?? value.max_report_chars_hint);
|
|
106
|
+
if (maxReportCharsHint !== undefined) {
|
|
107
|
+
normalized.maxReportCharsHint = maxReportCharsHint;
|
|
108
|
+
}
|
|
109
|
+
const maxSerializedBytesHint = normalizePositiveHint(value.maxSerializedBytesHint ?? value.max_serialized_bytes_hint);
|
|
110
|
+
if (maxSerializedBytesHint !== undefined) {
|
|
111
|
+
normalized.maxSerializedBytesHint = maxSerializedBytesHint;
|
|
112
|
+
}
|
|
113
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
114
|
+
}
|
|
29
115
|
function normalizeStringList(values, options = {}) {
|
|
30
116
|
const list = Array.isArray(values) ? values : [];
|
|
31
117
|
const normalized = list
|
|
@@ -81,11 +167,31 @@ function normalizeKnowledgeQueryRequestPayload(payload) {
|
|
|
81
167
|
function normalizeAgentConversationRequestPayload(payload) {
|
|
82
168
|
const record = isObjectRecord(payload) ? payload : {};
|
|
83
169
|
const topKValue = parsePositiveIntegerValue(readFirstPresentValue(record, ['topK', 'k', 'limit']));
|
|
170
|
+
const answerLanguage = normalizeAnswerLanguageValue(readFirstPresentValue(record, ['answerLanguage', 'answer_language', 'responseLanguage', 'response_language']));
|
|
171
|
+
const responseProfile = normalizeResponseProfileValue(readFirstPresentValue(record, ['responseProfile', 'response_profile', 'profile']));
|
|
172
|
+
const responseMode = normalizeResponseModeValue(readFirstPresentValue(record, ['responseMode', 'response_mode', 'answerMode', 'answer_mode', 'detailMode', 'detail_mode']));
|
|
173
|
+
const responseBudgetMode = normalizeResponseBudgetModeValue(readFirstPresentValue(record, [
|
|
174
|
+
'responseBudgetMode',
|
|
175
|
+
'response_budget_mode',
|
|
176
|
+
'budgetMode',
|
|
177
|
+
'budget_mode',
|
|
178
|
+
]));
|
|
179
|
+
const responseBudgetCapability = normalizeResponseBudgetCapabilityValue(readFirstPresentValue(record, [
|
|
180
|
+
'responseBudgetCapability',
|
|
181
|
+
'response_budget_capability',
|
|
182
|
+
'budgetCapability',
|
|
183
|
+
'budget_capability',
|
|
184
|
+
]));
|
|
84
185
|
return {
|
|
85
186
|
userId: String(readFirstPresentValue(record, ['userId', 'user_id', 'learnerId']) || '').trim(),
|
|
86
187
|
sessionId: readFirstNonEmptyString(record, ['sessionId', 'session_id']),
|
|
87
188
|
activeTarget: readFirstNonEmptyString(record, ['activeTarget', 'active_target', 'target']),
|
|
88
189
|
message: readFirstNonEmptyString(record, ['message', 'prompt', 'query', 'q', 'text']) || '',
|
|
190
|
+
answerLanguage,
|
|
191
|
+
responseMode,
|
|
192
|
+
responseBudgetMode,
|
|
193
|
+
responseBudgetCapability,
|
|
194
|
+
responseProfile,
|
|
89
195
|
topK: topKValue > 0 ? topKValue : undefined,
|
|
90
196
|
asOf: readFirstNonEmptyString(record, ['asOf', 'as_of', 'timestamp', 'now']),
|
|
91
197
|
persistMemory: readFirstPresentValue(record, ['persistMemory', 'persist_memory']) !== false,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const requestNormalization_1 = require("./requestNormalization");
|
|
4
|
+
describe('agent conversation request normalization', () => {
|
|
5
|
+
test.each([
|
|
6
|
+
['zh-CN', 'zh'],
|
|
7
|
+
['zh_CN', 'zh'],
|
|
8
|
+
['Chinese', 'zh'],
|
|
9
|
+
['en-US', 'en'],
|
|
10
|
+
['en_US', 'en'],
|
|
11
|
+
['English', 'en'],
|
|
12
|
+
['auto', 'auto'],
|
|
13
|
+
['unsupported-language', undefined],
|
|
14
|
+
])('normalizes answer language %s to %s', (answerLanguage, expectedLanguage) => {
|
|
15
|
+
expect((0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
16
|
+
message: 'what is water glass?',
|
|
17
|
+
answerLanguage,
|
|
18
|
+
}).answerLanguage).toBe(expectedLanguage);
|
|
19
|
+
});
|
|
20
|
+
test('keeps an explicit English answer contract even when the query contains CJK text', () => {
|
|
21
|
+
expect((0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
22
|
+
message: '\u4ec0\u4e48\u662f water glass?',
|
|
23
|
+
response_language: 'en-US',
|
|
24
|
+
}).answerLanguage).toBe('en');
|
|
25
|
+
});
|
|
26
|
+
test('normalizes the additive mobile response profile without changing the default', () => {
|
|
27
|
+
expect((0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
28
|
+
message: 'what is water glass?',
|
|
29
|
+
response_profile: 'mobile',
|
|
30
|
+
}).responseProfile).toBe('mobile_compact');
|
|
31
|
+
expect((0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
32
|
+
message: 'what is water glass?',
|
|
33
|
+
}).responseProfile).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
test.each([
|
|
36
|
+
[undefined, undefined],
|
|
37
|
+
['slim', 'slim'],
|
|
38
|
+
['definition', 'slim'],
|
|
39
|
+
['compact', 'slim'],
|
|
40
|
+
['full', 'full'],
|
|
41
|
+
['comprehensive', 'full'],
|
|
42
|
+
['unsupported-mode', undefined],
|
|
43
|
+
])('normalizes answer response mode %s to %s', (responseMode, expectedMode) => {
|
|
44
|
+
expect((0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
45
|
+
message: 'what is water glass?',
|
|
46
|
+
responseMode,
|
|
47
|
+
}).responseMode).toBe(expectedMode);
|
|
48
|
+
});
|
|
49
|
+
test('keeps response mode independent from the mobile response profile', () => {
|
|
50
|
+
const request = (0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
51
|
+
message: 'what is water glass?',
|
|
52
|
+
response_mode: 'full',
|
|
53
|
+
response_profile: 'mobile',
|
|
54
|
+
});
|
|
55
|
+
expect(request.responseMode).toBe('full');
|
|
56
|
+
expect(request.responseProfile).toBe('mobile_compact');
|
|
57
|
+
});
|
|
58
|
+
test.each([
|
|
59
|
+
[undefined, 'adaptive'],
|
|
60
|
+
['adaptive', 'adaptive'],
|
|
61
|
+
['unbounded', 'unbounded'],
|
|
62
|
+
['no-cap', 'unbounded'],
|
|
63
|
+
['no_cap', 'unbounded'],
|
|
64
|
+
['unsupported-budget', 'adaptive'],
|
|
65
|
+
])('normalizes response budget mode %s to %s', (responseBudgetMode, expectedMode) => {
|
|
66
|
+
expect((0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
67
|
+
message: 'what is water glass?',
|
|
68
|
+
responseBudgetMode,
|
|
69
|
+
}).responseBudgetMode).toBe(expectedMode);
|
|
70
|
+
});
|
|
71
|
+
test('normalizes and clamps response budget capability hints at the boundary', () => {
|
|
72
|
+
const request = (0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
73
|
+
message: 'what is water glass?',
|
|
74
|
+
response_budget_mode: 'unbounded',
|
|
75
|
+
response_budget_capability: {
|
|
76
|
+
memoryClass: 'HIGH',
|
|
77
|
+
workload: 'max',
|
|
78
|
+
maxReportCharsHint: 999999999,
|
|
79
|
+
maxSerializedBytesHint: -4,
|
|
80
|
+
ignored: 'field',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
expect(request.responseBudgetMode).toBe('unbounded');
|
|
84
|
+
expect(request.responseBudgetCapability).toEqual({
|
|
85
|
+
memoryClass: 'high',
|
|
86
|
+
workload: 'max',
|
|
87
|
+
maxReportCharsHint: 999999999,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
test('keeps budget mode additive when mobile projection is requested', () => {
|
|
91
|
+
const request = (0, requestNormalization_1.normalizeAgentConversationRequestPayload)({
|
|
92
|
+
message: 'what is water glass?',
|
|
93
|
+
response_mode: 'full',
|
|
94
|
+
response_budget_mode: 'unbounded',
|
|
95
|
+
response_profile: 'mobile',
|
|
96
|
+
});
|
|
97
|
+
expect(request.responseMode).toBe('full');
|
|
98
|
+
expect(request.responseBudgetMode).toBe('unbounded');
|
|
99
|
+
expect(request.responseProfile).toBe('mobile_compact');
|
|
100
|
+
});
|
|
101
|
+
});
|