@pellux/goodvibes-sdk 0.27.6 → 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/semantic/answer.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/answer.js +13 -17
- 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/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":"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,6 @@
|
|
|
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';
|
|
3
4
|
const GENERIC_ANSWER_INTENT_TOKENS = new Set([
|
|
4
5
|
'capabilities',
|
|
5
6
|
'capability',
|
|
@@ -53,7 +54,9 @@ export async function answerKnowledgeQuery(context, input) {
|
|
|
53
54
|
const sources = uniqueSources(evidence.flatMap((item) => item.source ? [item.source] : [])).slice(0, limit);
|
|
54
55
|
const linkedObjects = input.includeLinkedObjects === false
|
|
55
56
|
? []
|
|
56
|
-
: 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);
|
|
57
60
|
const gaps = await persistAnswerGaps(context.store, spaceId, input.query, llmAnswer?.gaps ?? []);
|
|
58
61
|
const text = llmAnswer?.answer?.trim() || renderFallbackAnswer(input.query, mode, evidence);
|
|
59
62
|
return {
|
|
@@ -260,14 +263,8 @@ function isLowValueFactForQuery(tokens, intent, fact) {
|
|
|
260
263
|
const kind = readString(fact.metadata.factKind) ?? 'note';
|
|
261
264
|
if (!['feature', 'capability', 'specification', 'compatibility', 'configuration', 'identity'].includes(kind))
|
|
262
265
|
return false;
|
|
263
|
-
const text =
|
|
264
|
-
|
|
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))
|
|
266
|
+
const text = semanticFactText(fact);
|
|
267
|
+
if (isLowValueFeatureOrSpecText(text))
|
|
271
268
|
return true;
|
|
272
269
|
const extractor = readString(fact.metadata.extractor);
|
|
273
270
|
const confidence = typeof fact.confidence === 'number' ? fact.confidence : 0;
|
|
@@ -279,12 +276,6 @@ function isLowValueFactForQuery(tokens, intent, fact) {
|
|
|
279
276
|
function hasFeatureIntent(intent) {
|
|
280
277
|
return intent.has('feature') || intent.has('capability') || intent.has('specification') || intent.has('compatibility');
|
|
281
278
|
}
|
|
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
279
|
function compareFactQuality(left, right) {
|
|
289
280
|
return factQuality(right) - factQuality(left) || left.title.localeCompare(right.title);
|
|
290
281
|
}
|
|
@@ -361,12 +352,17 @@ function sourceIdsLinkedToNodes(store, nodeIds, spaceId) {
|
|
|
361
352
|
}
|
|
362
353
|
function selectEvidenceExcerpt(query, text, facts) {
|
|
363
354
|
const tokens = expandQueryTokens(tokenizeSemanticQuery(query));
|
|
355
|
+
const intent = factIntent(tokenizeSemanticQuery(query));
|
|
356
|
+
const featureIntent = Boolean(intent && hasFeatureIntent(intent));
|
|
364
357
|
const factLines = facts
|
|
365
358
|
.map(renderFactForPrompt)
|
|
366
359
|
.filter((line) => scoreSemanticText(line, tokens) > 0)
|
|
367
360
|
.slice(0, 12);
|
|
368
|
-
const windows = evidenceWindows(text, tokens)
|
|
369
|
-
|
|
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');
|
|
370
366
|
}
|
|
371
367
|
function evidenceWindows(text, tokens) {
|
|
372
368
|
const normalized = normalizeWhitespace(text);
|
|
@@ -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,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;
|