@pellux/goodvibes-sdk 0.27.5 → 0.27.7
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/ask.js +2 -2
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +2 -1
- package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/rendering.js +5 -4
- 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 +100 -14
- 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/fact-quality.d.ts +7 -0
- package/dist/_internal/platform/knowledge/semantic/fact-quality.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/semantic/fact-quality.js +69 -0
- 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.7",
|
|
5
5
|
"operatorMethodCount": 256,
|
|
6
6
|
"operatorEventCount": 30,
|
|
7
7
|
"peerEndpointCount": 6
|
|
@@ -3,10 +3,10 @@ export async function answerHomeGraphQuery(input) {
|
|
|
3
3
|
const sources = input.results.flatMap((result) => result.source ? [result.source] : []);
|
|
4
4
|
const linkedObjects = collectLinkedObjects(input.results, input.state);
|
|
5
5
|
if (input.semanticService) {
|
|
6
|
-
|
|
6
|
+
void input.semanticService.enrichSources(uniqueSources(sources), {
|
|
7
7
|
knowledgeSpaceId: input.spaceId,
|
|
8
8
|
limit: Math.min(3, Math.max(1, sources.length)),
|
|
9
|
-
});
|
|
9
|
+
}).catch(() => { });
|
|
10
10
|
const answer = await input.semanticService.answer({
|
|
11
11
|
query: input.query.query,
|
|
12
12
|
knowledgeSpaceId: input.spaceId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated-pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/generated-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"generated-pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/generated-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA+BlD,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GACzE,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AA2ED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,6BAA6B,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CA2EhF;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAsC5E;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB5E"}
|
|
@@ -2,6 +2,7 @@ import { materializeGeneratedKnowledgeProjection, } from '../generated-projectio
|
|
|
2
2
|
import { HOME_GRAPH_CONNECTOR_ID, buildHomeGraphMetadata, edgeIsActive, homeGraphNodeId, homeGraphSourceId, isGeneratedPageSource, namespacedCanonicalUri, readRecord, uniqueStrings, } from './helpers.js';
|
|
3
3
|
import { findHomeAssistantNode, missingDevicePassportFields, readHomeGraphState, renderHomeGraphState, safeHomeGraphFilename, sourcesLinkedToNode, } from './state.js';
|
|
4
4
|
import { renderDevicePassportPage, renderPacketPage, renderRoomPage, } from './rendering.js';
|
|
5
|
+
import { isUsefulHomeGraphPageFact } from '../semantic/fact-quality.js';
|
|
5
6
|
export async function generateAutomaticHomeGraphPages(context) {
|
|
6
7
|
return generateHomeGraphPagesForCurrentState(context, context.input.pageAutomation ?? {});
|
|
7
8
|
}
|
|
@@ -286,7 +287,7 @@ function semanticFactsLinkedToSources(sources, nodes, edges) {
|
|
|
286
287
|
&& sourceIds.has(edge.fromId)
|
|
287
288
|
&& edge.toKind === 'node'
|
|
288
289
|
&& edge.relation === 'supports_fact')).map((edge) => edge.toId));
|
|
289
|
-
return nodes.filter((node) => factIds.has(node.id));
|
|
290
|
+
return nodes.filter((node) => factIds.has(node.id) && isUsefulHomeGraphPageFact(node));
|
|
290
291
|
}
|
|
291
292
|
function limitRecords(records, limit) {
|
|
292
293
|
if (typeof limit !== 'number')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EAEpB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAKrB,OAAO,KAAK,EAA6B,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EAEpB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAKrB,OAAO,KAAK,EAA6B,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGnG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,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,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAC7D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAmDnF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACzD,GAAG,MAAM,CA+BT;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GAAG,MAAM,CAkBd;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAcnH"}
|
|
@@ -2,6 +2,7 @@ import { renderKnowledgeMap } from '../map.js';
|
|
|
2
2
|
import { countFacet, normalizeStringArray, readString } from '../map-filters.js';
|
|
3
3
|
import { isUnusableHomeGraphExtractionText } from './extraction-quality.js';
|
|
4
4
|
import { edgeIsActive, isGeneratedPageSource, readRecord, uniqueStrings } from './helpers.js';
|
|
5
|
+
import { isLowValueFeatureOrSpecText, isUsefulHomeGraphPageFact } from '../semantic/fact-quality.js';
|
|
5
6
|
export function renderRoomPage(state, areaId) {
|
|
6
7
|
const area = areaId
|
|
7
8
|
? findNodeByHaId(state.nodes, 'ha_area', areaId) ?? findNodeByHaId(state.nodes, 'ha_room', areaId)
|
|
@@ -295,7 +296,7 @@ function sourceEvidenceSnippets(source, extraction, tokens, limit) {
|
|
|
295
296
|
...featureSentences(searchText),
|
|
296
297
|
source.summary,
|
|
297
298
|
source.description,
|
|
298
|
-
]).filter((entry) => !isUnusableHomeGraphExtractionText(entry)).slice(0, 80);
|
|
299
|
+
]).filter((entry) => !isUnusableHomeGraphExtractionText(entry) && !isLowValueFeatureOrSpecText(entry)).slice(0, 80);
|
|
299
300
|
const scored = candidates
|
|
300
301
|
.map((text) => ({ text: clampEvidence(text), score: evidenceScore(text, tokens) }))
|
|
301
302
|
.filter((entry) => entry.text.length > 0)
|
|
@@ -327,7 +328,7 @@ function featureSentences(value) {
|
|
|
327
328
|
const keywords = /\b(feature|features|support|supports|capability|capabilities|specification|specifications|mode|modes|hdmi|hdr|dolby|battery|reset|warranty|firmware|voice|remote)\b/i;
|
|
328
329
|
return (text.match(/[^.!?\n]+[.!?]?/g) ?? [])
|
|
329
330
|
.map((entry) => entry.trim())
|
|
330
|
-
.filter((entry) => keywords.test(entry))
|
|
331
|
+
.filter((entry) => keywords.test(entry) && !isLowValueFeatureOrSpecText(entry))
|
|
331
332
|
.slice(0, 24);
|
|
332
333
|
}
|
|
333
334
|
function evidenceScore(value, tokens) {
|
|
@@ -390,7 +391,7 @@ function renderSourceList(title, sources) {
|
|
|
390
391
|
}
|
|
391
392
|
function renderSemanticFacts(title, facts) {
|
|
392
393
|
const entries = facts
|
|
393
|
-
.filter(
|
|
394
|
+
.filter(isUsefulHomeGraphPageFact)
|
|
394
395
|
.sort((left, right) => semanticFactSortKey(left).localeCompare(semanticFactSortKey(right)) || left.title.localeCompare(right.title))
|
|
395
396
|
.slice(0, 80);
|
|
396
397
|
if (entries.length === 0)
|
|
@@ -423,7 +424,7 @@ function semanticFactsLinkedToSources(sources, nodes, edges) {
|
|
|
423
424
|
&& sourceIds.has(edge.fromId)
|
|
424
425
|
&& edge.toKind === 'node'
|
|
425
426
|
&& edge.relation === 'supports_fact')).map((edge) => edge.toId));
|
|
426
|
-
return nodes.filter((node) => factIds.has(node.id));
|
|
427
|
+
return nodes.filter((node) => factIds.has(node.id) && isUsefulHomeGraphPageFact(node));
|
|
427
428
|
}
|
|
428
429
|
function semanticFactSortKey(node) {
|
|
429
430
|
const order = {
|
|
@@ -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;
|
|
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;AAqBpB,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,CAmDxC"}
|
|
@@ -1,5 +1,30 @@
|
|
|
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
|
+
import { hasConcreteFeatureSignal, isLowValueFeatureOrSpecText, isSemanticAnswerLinkedObject, semanticFactText, } from './fact-quality.js';
|
|
4
|
+
const GENERIC_ANSWER_INTENT_TOKENS = new Set([
|
|
5
|
+
'capabilities',
|
|
6
|
+
'capability',
|
|
7
|
+
'configuration',
|
|
8
|
+
'configure',
|
|
9
|
+
'feature',
|
|
10
|
+
'features',
|
|
11
|
+
'function',
|
|
12
|
+
'functions',
|
|
13
|
+
'install',
|
|
14
|
+
'mode',
|
|
15
|
+
'modes',
|
|
16
|
+
'procedure',
|
|
17
|
+
'setting',
|
|
18
|
+
'settings',
|
|
19
|
+
'setup',
|
|
20
|
+
'spec',
|
|
21
|
+
'specification',
|
|
22
|
+
'specifications',
|
|
23
|
+
'specs',
|
|
24
|
+
'support',
|
|
25
|
+
'supported',
|
|
26
|
+
'supports',
|
|
27
|
+
]);
|
|
3
28
|
export async function answerKnowledgeQuery(context, input) {
|
|
4
29
|
const spaceId = normalizeKnowledgeSpaceId(input.knowledgeSpaceId);
|
|
5
30
|
const mode = input.mode ?? 'standard';
|
|
@@ -29,7 +54,9 @@ export async function answerKnowledgeQuery(context, input) {
|
|
|
29
54
|
const sources = uniqueSources(evidence.flatMap((item) => item.source ? [item.source] : [])).slice(0, limit);
|
|
30
55
|
const linkedObjects = input.includeLinkedObjects === false
|
|
31
56
|
? []
|
|
32
|
-
: uniqueNodes([...(input.linkedObjects ?? []), ...evidence.flatMap((item) => item.node ? [item.node] : [])])
|
|
57
|
+
: uniqueNodes([...(input.linkedObjects ?? []), ...evidence.flatMap((item) => item.node ? [item.node] : [])])
|
|
58
|
+
.filter(isSemanticAnswerLinkedObject)
|
|
59
|
+
.slice(0, 24);
|
|
33
60
|
const gaps = await persistAnswerGaps(context.store, spaceId, input.query, llmAnswer?.gaps ?? []);
|
|
34
61
|
const text = llmAnswer?.answer?.trim() || renderFallbackAnswer(input.query, mode, evidence);
|
|
35
62
|
return {
|
|
@@ -53,6 +80,7 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
53
80
|
const tokens = expandQueryTokens(tokenizeSemanticQuery(input.query));
|
|
54
81
|
if (tokens.length === 0)
|
|
55
82
|
return [];
|
|
83
|
+
const subjectTokens = tokens.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
|
|
56
84
|
const candidateSourceIds = new Set(input.candidateSourceIds ?? []);
|
|
57
85
|
const candidateNodeIds = new Set(input.candidateNodeIds ?? []);
|
|
58
86
|
const linkedObjectIds = new Set((input.linkedObjects ?? []).map((node) => node.id));
|
|
@@ -66,17 +94,28 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
66
94
|
const extraction = store.getExtractionBySourceId(source.id);
|
|
67
95
|
const facts = filterFactsForQuery(input.query, sourceFacts.get(source.id) ?? []);
|
|
68
96
|
const text = sourceSemanticText(source, extraction);
|
|
69
|
-
const
|
|
97
|
+
const scoringText = [
|
|
70
98
|
source.title,
|
|
71
99
|
source.summary,
|
|
72
100
|
source.description,
|
|
73
101
|
source.tags.join(' '),
|
|
74
102
|
text,
|
|
75
103
|
facts.map(renderFactForScoring).join(' '),
|
|
76
|
-
].join('\n')
|
|
104
|
+
].join('\n');
|
|
105
|
+
const baseScore = scoreSemanticText(scoringText, tokens);
|
|
106
|
+
const subjectScore = subjectTokens.length > 0 ? scoreSemanticText(scoringText, subjectTokens) : 0;
|
|
77
107
|
const candidateBoost = candidateSourceIds.has(source.id) ? 220 : 0;
|
|
78
108
|
const linkedBoost = linkedSourceIds.has(source.id) ? 160 : 0;
|
|
79
|
-
const
|
|
109
|
+
const genericOnly = subjectTokens.length > 0 && subjectScore === 0;
|
|
110
|
+
const weakStrictCandidate = strictCandidates
|
|
111
|
+
&& candidateSourceIds.size > 1
|
|
112
|
+
&& candidateSourceIds.has(source.id)
|
|
113
|
+
&& !linkedSourceIds.has(source.id)
|
|
114
|
+
&& genericOnly;
|
|
115
|
+
const weakBroadMatch = !strictCandidates && genericOnly;
|
|
116
|
+
const score = weakStrictCandidate || weakBroadMatch
|
|
117
|
+
? 0
|
|
118
|
+
: baseScore + candidateBoost + linkedBoost + Math.min(60, facts.length * 6);
|
|
80
119
|
return {
|
|
81
120
|
kind: 'source',
|
|
82
121
|
id: source.id,
|
|
@@ -88,18 +127,25 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
88
127
|
};
|
|
89
128
|
});
|
|
90
129
|
const nodeItems = store.listNodes(10_000)
|
|
91
|
-
.filter((node) => belongsToAnswerSpace(node, spaceId))
|
|
130
|
+
.filter((node) => belongsToAnswerSpace(node, spaceId) && node.status !== 'stale')
|
|
92
131
|
.filter((node) => !strictCandidates
|
|
93
132
|
|| candidateNodeIds.has(node.id)
|
|
94
133
|
|| linkedObjectIds.has(node.id)
|
|
95
134
|
|| (typeof node.sourceId === 'string' && candidateSourceIds.has(node.sourceId)))
|
|
96
135
|
.map((node) => {
|
|
97
|
-
const
|
|
136
|
+
const scoringText = [
|
|
98
137
|
node.title,
|
|
99
138
|
node.summary,
|
|
100
139
|
node.aliases.join(' '),
|
|
101
140
|
JSON.stringify(node.metadata),
|
|
102
|
-
].join('\n')
|
|
141
|
+
].join('\n');
|
|
142
|
+
const baseScore = scoreSemanticText(scoringText, tokens);
|
|
143
|
+
const subjectScore = subjectTokens.length > 0 ? scoreSemanticText(scoringText, subjectTokens) : 0;
|
|
144
|
+
const genericOnly = subjectTokens.length > 0 && subjectScore === 0;
|
|
145
|
+
const candidateOrLinked = candidateNodeIds.has(node.id) || linkedObjectIds.has(node.id);
|
|
146
|
+
const score = genericOnly && !candidateOrLinked
|
|
147
|
+
? 0
|
|
148
|
+
: baseScore + (candidateOrLinked ? 120 : 0) + semanticKindBoost(node);
|
|
103
149
|
return {
|
|
104
150
|
kind: 'node',
|
|
105
151
|
id: node.id,
|
|
@@ -184,10 +230,14 @@ function renderFallbackAnswer(query, mode, evidence) {
|
|
|
184
230
|
return lines.length > 0 ? lines.join('\n') : `No knowledge matched "${query}".`;
|
|
185
231
|
}
|
|
186
232
|
function filterFactsForQuery(query, facts) {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
233
|
+
const tokens = tokenizeSemanticQuery(query);
|
|
234
|
+
const intent = factIntent(tokens);
|
|
235
|
+
const matching = intent
|
|
236
|
+
? facts.filter((fact) => intent.has(readString(fact.metadata.factKind) ?? 'note'))
|
|
237
|
+
: [...facts];
|
|
238
|
+
return matching
|
|
239
|
+
.filter((fact) => fact.status !== 'stale' && !isLowValueFactForQuery(tokens, intent, fact))
|
|
240
|
+
.sort(compareFactQuality);
|
|
191
241
|
}
|
|
192
242
|
function factIntent(tokens) {
|
|
193
243
|
const tokenSet = new Set(tokens);
|
|
@@ -207,6 +257,37 @@ function factIntent(tokens) {
|
|
|
207
257
|
function hasAny(values, candidates) {
|
|
208
258
|
return candidates.some((candidate) => values.has(candidate));
|
|
209
259
|
}
|
|
260
|
+
function isLowValueFactForQuery(tokens, intent, fact) {
|
|
261
|
+
if (!intent || !hasFeatureIntent(intent))
|
|
262
|
+
return false;
|
|
263
|
+
const kind = readString(fact.metadata.factKind) ?? 'note';
|
|
264
|
+
if (!['feature', 'capability', 'specification', 'compatibility', 'configuration', 'identity'].includes(kind))
|
|
265
|
+
return false;
|
|
266
|
+
const text = semanticFactText(fact);
|
|
267
|
+
if (isLowValueFeatureOrSpecText(text))
|
|
268
|
+
return true;
|
|
269
|
+
const extractor = readString(fact.metadata.extractor);
|
|
270
|
+
const confidence = typeof fact.confidence === 'number' ? fact.confidence : 0;
|
|
271
|
+
if (extractor !== 'deterministic' || confidence > 60)
|
|
272
|
+
return false;
|
|
273
|
+
const subjectTokens = tokens.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
|
|
274
|
+
return subjectTokens.length > 0 && !hasConcreteFeatureSignal(text);
|
|
275
|
+
}
|
|
276
|
+
function hasFeatureIntent(intent) {
|
|
277
|
+
return intent.has('feature') || intent.has('capability') || intent.has('specification') || intent.has('compatibility');
|
|
278
|
+
}
|
|
279
|
+
function compareFactQuality(left, right) {
|
|
280
|
+
return factQuality(right) - factQuality(left) || left.title.localeCompare(right.title);
|
|
281
|
+
}
|
|
282
|
+
function factQuality(fact) {
|
|
283
|
+
const extractor = readString(fact.metadata.extractor);
|
|
284
|
+
const kind = readString(fact.metadata.factKind);
|
|
285
|
+
const value = readString(fact.metadata.value);
|
|
286
|
+
return (extractor === 'llm' ? 40 : 0)
|
|
287
|
+
+ (value ? 12 : 0)
|
|
288
|
+
+ (kind === 'capability' || kind === 'feature' ? 8 : kind === 'specification' ? 6 : 0)
|
|
289
|
+
+ Math.round(fact.confidence / 10);
|
|
290
|
+
}
|
|
210
291
|
async function persistAnswerGap(store, spaceId, query, reason) {
|
|
211
292
|
const node = await store.upsertNode({
|
|
212
293
|
id: `sem-answer-gap-${semanticHash(spaceId, query)}`,
|
|
@@ -245,7 +326,7 @@ async function persistAnswerGaps(store, spaceId, query, gaps) {
|
|
|
245
326
|
return nodes;
|
|
246
327
|
}
|
|
247
328
|
function buildSourceFactIndex(store, spaceId) {
|
|
248
|
-
const facts = store.listNodes(10_000).filter((node) => (node.metadata.semanticKind === 'fact' && belongsToAnswerSpace(node, spaceId)));
|
|
329
|
+
const facts = store.listNodes(10_000).filter((node) => (node.status !== 'stale' && node.metadata.semanticKind === 'fact' && belongsToAnswerSpace(node, spaceId)));
|
|
249
330
|
const bySource = new Map();
|
|
250
331
|
for (const fact of facts) {
|
|
251
332
|
const sourceId = readString(fact.metadata.sourceId) ?? fact.sourceId;
|
|
@@ -271,12 +352,17 @@ function sourceIdsLinkedToNodes(store, nodeIds, spaceId) {
|
|
|
271
352
|
}
|
|
272
353
|
function selectEvidenceExcerpt(query, text, facts) {
|
|
273
354
|
const tokens = expandQueryTokens(tokenizeSemanticQuery(query));
|
|
355
|
+
const intent = factIntent(tokenizeSemanticQuery(query));
|
|
356
|
+
const featureIntent = Boolean(intent && hasFeatureIntent(intent));
|
|
274
357
|
const factLines = facts
|
|
275
358
|
.map(renderFactForPrompt)
|
|
276
359
|
.filter((line) => scoreSemanticText(line, tokens) > 0)
|
|
277
360
|
.slice(0, 12);
|
|
278
|
-
const windows = evidenceWindows(text, tokens)
|
|
279
|
-
|
|
361
|
+
const windows = evidenceWindows(text, tokens)
|
|
362
|
+
.filter((line) => !featureIntent || !isLowValueFeatureOrSpecText(line))
|
|
363
|
+
.slice(0, 4);
|
|
364
|
+
const fallback = featureIntent && (factLines.length > 0 || windows.length > 0) ? [] : [clampText(text, 720)];
|
|
365
|
+
return uniqueStrings([...factLines, ...windows, ...fallback]).join('\n');
|
|
280
366
|
}
|
|
281
367
|
function evidenceWindows(text, tokens) {
|
|
282
368
|
const normalized = normalizeWhitespace(text);
|
|
@@ -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())
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { KnowledgeNodeRecord } from '../types.js';
|
|
2
|
+
export declare function isSemanticAnswerLinkedObject(node: KnowledgeNodeRecord): boolean;
|
|
3
|
+
export declare function semanticFactText(fact: KnowledgeNodeRecord): string;
|
|
4
|
+
export declare function isLowValueFeatureOrSpecText(text: string): boolean;
|
|
5
|
+
export declare function hasConcreteFeatureSignal(text: string): boolean;
|
|
6
|
+
export declare function isUsefulHomeGraphPageFact(fact: KnowledgeNodeRecord): boolean;
|
|
7
|
+
//# sourceMappingURL=fact-quality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-quality.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/fact-quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAcvD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAK/E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAQlE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmBjE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAa5E"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readString } from './utils.js';
|
|
2
|
+
const USEFUL_PAGE_FACT_KINDS = new Set([
|
|
3
|
+
'feature',
|
|
4
|
+
'capability',
|
|
5
|
+
'specification',
|
|
6
|
+
'identity',
|
|
7
|
+
'maintenance',
|
|
8
|
+
'compatibility',
|
|
9
|
+
'configuration',
|
|
10
|
+
'troubleshooting',
|
|
11
|
+
]);
|
|
12
|
+
export function isSemanticAnswerLinkedObject(node) {
|
|
13
|
+
if (node.status === 'stale')
|
|
14
|
+
return false;
|
|
15
|
+
const semanticKind = readString(node.metadata.semanticKind);
|
|
16
|
+
if (semanticKind)
|
|
17
|
+
return false;
|
|
18
|
+
return node.kind !== 'fact' && node.kind !== 'wiki_page' && node.kind !== 'knowledge_gap';
|
|
19
|
+
}
|
|
20
|
+
export function semanticFactText(fact) {
|
|
21
|
+
return [
|
|
22
|
+
fact.title,
|
|
23
|
+
fact.summary,
|
|
24
|
+
readString(fact.metadata.value),
|
|
25
|
+
readString(fact.metadata.evidence),
|
|
26
|
+
Array.isArray(fact.metadata.labels) ? fact.metadata.labels.join(' ') : '',
|
|
27
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
export function isLowValueFeatureOrSpecText(text) {
|
|
30
|
+
const lower = text.toLowerCase();
|
|
31
|
+
const magicRemoteDetail = /\bmagic remote\b/.test(lower) || /\bbluetooth\b/.test(lower);
|
|
32
|
+
if (/\b(items? supplied|supplied items?|included accessories|optional extras?|sold separately|separate purchase|accessories may vary|contents? of (this )?manual|may be changed|without prior notice|available menus? and options?|certified cable|unapproved items?)\b/.test(lower)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (!magicRemoteDetail && /\b(may vary|depending (upon|on) (the )?model|depending on country|depending on region)\b/.test(lower)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (/\b(fasten|screws?|stand|tip over|overturn|fall over|transporting|move the tv|moving the tv|oils?|lubricants?|cleaning cloth|power cord|electric shock|fire hazard|near water|ventilation|antenna grounding)\b/.test(lower)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (/\b(bezel|less than \d+(?:\.\d+)?\s*(mm|cm|inches?)|does not fit|will not fit|fit your tv'?s usb port|usb port may not fit)\b/.test(lower)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (/\b(warning|caution|risk|hazard|do not|never)\b/.test(lower) && !/\b(feature|supports?|hdmi|usb|hdr|remote|bluetooth)\b/.test(lower)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
export function hasConcreteFeatureSignal(text) {
|
|
50
|
+
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.toLowerCase());
|
|
51
|
+
}
|
|
52
|
+
export function isUsefulHomeGraphPageFact(fact) {
|
|
53
|
+
if (fact.status === 'stale')
|
|
54
|
+
return false;
|
|
55
|
+
if (fact.metadata.semanticKind !== 'fact')
|
|
56
|
+
return false;
|
|
57
|
+
const kind = readString(fact.metadata.factKind) ?? 'note';
|
|
58
|
+
if (!USEFUL_PAGE_FACT_KINDS.has(kind))
|
|
59
|
+
return false;
|
|
60
|
+
const text = semanticFactText(fact);
|
|
61
|
+
if (isLowValueFeatureOrSpecText(text))
|
|
62
|
+
return false;
|
|
63
|
+
const extractor = readString(fact.metadata.extractor);
|
|
64
|
+
const confidence = typeof fact.confidence === 'number' ? fact.confidence : 0;
|
|
65
|
+
if (extractor === 'deterministic' && confidence <= 60 && ['feature', 'capability', 'specification', 'compatibility', 'configuration'].includes(kind)) {
|
|
66
|
+
return hasConcreteFeatureSignal(text);
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
@@ -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.7';
|
|
4
4
|
try {
|
|
5
5
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
|
|
6
6
|
version = pkg.version ?? version;
|