@pellux/goodvibes-sdk 0.27.5 → 0.27.6
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/dist/_internal/contracts/artifacts/operator-contract.json +1 -1
- package/dist/_internal/contracts/generated/foundation-metadata.d.ts +1 -1
- package/dist/_internal/contracts/generated/foundation-metadata.js +1 -1
- package/dist/_internal/contracts/generated/operator-contract.js +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.js +45 -3
- package/dist/_internal/platform/knowledge/home-graph/state.js +1 -1
- package/dist/_internal/platform/knowledge/semantic/answer.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/answer.js +101 -11
- package/dist/_internal/platform/knowledge/semantic/enrichment.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/enrichment.js +34 -1
- package/dist/_internal/platform/knowledge/semantic/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/service.js +13 -1
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Synced from packages/contracts/src/generated/foundation-metadata.ts
|
|
2
2
|
export const FOUNDATION_METADATA = {
|
|
3
3
|
"productId": "goodvibes",
|
|
4
|
-
"productVersion": "0.27.
|
|
4
|
+
"productVersion": "0.27.6",
|
|
5
5
|
"operatorMethodCount": 256,
|
|
6
6
|
"operatorEventCount": 30,
|
|
7
7
|
"peerEndpointCount": 6
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAoIxD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;CAC/E;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAuBrG;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,yBAAyB,GAAG,IAAI,GAAG,SAAS,EACxF,KAAK,EAAE,MAAM,GACZ,qBAAqB,EAAE,CAoFzB;AAED,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,yBAAyB,GAAG,IAAI,GAAG,SAAS,EACxF,KAAK,EAAE,MAAM,GACZ,qBAAqB,EAAE,CAoCzB;AAmND,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,yBAAyB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAWhH"}
|
|
@@ -91,10 +91,39 @@ const SOURCE_EVIDENCE_TOKENS = new Set([
|
|
|
91
91
|
'supports',
|
|
92
92
|
'warranty',
|
|
93
93
|
]);
|
|
94
|
+
const ANCHOR_INTENT_TOKENS = new Set([
|
|
95
|
+
...SOURCE_EVIDENCE_TOKENS,
|
|
96
|
+
'function',
|
|
97
|
+
'functions',
|
|
98
|
+
'mode',
|
|
99
|
+
'modes',
|
|
100
|
+
'setup',
|
|
101
|
+
'install',
|
|
102
|
+
'configure',
|
|
103
|
+
'documentation',
|
|
104
|
+
'manual',
|
|
105
|
+
]);
|
|
106
|
+
const HOME_GRAPH_ANCHOR_KINDS = new Set([
|
|
107
|
+
'ha_home',
|
|
108
|
+
'ha_entity',
|
|
109
|
+
'ha_device',
|
|
110
|
+
'ha_area',
|
|
111
|
+
'ha_automation',
|
|
112
|
+
'ha_script',
|
|
113
|
+
'ha_scene',
|
|
114
|
+
'ha_label',
|
|
115
|
+
'ha_integration',
|
|
116
|
+
'ha_room',
|
|
117
|
+
'ha_device_passport',
|
|
118
|
+
'ha_maintenance_item',
|
|
119
|
+
'ha_troubleshooting_case',
|
|
120
|
+
'ha_purchase',
|
|
121
|
+
'ha_network_node',
|
|
122
|
+
]);
|
|
94
123
|
const INTEGRATION_QUERY_TOKENS = new Set(['automation', 'automations', 'homeassistant', 'integration', 'integrations', 'webostv']);
|
|
95
124
|
export function readHomeGraphSearchState(store, spaceId) {
|
|
96
125
|
const sources = store.listSources(10_000).filter((source) => (belongsToSpace(source, spaceId) && !isGeneratedPageSource(source)));
|
|
97
|
-
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId));
|
|
126
|
+
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId) && node.status !== 'stale');
|
|
98
127
|
const sourceIds = new Set(sources.map((source) => source.id));
|
|
99
128
|
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
100
129
|
const edges = store.listEdges().filter((edge) => (edgeIsActive(edge)
|
|
@@ -118,8 +147,9 @@ export function scoreHomeGraphResults(query, sources, nodes, edges, extractionBy
|
|
|
118
147
|
if (tokens.length === 0)
|
|
119
148
|
return [];
|
|
120
149
|
const expandedTokens = expandTokens(tokens);
|
|
121
|
-
const
|
|
122
|
-
const
|
|
150
|
+
const anchorTokens = selectAnchorQueryTokens(tokens);
|
|
151
|
+
const anchors = selectAnchorNodes(anchorTokens, nodes);
|
|
152
|
+
const sourceAnchors = selectSourceAnchors(anchorTokens, anchors.map((anchor) => anchor.node));
|
|
123
153
|
const anchorIds = new Set(sourceAnchors.map((node) => node.id));
|
|
124
154
|
const anchorIdentityTokens = collectAnchorIdentityTokens(sourceAnchors);
|
|
125
155
|
const sourceLinks = buildSourceLinkIndex(edges);
|
|
@@ -319,7 +349,11 @@ function sourceAnchorIdentityScore(anchorTokens, source, extraction) {
|
|
|
319
349
|
]);
|
|
320
350
|
}
|
|
321
351
|
function selectAnchorNodes(tokens, nodes) {
|
|
352
|
+
if (tokens.length === 0)
|
|
353
|
+
return [];
|
|
322
354
|
return nodes.map((node) => {
|
|
355
|
+
if (!isHomeGraphAnchorNode(node))
|
|
356
|
+
return { node, score: 0 };
|
|
323
357
|
const baseScore = scoreFields(tokens, nodeIdentityFields(node));
|
|
324
358
|
const intentBoost = sourceAnchorIntentBoost(tokens, node);
|
|
325
359
|
return {
|
|
@@ -331,6 +365,14 @@ function selectAnchorNodes(tokens, nodes) {
|
|
|
331
365
|
.sort((a, b) => b.score - a.score || a.node.id.localeCompare(b.node.id))
|
|
332
366
|
.slice(0, 12);
|
|
333
367
|
}
|
|
368
|
+
function selectAnchorQueryTokens(tokens) {
|
|
369
|
+
return tokens.filter((token) => !ANCHOR_INTENT_TOKENS.has(token));
|
|
370
|
+
}
|
|
371
|
+
function isHomeGraphAnchorNode(node) {
|
|
372
|
+
if (typeof node.metadata.semanticKind === 'string')
|
|
373
|
+
return false;
|
|
374
|
+
return HOME_GRAPH_ANCHOR_KINDS.has(node.kind);
|
|
375
|
+
}
|
|
334
376
|
function selectSourceAnchors(tokens, nodes) {
|
|
335
377
|
const preferred = nodes.filter((node) => sourceAnchorIntentBoost(tokens, node) >= 0);
|
|
336
378
|
if (preferred.length > 0)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { belongsToSpace, edgeIsActive, readRecord } from './helpers.js';
|
|
2
2
|
export function readHomeGraphState(store, spaceId) {
|
|
3
3
|
const sources = store.listSources(10_000).filter((source) => belongsToSpace(source, spaceId));
|
|
4
|
-
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId));
|
|
4
|
+
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId) && node.status !== 'stale');
|
|
5
5
|
const sourceIds = new Set(sources.map((source) => source.id));
|
|
6
6
|
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
7
7
|
const edges = store.listEdges().filter((edge) => (edgeIsActive(edge)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAE7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAepB,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAE7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAepB,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAsCD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,6BAA6B,CAAC,CAiDxC"}
|
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
import { getKnowledgeSpaceId, isInKnowledgeSpace, normalizeKnowledgeSpaceId } from '../spaces.js';
|
|
2
2
|
import { MAX_ANSWER_EVIDENCE_CHARS, clampText, normalizeWhitespace, readRecord, readString, scoreSemanticText, semanticHash, semanticMetadata, sourceSemanticText, tokenizeSemanticQuery, uniqueStrings, } from './utils.js';
|
|
3
|
+
const GENERIC_ANSWER_INTENT_TOKENS = new Set([
|
|
4
|
+
'capabilities',
|
|
5
|
+
'capability',
|
|
6
|
+
'configuration',
|
|
7
|
+
'configure',
|
|
8
|
+
'feature',
|
|
9
|
+
'features',
|
|
10
|
+
'function',
|
|
11
|
+
'functions',
|
|
12
|
+
'install',
|
|
13
|
+
'mode',
|
|
14
|
+
'modes',
|
|
15
|
+
'procedure',
|
|
16
|
+
'setting',
|
|
17
|
+
'settings',
|
|
18
|
+
'setup',
|
|
19
|
+
'spec',
|
|
20
|
+
'specification',
|
|
21
|
+
'specifications',
|
|
22
|
+
'specs',
|
|
23
|
+
'support',
|
|
24
|
+
'supported',
|
|
25
|
+
'supports',
|
|
26
|
+
]);
|
|
3
27
|
export async function answerKnowledgeQuery(context, input) {
|
|
4
28
|
const spaceId = normalizeKnowledgeSpaceId(input.knowledgeSpaceId);
|
|
5
29
|
const mode = input.mode ?? 'standard';
|
|
@@ -53,6 +77,7 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
53
77
|
const tokens = expandQueryTokens(tokenizeSemanticQuery(input.query));
|
|
54
78
|
if (tokens.length === 0)
|
|
55
79
|
return [];
|
|
80
|
+
const subjectTokens = tokens.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
|
|
56
81
|
const candidateSourceIds = new Set(input.candidateSourceIds ?? []);
|
|
57
82
|
const candidateNodeIds = new Set(input.candidateNodeIds ?? []);
|
|
58
83
|
const linkedObjectIds = new Set((input.linkedObjects ?? []).map((node) => node.id));
|
|
@@ -66,17 +91,28 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
66
91
|
const extraction = store.getExtractionBySourceId(source.id);
|
|
67
92
|
const facts = filterFactsForQuery(input.query, sourceFacts.get(source.id) ?? []);
|
|
68
93
|
const text = sourceSemanticText(source, extraction);
|
|
69
|
-
const
|
|
94
|
+
const scoringText = [
|
|
70
95
|
source.title,
|
|
71
96
|
source.summary,
|
|
72
97
|
source.description,
|
|
73
98
|
source.tags.join(' '),
|
|
74
99
|
text,
|
|
75
100
|
facts.map(renderFactForScoring).join(' '),
|
|
76
|
-
].join('\n')
|
|
101
|
+
].join('\n');
|
|
102
|
+
const baseScore = scoreSemanticText(scoringText, tokens);
|
|
103
|
+
const subjectScore = subjectTokens.length > 0 ? scoreSemanticText(scoringText, subjectTokens) : 0;
|
|
77
104
|
const candidateBoost = candidateSourceIds.has(source.id) ? 220 : 0;
|
|
78
105
|
const linkedBoost = linkedSourceIds.has(source.id) ? 160 : 0;
|
|
79
|
-
const
|
|
106
|
+
const genericOnly = subjectTokens.length > 0 && subjectScore === 0;
|
|
107
|
+
const weakStrictCandidate = strictCandidates
|
|
108
|
+
&& candidateSourceIds.size > 1
|
|
109
|
+
&& candidateSourceIds.has(source.id)
|
|
110
|
+
&& !linkedSourceIds.has(source.id)
|
|
111
|
+
&& genericOnly;
|
|
112
|
+
const weakBroadMatch = !strictCandidates && genericOnly;
|
|
113
|
+
const score = weakStrictCandidate || weakBroadMatch
|
|
114
|
+
? 0
|
|
115
|
+
: baseScore + candidateBoost + linkedBoost + Math.min(60, facts.length * 6);
|
|
80
116
|
return {
|
|
81
117
|
kind: 'source',
|
|
82
118
|
id: source.id,
|
|
@@ -88,18 +124,25 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
88
124
|
};
|
|
89
125
|
});
|
|
90
126
|
const nodeItems = store.listNodes(10_000)
|
|
91
|
-
.filter((node) => belongsToAnswerSpace(node, spaceId))
|
|
127
|
+
.filter((node) => belongsToAnswerSpace(node, spaceId) && node.status !== 'stale')
|
|
92
128
|
.filter((node) => !strictCandidates
|
|
93
129
|
|| candidateNodeIds.has(node.id)
|
|
94
130
|
|| linkedObjectIds.has(node.id)
|
|
95
131
|
|| (typeof node.sourceId === 'string' && candidateSourceIds.has(node.sourceId)))
|
|
96
132
|
.map((node) => {
|
|
97
|
-
const
|
|
133
|
+
const scoringText = [
|
|
98
134
|
node.title,
|
|
99
135
|
node.summary,
|
|
100
136
|
node.aliases.join(' '),
|
|
101
137
|
JSON.stringify(node.metadata),
|
|
102
|
-
].join('\n')
|
|
138
|
+
].join('\n');
|
|
139
|
+
const baseScore = scoreSemanticText(scoringText, tokens);
|
|
140
|
+
const subjectScore = subjectTokens.length > 0 ? scoreSemanticText(scoringText, subjectTokens) : 0;
|
|
141
|
+
const genericOnly = subjectTokens.length > 0 && subjectScore === 0;
|
|
142
|
+
const candidateOrLinked = candidateNodeIds.has(node.id) || linkedObjectIds.has(node.id);
|
|
143
|
+
const score = genericOnly && !candidateOrLinked
|
|
144
|
+
? 0
|
|
145
|
+
: baseScore + (candidateOrLinked ? 120 : 0) + semanticKindBoost(node);
|
|
103
146
|
return {
|
|
104
147
|
kind: 'node',
|
|
105
148
|
id: node.id,
|
|
@@ -184,10 +227,14 @@ function renderFallbackAnswer(query, mode, evidence) {
|
|
|
184
227
|
return lines.length > 0 ? lines.join('\n') : `No knowledge matched "${query}".`;
|
|
185
228
|
}
|
|
186
229
|
function filterFactsForQuery(query, facts) {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
230
|
+
const tokens = tokenizeSemanticQuery(query);
|
|
231
|
+
const intent = factIntent(tokens);
|
|
232
|
+
const matching = intent
|
|
233
|
+
? facts.filter((fact) => intent.has(readString(fact.metadata.factKind) ?? 'note'))
|
|
234
|
+
: [...facts];
|
|
235
|
+
return matching
|
|
236
|
+
.filter((fact) => fact.status !== 'stale' && !isLowValueFactForQuery(tokens, intent, fact))
|
|
237
|
+
.sort(compareFactQuality);
|
|
191
238
|
}
|
|
192
239
|
function factIntent(tokens) {
|
|
193
240
|
const tokenSet = new Set(tokens);
|
|
@@ -207,6 +254,49 @@ function factIntent(tokens) {
|
|
|
207
254
|
function hasAny(values, candidates) {
|
|
208
255
|
return candidates.some((candidate) => values.has(candidate));
|
|
209
256
|
}
|
|
257
|
+
function isLowValueFactForQuery(tokens, intent, fact) {
|
|
258
|
+
if (!intent || !hasFeatureIntent(intent))
|
|
259
|
+
return false;
|
|
260
|
+
const kind = readString(fact.metadata.factKind) ?? 'note';
|
|
261
|
+
if (!['feature', 'capability', 'specification', 'compatibility', 'configuration', 'identity'].includes(kind))
|
|
262
|
+
return false;
|
|
263
|
+
const text = [
|
|
264
|
+
fact.title,
|
|
265
|
+
fact.summary,
|
|
266
|
+
readString(fact.metadata.value),
|
|
267
|
+
readString(fact.metadata.evidence),
|
|
268
|
+
Array.isArray(fact.metadata.labels) ? fact.metadata.labels.join(' ') : '',
|
|
269
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
270
|
+
if (isManualBoilerplateFeatureText(text))
|
|
271
|
+
return true;
|
|
272
|
+
const extractor = readString(fact.metadata.extractor);
|
|
273
|
+
const confidence = typeof fact.confidence === 'number' ? fact.confidence : 0;
|
|
274
|
+
if (extractor !== 'deterministic' || confidence > 60)
|
|
275
|
+
return false;
|
|
276
|
+
const subjectTokens = tokens.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
|
|
277
|
+
return subjectTokens.length > 0 && !hasConcreteFeatureSignal(text);
|
|
278
|
+
}
|
|
279
|
+
function hasFeatureIntent(intent) {
|
|
280
|
+
return intent.has('feature') || intent.has('capability') || intent.has('specification') || intent.has('compatibility');
|
|
281
|
+
}
|
|
282
|
+
function isManualBoilerplateFeatureText(text) {
|
|
283
|
+
return /\b(items? supplied|accessories supplied|contents? of (this )?manual|may vary|may be changed|without prior notice|depending (upon|on) (the )?model|available menus? and options?|product specifications?|power cord|electric shock|fire hazard|certified cable|do not|warning|caution|risk|hazard|clean only|near water|ventilation|antenna grounding)\b/.test(text);
|
|
284
|
+
}
|
|
285
|
+
function hasConcreteFeatureSignal(text) {
|
|
286
|
+
return /\b(hdmi|usb|hdr|hdr10|dolby|vision|earc|arc|bluetooth|wi-?fi|ethernet|voice|remote|game|filmmaker|airplay|chromecast|resolution|4k|8k|refresh|ports?|speakers?|audio|display|screen|apps?|streaming|matter|energy monitoring|scheduling|sensor|battery|z-?wave|zigbee|thread|motion|temperature|humidity|camera|recording|lock|garage|local control|api|automation)\b/.test(text);
|
|
287
|
+
}
|
|
288
|
+
function compareFactQuality(left, right) {
|
|
289
|
+
return factQuality(right) - factQuality(left) || left.title.localeCompare(right.title);
|
|
290
|
+
}
|
|
291
|
+
function factQuality(fact) {
|
|
292
|
+
const extractor = readString(fact.metadata.extractor);
|
|
293
|
+
const kind = readString(fact.metadata.factKind);
|
|
294
|
+
const value = readString(fact.metadata.value);
|
|
295
|
+
return (extractor === 'llm' ? 40 : 0)
|
|
296
|
+
+ (value ? 12 : 0)
|
|
297
|
+
+ (kind === 'capability' || kind === 'feature' ? 8 : kind === 'specification' ? 6 : 0)
|
|
298
|
+
+ Math.round(fact.confidence / 10);
|
|
299
|
+
}
|
|
210
300
|
async function persistAnswerGap(store, spaceId, query, reason) {
|
|
211
301
|
const node = await store.upsertNode({
|
|
212
302
|
id: `sem-answer-gap-${semanticHash(spaceId, query)}`,
|
|
@@ -245,7 +335,7 @@ async function persistAnswerGaps(store, spaceId, query, gaps) {
|
|
|
245
335
|
return nodes;
|
|
246
336
|
}
|
|
247
337
|
function buildSourceFactIndex(store, spaceId) {
|
|
248
|
-
const facts = store.listNodes(10_000).filter((node) => (node.metadata.semanticKind === 'fact' && belongsToAnswerSpace(node, spaceId)));
|
|
338
|
+
const facts = store.listNodes(10_000).filter((node) => (node.status !== 'stale' && node.metadata.semanticKind === 'fact' && belongsToAnswerSpace(node, spaceId)));
|
|
249
339
|
const bySource = new Map();
|
|
250
340
|
for (const fact of facts) {
|
|
251
341
|
const sourceId = readString(fact.metadata.sourceId) ?? fact.sourceId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrichment.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/enrichment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAEV,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAKV,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAkBpB,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC/C;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,kCAAkC,EAC3C,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,2BAA2B,CAAC,
|
|
1
|
+
{"version":3,"file":"enrichment.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/enrichment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAEV,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAKV,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAkBpB,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC/C;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,kCAAkC,EAC3C,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,2BAA2B,CAAC,CA+BtC"}
|
|
@@ -4,7 +4,9 @@ export async function enrichKnowledgeSource(context, source, options = {}) {
|
|
|
4
4
|
const text = sourceSemanticText(source, extraction);
|
|
5
5
|
const textHash = sourceSemanticHash(source, extraction);
|
|
6
6
|
const existingSemantic = readRecord(source.metadata.semanticEnrichment);
|
|
7
|
-
|
|
7
|
+
const currentExtractor = readString(existingSemantic.extractor);
|
|
8
|
+
const shouldUpgradeDeterministic = Boolean(context.llm && existingSemantic.textHash === textHash && currentExtractor !== 'llm');
|
|
9
|
+
if (!options.force && existingSemantic.textHash === textHash && !shouldUpgradeDeterministic) {
|
|
8
10
|
return emptyResult(source, true, 'semantic enrichment is current');
|
|
9
11
|
}
|
|
10
12
|
if (text.length < 40) {
|
|
@@ -257,8 +259,39 @@ async function persistSemanticExtraction(store, source, extraction, semantic, op
|
|
|
257
259
|
});
|
|
258
260
|
}
|
|
259
261
|
const wikiPage = await persistWikiPage(store, source, semantic, spaceId, options.textHash);
|
|
262
|
+
await markPreviousSemanticNodesStale(store, source.id, spaceId, new Set([
|
|
263
|
+
...entities.map((node) => node.id),
|
|
264
|
+
...facts.map((node) => node.id),
|
|
265
|
+
...gaps.map((node) => node.id),
|
|
266
|
+
...(wikiPage ? [wikiPage.id] : []),
|
|
267
|
+
]));
|
|
260
268
|
return { source, skipped: false, extractor: semantic.extractor, facts, entities, gaps, ...(wikiPage ? { wikiPage } : {}) };
|
|
261
269
|
}
|
|
270
|
+
async function markPreviousSemanticNodesStale(store, sourceId, spaceId, activeIds) {
|
|
271
|
+
const supersededAt = Date.now();
|
|
272
|
+
const semanticNodes = store.listNodes(10_000).filter((node) => (node.sourceId === sourceId
|
|
273
|
+
&& typeof node.metadata.semanticKind === 'string'
|
|
274
|
+
&& node.status !== 'stale'
|
|
275
|
+
&& !activeIds.has(node.id)));
|
|
276
|
+
for (const node of semanticNodes) {
|
|
277
|
+
await store.upsertNode({
|
|
278
|
+
id: node.id,
|
|
279
|
+
kind: node.kind,
|
|
280
|
+
slug: node.slug,
|
|
281
|
+
title: node.title,
|
|
282
|
+
summary: node.summary,
|
|
283
|
+
aliases: node.aliases,
|
|
284
|
+
status: 'stale',
|
|
285
|
+
confidence: node.confidence,
|
|
286
|
+
...(node.sourceId ? { sourceId: node.sourceId } : {}),
|
|
287
|
+
metadata: {
|
|
288
|
+
...node.metadata,
|
|
289
|
+
supersededAt,
|
|
290
|
+
supersededInSpaceId: spaceId,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
262
295
|
async function persistWikiPage(store, source, semantic, spaceId, textHash) {
|
|
263
296
|
const markdown = semantic.wikiPage?.markdown ?? renderDeterministicWikiPage(source, semantic.facts);
|
|
264
297
|
if (!markdown.trim())
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,oBAAoB,EACrB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAC3C;AAED,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,+BAAoC;IAG1D,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAO,GAC3E,OAAO,CAAC,iCAAiC,GAAG,IAAI,CAAC;IAO9C,aAAa,CACjB,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,KAAK,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GACpG,OAAO,CAAC,SAAS,iCAAiC,EAAE,CAAC;IASlD,OAAO,CAAC,KAAK,GAAE;QACnB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC/B,GAAG,OAAO,CAAC;QACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,SAAS;YAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACnF,CAAC;IA6BI,MAAM,CAAC,KAAK,EAAE,4BAA4B,GAAG,OAAO,CAAC,6BAA6B,CAAC;CAI1F"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { answerKnowledgeQuery } from './answer.js';
|
|
2
2
|
import { enrichKnowledgeSource } from './enrichment.js';
|
|
3
|
+
import { readRecord, readString, sourceSemanticHash, sourceSemanticText } from './utils.js';
|
|
3
4
|
export class KnowledgeSemanticService {
|
|
4
5
|
store;
|
|
5
6
|
options;
|
|
@@ -37,7 +38,9 @@ export class KnowledgeSemanticService {
|
|
|
37
38
|
const errors = [];
|
|
38
39
|
for (const source of sources) {
|
|
39
40
|
try {
|
|
40
|
-
const llm = this.options.llm && llmAttempts < maxLlmSources
|
|
41
|
+
const llm = this.options.llm && llmAttempts < maxLlmSources && sourceCanUseLlmUpgrade(this.store, source)
|
|
42
|
+
? this.options.llm
|
|
43
|
+
: null;
|
|
41
44
|
if (llm)
|
|
42
45
|
llmAttempts += 1;
|
|
43
46
|
const result = await enrichKnowledgeSource({ store: this.store, llm }, source, input);
|
|
@@ -58,3 +61,12 @@ export class KnowledgeSemanticService {
|
|
|
58
61
|
return answerKnowledgeQuery({ store: this.store, llm: this.options.llm }, input);
|
|
59
62
|
}
|
|
60
63
|
}
|
|
64
|
+
function sourceCanUseLlmUpgrade(store, source) {
|
|
65
|
+
const extraction = store.getExtractionBySourceId(source.id);
|
|
66
|
+
const text = sourceSemanticText(source, extraction);
|
|
67
|
+
if (text.length < 40)
|
|
68
|
+
return false;
|
|
69
|
+
const existingSemantic = readRecord(source.metadata.semanticEnrichment);
|
|
70
|
+
return existingSemantic.textHash !== sourceSemanticHash(source, extraction)
|
|
71
|
+
|| readString(existingSemantic.extractor) !== 'llm';
|
|
72
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
let version = '0.27.
|
|
3
|
+
let version = '0.27.6';
|
|
4
4
|
try {
|
|
5
5
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
|
|
6
6
|
version = pkg.version ?? version;
|