@pellux/goodvibes-sdk 0.28.16 → 0.28.18
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 +70 -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.d.ts.map +1 -1
- package/dist/_internal/contracts/generated/operator-contract.js +70 -1
- package/dist/_internal/platform/artifacts/store.d.ts +2 -0
- package/dist/_internal/platform/artifacts/store.d.ts.map +1 -1
- package/dist/_internal/platform/artifacts/store.js +20 -0
- package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +9 -0
- package/dist/_internal/platform/control-plane/operator-contract-schemas-homegraph.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/operator-contract-schemas-homegraph.js +15 -1
- package/dist/_internal/platform/knowledge/home-graph/ask-page-refresh.d.ts +11 -0
- package/dist/_internal/platform/knowledge/home-graph/ask-page-refresh.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/ask-page-refresh.js +31 -0
- package/dist/_internal/platform/knowledge/home-graph/ask.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/ask.js +24 -1
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +92 -20
- package/dist/_internal/platform/knowledge/home-graph/map-view.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/map-view.js +14 -0
- package/dist/_internal/platform/knowledge/home-graph/pages.d.ts +3 -1
- package/dist/_internal/platform/knowledge/home-graph/pages.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/pages.js +104 -1
- package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts +0 -1
- package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/rendering.js +0 -12
- package/dist/_internal/platform/knowledge/home-graph/reset.d.ts +2 -1
- package/dist/_internal/platform/knowledge/home-graph/reset.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/reset.js +35 -2
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.js +2 -18
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts +1 -1
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/service.js +17 -5
- package/dist/_internal/platform/knowledge/home-graph/source-links.d.ts +3 -0
- package/dist/_internal/platform/knowledge/home-graph/source-links.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/source-links.js +30 -0
- package/dist/_internal/platform/knowledge/home-graph/state.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/state.js +43 -1
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts +39 -1
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/ingest-compile.js +1 -1
- package/dist/_internal/platform/knowledge/pdf-extractor.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/pdf-extractor.js +8 -3
- package/dist/_internal/platform/knowledge/semantic/answer-fallback.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/answer-fallback.js +21 -16
- package/dist/_internal/platform/knowledge/semantic/answer-source-ranking.d.ts +7 -0
- package/dist/_internal/platform/knowledge/semantic/answer-source-ranking.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/semantic/answer-source-ranking.js +57 -0
- package/dist/_internal/platform/knowledge/semantic/answer.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/answer.js +76 -9
- package/dist/_internal/platform/knowledge/semantic/fact-quality.d.ts +0 -1
- package/dist/_internal/platform/knowledge/semantic/fact-quality.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/fact-quality.js +18 -18
- package/dist/_internal/platform/knowledge/semantic/homeassistant-scope.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/homeassistant-scope.js +63 -5
- package/dist/_internal/platform/knowledge/semantic/repair-profile.d.ts +16 -0
- package/dist/_internal/platform/knowledge/semantic/repair-profile.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/semantic/repair-profile.js +152 -0
- package/dist/_internal/platform/knowledge/semantic/repair-subjects.d.ts +16 -0
- package/dist/_internal/platform/knowledge/semantic/repair-subjects.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/semantic/repair-subjects.js +52 -0
- package/dist/_internal/platform/knowledge/semantic/self-improvement-promotion.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/self-improvement-promotion.js +122 -57
- package/dist/_internal/platform/knowledge/semantic/self-improvement.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/semantic/self-improvement.js +40 -13
- package/dist/_internal/platform/knowledge/semantic/service.js +1 -1
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +2 -1
|
@@ -94,7 +94,7 @@ export async function recompileKnowledgeSource(context, source) {
|
|
|
94
94
|
function extractionNeedsRefresh(extraction) {
|
|
95
95
|
if (!extraction)
|
|
96
96
|
return true;
|
|
97
|
-
if (extraction.format === 'pdf' && extraction.extractorId
|
|
97
|
+
if (extraction.format === 'pdf' && extraction.extractorId === 'pdf')
|
|
98
98
|
return true;
|
|
99
99
|
const searchText = readSearchText(extraction.structure) ?? readSearchText(extraction.metadata);
|
|
100
100
|
if (searchText)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pdf-extractor.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/knowledge/pdf-extractor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAgEjE,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,
|
|
1
|
+
{"version":3,"file":"pdf-extractor.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/knowledge/pdf-extractor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAgEjE,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAMnF"}
|
|
@@ -63,7 +63,10 @@ export async function extractPdf(buffer) {
|
|
|
63
63
|
const parsed = await extractPdfWithPdfJs(buffer);
|
|
64
64
|
if (parsed)
|
|
65
65
|
return parsed;
|
|
66
|
-
|
|
66
|
+
const raw = extractPdfRawStreams(buffer);
|
|
67
|
+
if (raw)
|
|
68
|
+
return raw;
|
|
69
|
+
throw new Error('PDF extraction failed: no readable text was extracted. OCR or a dedicated PDF provider may be required.');
|
|
67
70
|
}
|
|
68
71
|
async function extractPdfWithPdfJs(buffer) {
|
|
69
72
|
try {
|
|
@@ -148,11 +151,13 @@ function extractPdfRawStreams(buffer) {
|
|
|
148
151
|
const combined = uniqueStrings(texts, 64).join('\n');
|
|
149
152
|
const searchable = uniqueStrings(texts, 512).join('\n');
|
|
150
153
|
const searchText = searchTextPayload(searchable);
|
|
154
|
+
if (!searchText)
|
|
155
|
+
return undefined;
|
|
151
156
|
return {
|
|
152
|
-
extractorId: 'pdf',
|
|
157
|
+
extractorId: 'pdf-raw',
|
|
153
158
|
format: 'pdf',
|
|
154
159
|
title: firstNonEmptyLine(combined) ?? 'PDF document',
|
|
155
|
-
summary: summarizeText(combined) ?? 'PDF
|
|
160
|
+
summary: summarizeText(combined) ?? 'PDF document text extracted from raw streams.',
|
|
156
161
|
excerpt: excerptText(combined),
|
|
157
162
|
sections: uniqueStrings(combined.split(/\n+/), 8),
|
|
158
163
|
links: uniqueStrings(Array.from(combined.matchAll(/\bhttps?:\/\/[^\s)]+/g), (linkMatch) => linkMatch[0]), 50),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer-fallback.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"answer-fallback.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIvD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,SAAS,sBAAsB,EAAE,EAC3C,KAAK,EAAE,SAAS,mBAAmB,EAAE,GACpC,cAAc,CAuBhB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isLowValueFeatureOrSpecText } from './fact-quality.js';
|
|
1
2
|
import { clampText, normalizeWhitespace, readString } from './utils.js';
|
|
2
3
|
export function renderFallbackAnswer(query, mode, evidence, facts) {
|
|
3
4
|
const factLimit = mode === 'detailed' ? 12 : mode === 'concise' ? 3 : 8;
|
|
@@ -5,17 +6,17 @@ export function renderFallbackAnswer(query, mode, evidence, facts) {
|
|
|
5
6
|
if (factPhrases.length > 0) {
|
|
6
7
|
return {
|
|
7
8
|
synthesized: true,
|
|
8
|
-
text: `
|
|
9
|
+
text: `Available source-backed details include ${joinFactPhrases(factPhrases)}.`,
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
|
-
const
|
|
12
|
-
.slice(0, mode === 'detailed' ?
|
|
13
|
-
.map(
|
|
12
|
+
const sourceTitles = evidence
|
|
13
|
+
.slice(0, mode === 'detailed' ? 6 : mode === 'concise' ? 2 : 4)
|
|
14
|
+
.map((item) => normalizeWhitespace(item.title))
|
|
14
15
|
.filter(Boolean);
|
|
15
|
-
if (
|
|
16
|
+
if (sourceTitles.length > 0) {
|
|
16
17
|
return {
|
|
17
|
-
synthesized:
|
|
18
|
-
text: `
|
|
18
|
+
synthesized: false,
|
|
19
|
+
text: `I found matching sources (${joinFactPhrases(sourceTitles)}), but they have not produced enough source-backed facts to answer "${query}" yet.`,
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
return {
|
|
@@ -26,9 +27,19 @@ export function renderFallbackAnswer(query, mode, evidence, facts) {
|
|
|
26
27
|
function renderFactPhrase(fact) {
|
|
27
28
|
const value = readString(fact.metadata.value);
|
|
28
29
|
const summary = fact.summary ?? readString(fact.metadata.evidence);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const phrase = value ? `${fact.title}: ${value}` : summary ? `${fact.title}: ${summary}` : fact.title;
|
|
31
|
+
const cleaned = normalizeWhitespace(clampText(phrase, 220));
|
|
32
|
+
if (isRawSourceFragment(cleaned))
|
|
33
|
+
return '';
|
|
34
|
+
return isLowValueFeatureOrSpecText(cleaned) ? '' : cleaned;
|
|
35
|
+
}
|
|
36
|
+
function isRawSourceFragment(value) {
|
|
37
|
+
const lower = value.toLowerCase();
|
|
38
|
+
return /\bsemantic-gap-repair\b/.test(lower)
|
|
39
|
+
|| /\bsource-backed facts identify\b/.test(lower)
|
|
40
|
+
|| /\b(manual\.nz|current page|loading)\b/.test(lower)
|
|
41
|
+
|| /\b[a-z0-9-]+\.(com|net|org|io|dev|tv|ca)\/[a-z0-9/_?=&.#-]+/.test(lower)
|
|
42
|
+
|| /\b[a-z]{2}\/[a-z0-9/_-]+\/[a-z0-9._-]+/.test(lower);
|
|
32
43
|
}
|
|
33
44
|
function joinFactPhrases(phrases) {
|
|
34
45
|
if (phrases.length <= 1)
|
|
@@ -37,9 +48,3 @@ function joinFactPhrases(phrases) {
|
|
|
37
48
|
return `${phrases[0]} and ${phrases[1]}`;
|
|
38
49
|
return `${phrases.slice(0, -1).join('; ')}; and ${phrases[phrases.length - 1]}`;
|
|
39
50
|
}
|
|
40
|
-
function renderEvidencePhrase(item) {
|
|
41
|
-
const excerpt = normalizeWhitespace(item.excerpt ?? '');
|
|
42
|
-
if (!excerpt)
|
|
43
|
-
return item.title;
|
|
44
|
-
return `${item.title}: ${clampText(excerpt, 260)}`;
|
|
45
|
-
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
|
|
2
|
+
export interface AnswerSourceRankingEvidence {
|
|
3
|
+
readonly score: number;
|
|
4
|
+
readonly source?: KnowledgeSourceRecord;
|
|
5
|
+
}
|
|
6
|
+
export declare function rankAnswerSources(evidence: readonly AnswerSourceRankingEvidence[], facts: readonly KnowledgeNodeRecord[]): KnowledgeSourceRecord[];
|
|
7
|
+
//# sourceMappingURL=answer-source-ranking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer-source-ranking.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer-source-ranking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAG9E,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;CACzC;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,2BAA2B,EAAE,EAChD,KAAK,EAAE,SAAS,mBAAmB,EAAE,GACpC,qBAAqB,EAAE,CAwBzB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readRecord, readString } from './utils.js';
|
|
2
|
+
export function rankAnswerSources(evidence, facts) {
|
|
3
|
+
const evidenceScoreBySource = new Map();
|
|
4
|
+
for (const item of evidence) {
|
|
5
|
+
if (!item.source)
|
|
6
|
+
continue;
|
|
7
|
+
evidenceScoreBySource.set(item.source.id, Math.max(evidenceScoreBySource.get(item.source.id) ?? 0, item.score));
|
|
8
|
+
}
|
|
9
|
+
const factCountBySource = new Map();
|
|
10
|
+
const promotedFactCountBySource = new Map();
|
|
11
|
+
for (const fact of facts) {
|
|
12
|
+
const sourceId = readString(fact.metadata.sourceId) ?? fact.sourceId;
|
|
13
|
+
if (!sourceId)
|
|
14
|
+
continue;
|
|
15
|
+
factCountBySource.set(sourceId, (factCountBySource.get(sourceId) ?? 0) + 1);
|
|
16
|
+
if (readString(fact.metadata.extractor) === 'repair-promotion') {
|
|
17
|
+
promotedFactCountBySource.set(sourceId, (promotedFactCountBySource.get(sourceId) ?? 0) + 1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const sources = uniqueSources(evidence.flatMap((item) => item.source ? [item.source] : []));
|
|
21
|
+
const realSources = sources.filter((source) => source.metadata.homeGraphGeneratedPage !== true);
|
|
22
|
+
return (realSources.length > 0 ? realSources : sources)
|
|
23
|
+
.sort((left, right) => (sourceAnswerQuality(right, evidenceScoreBySource, factCountBySource, promotedFactCountBySource)
|
|
24
|
+
- sourceAnswerQuality(left, evidenceScoreBySource, factCountBySource, promotedFactCountBySource)
|
|
25
|
+
|| left.id.localeCompare(right.id)));
|
|
26
|
+
}
|
|
27
|
+
function sourceAnswerQuality(source, evidenceScoreBySource, factCountBySource, promotedFactCountBySource) {
|
|
28
|
+
const discovery = readRecord(source.metadata.sourceDiscovery);
|
|
29
|
+
const rank = typeof discovery.sourceRank === 'number' ? Math.max(0, 12 - discovery.sourceRank) : 0;
|
|
30
|
+
return Math.round((evidenceScoreBySource.get(source.id) ?? 0) / 4)
|
|
31
|
+
+ sourceAuthorityBoost(source)
|
|
32
|
+
+ Math.min(80, (factCountBySource.get(source.id) ?? 0) * 10)
|
|
33
|
+
+ Math.min(90, (promotedFactCountBySource.get(source.id) ?? 0) * 18)
|
|
34
|
+
+ rank * 4
|
|
35
|
+
+ (source.status === 'indexed' ? 12 : source.status === 'pending' ? 2 : 0)
|
|
36
|
+
- (source.metadata.homeGraphGeneratedPage === true ? 90 : 0);
|
|
37
|
+
}
|
|
38
|
+
function sourceAuthorityBoost(source) {
|
|
39
|
+
const discovery = readRecord(source.metadata.sourceDiscovery);
|
|
40
|
+
const text = `${readString(discovery.trustReason) ?? ''} ${readString(discovery.sourceDomain) ?? ''} ${source.sourceUri ?? ''} ${source.canonicalUri ?? ''}`.toLowerCase();
|
|
41
|
+
if (/\bofficial-vendor-domain\b/.test(text) || /(^|[/.])(?:lg|sony|samsung|apple)\.com\b/.test(text))
|
|
42
|
+
return 140;
|
|
43
|
+
if (/\bmanufacturer-domain\b/.test(text))
|
|
44
|
+
return 80;
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
function uniqueSources(values) {
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
const result = [];
|
|
50
|
+
for (const source of values) {
|
|
51
|
+
if (seen.has(source.id))
|
|
52
|
+
continue;
|
|
53
|
+
seen.add(source.id);
|
|
54
|
+
result.push(source);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
@@ -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;AAYlD,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;AAYlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAE7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAiCpB,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAyCD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,6BAA6B,CAAC,CA+FxC"}
|
|
@@ -6,11 +6,14 @@ import { concreteAnswerGapSpaceId } from './answer-space.js';
|
|
|
6
6
|
import { answerNeedsFeatureGap, cleanSynthesizedAnswer } from './answer-quality.js';
|
|
7
7
|
import { clampTimeoutMs, withTimeoutOrNull } from './timeouts.js';
|
|
8
8
|
import { renderFallbackAnswer } from './answer-fallback.js';
|
|
9
|
+
import { rankAnswerSources } from './answer-source-ranking.js';
|
|
10
|
+
import { canonicalRepairSubjectNodes } from './repair-subjects.js';
|
|
9
11
|
const GENERIC_ANSWER_INTENT_TOKENS = new Set([
|
|
10
12
|
'capabilities',
|
|
11
13
|
'capability',
|
|
12
14
|
'configuration',
|
|
13
15
|
'configure',
|
|
16
|
+
'device',
|
|
14
17
|
'feature',
|
|
15
18
|
'features',
|
|
16
19
|
'function',
|
|
@@ -26,9 +29,11 @@ const GENERIC_ANSWER_INTENT_TOKENS = new Set([
|
|
|
26
29
|
'specification',
|
|
27
30
|
'specifications',
|
|
28
31
|
'specs',
|
|
32
|
+
'object',
|
|
29
33
|
'support',
|
|
30
34
|
'supported',
|
|
31
35
|
'supports',
|
|
36
|
+
'thing',
|
|
32
37
|
]);
|
|
33
38
|
export async function answerKnowledgeQuery(context, input) {
|
|
34
39
|
const spaceId = normalizeKnowledgeSpaceId(input.knowledgeSpaceId);
|
|
@@ -60,14 +65,25 @@ export async function answerKnowledgeQuery(context, input) {
|
|
|
60
65
|
}
|
|
61
66
|
const llmAnswer = await synthesizeAnswer(context.llm ?? null, input.query, mode, evidence, input.timeoutMs);
|
|
62
67
|
const facts = filterFactsForQuery(input.query, uniqueNodes(evidence.flatMap((item) => item.facts))).slice(0, 24);
|
|
63
|
-
const sources =
|
|
68
|
+
const sources = rankAnswerSources(evidence, facts)
|
|
64
69
|
.slice(0, limit)
|
|
65
70
|
.map(withAnswerSourceAliases);
|
|
66
|
-
const
|
|
71
|
+
const inferredHomeAssistantLinkedObjects = input.includeLinkedObjects === false
|
|
67
72
|
? []
|
|
68
|
-
:
|
|
73
|
+
: inferHomeAssistantLinkedObjects(context.store, spaceId, input.query);
|
|
74
|
+
const evidenceLinkedObjects = shouldUseEvidenceLinkedObjects(spaceId, input, inferredHomeAssistantLinkedObjects)
|
|
75
|
+
? evidence.flatMap((item) => item.node ? [item.node] : [])
|
|
76
|
+
: [];
|
|
77
|
+
const rawLinkedObjects = input.includeLinkedObjects === false
|
|
78
|
+
? []
|
|
79
|
+
: uniqueNodes([
|
|
80
|
+
...(input.linkedObjects ?? []),
|
|
81
|
+
...evidenceLinkedObjects,
|
|
82
|
+
...inferredHomeAssistantLinkedObjects,
|
|
83
|
+
])
|
|
69
84
|
.filter(isSemanticAnswerLinkedObject)
|
|
70
85
|
.slice(0, 24);
|
|
86
|
+
const linkedObjects = filterAnswerLinkedObjects(spaceId, input.query, rawLinkedObjects);
|
|
71
87
|
const gapSpaceId = concreteAnswerGapSpaceId(spaceId, evidence, sources, linkedObjects);
|
|
72
88
|
const gaps = await persistAnswerGaps(context.store, gapSpaceId, input.query, llmAnswer?.gaps ?? [], {
|
|
73
89
|
sources,
|
|
@@ -86,8 +102,17 @@ export async function answerKnowledgeQuery(context, input) {
|
|
|
86
102
|
linkedObjects,
|
|
87
103
|
})
|
|
88
104
|
: null;
|
|
89
|
-
const
|
|
90
|
-
const
|
|
105
|
+
const llmText = llmAnswer?.answer?.trim();
|
|
106
|
+
const cleanedLlmText = llmText ? cleanSynthesizedAnswer(llmText, featureIntent) : undefined;
|
|
107
|
+
const dropLowValueLlmAnswer = featureIntent && Boolean(cleanedLlmText) && isLowValueFeatureOrSpecText(cleanedLlmText ?? '');
|
|
108
|
+
const fallback = !llmText || dropLowValueLlmAnswer
|
|
109
|
+
? renderFallbackAnswer(input.query, mode, evidence, facts)
|
|
110
|
+
: null;
|
|
111
|
+
const synthesizedAnswer = dropLowValueLlmAnswer ? undefined : cleanedLlmText;
|
|
112
|
+
const text = synthesizedAnswer || cleanSynthesizedAnswer(fallback?.text || '', featureIntent);
|
|
113
|
+
const confidence = input.includeConfidence === false
|
|
114
|
+
? 0
|
|
115
|
+
: dropLowValueLlmAnswer ? 0 : answerConfidence(llmAnswer, evidence);
|
|
91
116
|
return {
|
|
92
117
|
ok: true,
|
|
93
118
|
spaceId,
|
|
@@ -95,12 +120,12 @@ export async function answerKnowledgeQuery(context, input) {
|
|
|
95
120
|
answer: {
|
|
96
121
|
text,
|
|
97
122
|
mode,
|
|
98
|
-
confidence
|
|
123
|
+
confidence,
|
|
99
124
|
sources: input.includeSources === false ? [] : sources,
|
|
100
125
|
linkedObjects,
|
|
101
126
|
facts,
|
|
102
127
|
gaps: evidenceGap ? uniqueNodes([...gaps, evidenceGap]) : gaps,
|
|
103
|
-
synthesized: Boolean(
|
|
128
|
+
synthesized: Boolean(synthesizedAnswer) || Boolean(fallback?.synthesized),
|
|
104
129
|
},
|
|
105
130
|
results: evidence.map(toSearchResult),
|
|
106
131
|
};
|
|
@@ -123,7 +148,7 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
123
148
|
const sourceItems = store.listSources(10_000)
|
|
124
149
|
.filter((source) => belongsToAnswerSpace(source, spaceId))
|
|
125
150
|
.filter((source) => sourceInHomeAssistantAnswerScope(store, source, homeAssistantScope))
|
|
126
|
-
.filter((source) => !strictCandidates || candidateSourceIds.has(source.id) ||
|
|
151
|
+
.filter((source) => !strictCandidates || candidateSourceIds.has(source.id) || linkedSourceIds.has(source.id))
|
|
127
152
|
.map((source) => {
|
|
128
153
|
const extraction = store.getExtractionBySourceId(source.id);
|
|
129
154
|
const facts = filterFactsForQuery(input.query, sourceFacts.get(source.id) ?? []);
|
|
@@ -198,6 +223,36 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
|
|
|
198
223
|
.sort((left, right) => right.score - left.score || left.id.localeCompare(right.id));
|
|
199
224
|
return pruneEvidence(items, limit, broadHomeAssistantAlias);
|
|
200
225
|
}
|
|
226
|
+
function inferHomeAssistantLinkedObjects(store, spaceId, query) {
|
|
227
|
+
const tokens = expandQueryTokens(tokenizeSemanticQuery(query));
|
|
228
|
+
const subjectTokens = tokens.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
|
|
229
|
+
const scope = inferHomeAssistantAnswerScope(store, spaceId, query, subjectTokens);
|
|
230
|
+
if (!scope || scope.anchorNodeIds.size === 0)
|
|
231
|
+
return [];
|
|
232
|
+
return store.listNodes(10_000)
|
|
233
|
+
.filter((node) => scope.anchorNodeIds.has(node.id))
|
|
234
|
+
.filter((node) => belongsToAnswerSpace(node, spaceId))
|
|
235
|
+
.filter(isSemanticAnswerLinkedObject);
|
|
236
|
+
}
|
|
237
|
+
function filterAnswerLinkedObjects(spaceId, query, nodes) {
|
|
238
|
+
const normalized = normalizeKnowledgeSpaceId(spaceId);
|
|
239
|
+
if (normalized !== 'homeassistant' && !isHomeAssistantKnowledgeSpace(normalized))
|
|
240
|
+
return nodes;
|
|
241
|
+
const canonical = canonicalRepairSubjectNodes({ nodes, text: query });
|
|
242
|
+
if (canonical.length > 0)
|
|
243
|
+
return canonical.slice(0, 24);
|
|
244
|
+
const integrationIntent = /\b(integration|platform|add-?on|addon|plugin|service|api|setup|configure|configuration|auth|credential|rate limit)\b/i.test(query);
|
|
245
|
+
return nodes.filter((node) => integrationIntent || node.kind !== 'ha_integration').slice(0, 24);
|
|
246
|
+
}
|
|
247
|
+
function shouldUseEvidenceLinkedObjects(spaceId, input, inferredHomeAssistantLinkedObjects) {
|
|
248
|
+
if (input.linkedObjects?.length)
|
|
249
|
+
return true;
|
|
250
|
+
const normalized = normalizeKnowledgeSpaceId(spaceId);
|
|
251
|
+
if (normalized === 'homeassistant' || isHomeAssistantKnowledgeSpace(normalized)) {
|
|
252
|
+
return inferredHomeAssistantLinkedObjects.length > 0;
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
201
256
|
async function synthesizeAnswer(llm, query, mode, evidence, requestedTimeoutMs) {
|
|
202
257
|
if (!llm)
|
|
203
258
|
return null;
|
|
@@ -465,13 +520,25 @@ function sourceIdsLinkedToNodes(store, nodeIds, spaceId) {
|
|
|
465
520
|
const sourceIds = new Set();
|
|
466
521
|
if (nodeIds.size === 0)
|
|
467
522
|
return sourceIds;
|
|
468
|
-
|
|
523
|
+
const edges = store.listEdges();
|
|
524
|
+
const factIds = new Set();
|
|
525
|
+
for (const edge of edges) {
|
|
469
526
|
if (!belongsToAnswerSpace(edge, spaceId))
|
|
470
527
|
continue;
|
|
471
528
|
if (edge.fromKind === 'source' && edge.toKind === 'node' && nodeIds.has(edge.toId))
|
|
472
529
|
sourceIds.add(edge.fromId);
|
|
473
530
|
if (edge.fromKind === 'node' && nodeIds.has(edge.fromId) && edge.toKind === 'source')
|
|
474
531
|
sourceIds.add(edge.toId);
|
|
532
|
+
if (edge.fromKind === 'node' && edge.toKind === 'node' && nodeIds.has(edge.toId) && edge.relation === 'describes') {
|
|
533
|
+
factIds.add(edge.fromId);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
for (const edge of edges) {
|
|
537
|
+
if (!belongsToAnswerSpace(edge, spaceId))
|
|
538
|
+
continue;
|
|
539
|
+
if (edge.fromKind === 'source' && edge.toKind === 'node' && factIds.has(edge.toId) && edge.relation === 'supports_fact') {
|
|
540
|
+
sourceIds.add(edge.fromId);
|
|
541
|
+
}
|
|
475
542
|
}
|
|
476
543
|
return sourceIds;
|
|
477
544
|
}
|
|
@@ -3,6 +3,5 @@ export declare function isSemanticAnswerLinkedObject(node: KnowledgeNodeRecord):
|
|
|
3
3
|
export declare function semanticFactText(fact: KnowledgeNodeRecord): string;
|
|
4
4
|
export declare function isLowValueFeatureOrSpecText(text: string): boolean;
|
|
5
5
|
export declare function hasConcreteFeatureSignal(text: string): boolean;
|
|
6
|
-
export declare function isUsefulHomeGraphSourceBackedNote(text: string): boolean;
|
|
7
6
|
export declare function isUsefulHomeGraphPageFact(fact: KnowledgeNodeRecord): boolean;
|
|
8
7
|
//# sourceMappingURL=fact-quality.d.ts.map
|
|
@@ -1 +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,
|
|
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,CA0HjE;AAkBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAiB5E"}
|
|
@@ -29,6 +29,14 @@ export function semanticFactText(fact) {
|
|
|
29
29
|
export function isLowValueFeatureOrSpecText(text) {
|
|
30
30
|
const lower = text.toLowerCase();
|
|
31
31
|
const magicRemoteDetail = /\bmagic remote\b/.test(lower) || /\bbluetooth\b/.test(lower);
|
|
32
|
+
if (/\?\s*$/.test(text.trim()))
|
|
33
|
+
return true;
|
|
34
|
+
if (/\bsemantic-gap-repair\b/.test(lower))
|
|
35
|
+
return true;
|
|
36
|
+
if (isUrlOrPathFragment(lower) && !hasConcreteFeatureSignal(lower))
|
|
37
|
+
return true;
|
|
38
|
+
if (isUrlOrPathFragment(lower) && /\b(source-backed facts identify|current page|database|manual\.nz|loading|semantic-gap-repair)\b/.test(lower))
|
|
39
|
+
return true;
|
|
32
40
|
if (isTruncatedManualFragment(lower))
|
|
33
41
|
return true;
|
|
34
42
|
if (/\b(items? supplied|supplied items?|included accessories|optional extras?|sold separately|separate purchase|accessories may vary|contents? of (this )?manual|may be changed|may change|subject to change|without prior notice|available menus? and options?|certified cable|unapproved items?)\b/.test(lower)) {
|
|
@@ -42,7 +50,10 @@ export function isLowValueFeatureOrSpecText(text) {
|
|
|
42
50
|
}
|
|
43
51
|
if (/^\s*\d+\s*(yes|no)\b/.test(lower)
|
|
44
52
|
|| /^\s*\d+(hdmi|usb|audio|ports?|features?|smart)\b/.test(lower)
|
|
45
|
-
||
|
|
53
|
+
|| /^\s*0\s+ports\b/.test(lower)
|
|
54
|
+
|| /\b\d+\s+features such as\b/.test(lower)
|
|
55
|
+
|| /\b(case color|hardware cpu cores|hardware gpu cores)\b/.test(lower)
|
|
56
|
+
|| /^\s*\d+\s*kg\d*/.test(lower)) {
|
|
46
57
|
return true;
|
|
47
58
|
}
|
|
48
59
|
if (/\b(button|buttons|remote control)\b/.test(lower) && /[\u25b2\u25bc\u25c4\u25ba]|[▲▼◄►]|\\u25/.test(text)) {
|
|
@@ -143,6 +154,12 @@ export function isLowValueFeatureOrSpecText(text) {
|
|
|
143
154
|
}
|
|
144
155
|
return false;
|
|
145
156
|
}
|
|
157
|
+
function isUrlOrPathFragment(value) {
|
|
158
|
+
return /https?:\/\//.test(value)
|
|
159
|
+
|| /\b[a-z0-9-]+\.(com|net|org|io|dev|tv|ca|co\.uk)\/[a-z0-9/_?=&.#-]+/.test(value)
|
|
160
|
+
|| /\b[a-z]{2}\/[a-z0-9/_-]+\/[a-z0-9._-]+/.test(value)
|
|
161
|
+
|| /\b[a-z0-9._-]+\/(specifications?|manuals?|products?|support|features?)\/[a-z0-9._-]+/.test(value);
|
|
162
|
+
}
|
|
146
163
|
function isTruncatedManualFragment(value) {
|
|
147
164
|
const trimmed = value.trim();
|
|
148
165
|
if (trimmed.length === 0)
|
|
@@ -156,23 +173,6 @@ function isTruncatedManualFragment(value) {
|
|
|
156
173
|
export function hasConcreteFeatureSignal(text) {
|
|
157
174
|
return /\b(hdmi|usb|hdr|hdr10|dolby|vision|earc|arc|bluetooth|wi-?fi|wireless lan|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|atsc|ntsc|qam|tuner|broadcast|rs-?232c|external control)\b/.test(text.toLowerCase());
|
|
158
175
|
}
|
|
159
|
-
export function isUsefulHomeGraphSourceBackedNote(text) {
|
|
160
|
-
if (isLowValueFeatureOrSpecText(text))
|
|
161
|
-
return false;
|
|
162
|
-
const lower = text.toLowerCase();
|
|
163
|
-
if (/\b(magic remote|remote control)\b/.test(lower))
|
|
164
|
-
return false;
|
|
165
|
-
const signalCount = [
|
|
166
|
-
/\b(hdmi|usb|ethernet|wi-?fi|bluetooth|airplay|miracast|chromecast)\b/,
|
|
167
|
-
/\b(hdr|hdr10|dolby vision|dolby atmos|filmmaker|game optimizer|freesync|g-sync|allm|vrr)\b/,
|
|
168
|
-
/\b(4k|8k|uhd|resolution|refresh rate|120\s*hz|60\s*hz|nanocell|led|lcd|display|screen)\b/,
|
|
169
|
-
/\b(tuner|atsc|qam|ntsc|codec|mpeg|dolby digital|audio formats?|video formats?)\b/,
|
|
170
|
-
/\b(ports?|speaker system|speaker power|channels?|arc|earc)\b/,
|
|
171
|
-
].filter((pattern) => pattern.test(lower)).length;
|
|
172
|
-
return signalCount >= 2
|
|
173
|
-
|| /\b(supported external devices|supports? .* (hdr|hdmi|usb|wi-?fi|bluetooth|earc|arc|4k|120\s*hz))\b/.test(lower)
|
|
174
|
-
|| /\b(dtv audio supported codec|supported codecs?|supported audio formats?|supported video formats?|supported picture formats?)\b/.test(lower);
|
|
175
|
-
}
|
|
176
176
|
export function isUsefulHomeGraphPageFact(fact) {
|
|
177
177
|
if (fact.status === 'stale')
|
|
178
178
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"homeassistant-scope.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/homeassistant-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAI9E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,wBAAwB,GAAG,IAAI,CAIjC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,wBAAwB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"homeassistant-scope.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/homeassistant-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAI9E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,wBAAwB,GAAG,IAAI,CAIjC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,wBAAwB,GAAG,IAAI,CAgCjC;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,wBAAwB,GAAG,IAAI,GACrC,OAAO,CAMT;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,GAAG,IAAI,GACrC,OAAO,CAKT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getKnowledgeSpaceId, isHomeAssistantKnowledgeSpace, normalizeKnowledgeSpaceId } from '../spaces.js';
|
|
2
|
-
import { readRecord, readString, scoreSemanticText, sourceSemanticText, tokenizeSemanticQuery } from './utils.js';
|
|
2
|
+
import { readRecord, readString, scoreSemanticText, sourceSemanticText, tokenizeSemanticQuery, uniqueStrings } from './utils.js';
|
|
3
3
|
export function inferHomeAssistantAnswerScopeForQuery(store, spaceId, query) {
|
|
4
4
|
const subjectTokens = tokenizeSemanticQuery(query)
|
|
5
5
|
.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
|
|
@@ -9,19 +9,23 @@ export function inferHomeAssistantAnswerScope(store, spaceId, query, subjectToke
|
|
|
9
9
|
const normalized = normalizeKnowledgeSpaceId(spaceId);
|
|
10
10
|
if (normalized !== 'homeassistant' && !isHomeAssistantKnowledgeSpace(normalized))
|
|
11
11
|
return null;
|
|
12
|
-
|
|
12
|
+
const queryTokens = tokenizeSemanticQuery(query);
|
|
13
|
+
if (subjectTokens.length === 0 && queryTokens.length === 0)
|
|
13
14
|
return null;
|
|
14
|
-
const singularObjectQuery = isSingularObjectQuery(query,
|
|
15
|
+
const singularObjectQuery = isSingularObjectQuery(query, queryTokens);
|
|
15
16
|
const strongIdentityTokens = subjectTokens.filter(isStrongIdentityToken);
|
|
16
17
|
if (!singularObjectQuery && strongIdentityTokens.length === 0)
|
|
17
18
|
return null;
|
|
19
|
+
if (singularObjectQuery && strongIdentityTokens.length === 0 && !hasSpecificObjectTypeToken(queryTokens))
|
|
20
|
+
return null;
|
|
18
21
|
const linkedSourceQualityByNode = linkedSourceQualityByAnchor(store);
|
|
22
|
+
const anchorTokens = uniqueStrings([...subjectTokens, ...queryTokens]);
|
|
19
23
|
const anchors = store.listNodes(10_000)
|
|
20
24
|
.filter((node) => node.status !== 'stale' && isHomeAssistantObjectNode(node))
|
|
21
25
|
.filter((node) => normalized === 'homeassistant' || normalizeKnowledgeSpaceId(getKnowledgeSpaceId(node)) === normalized)
|
|
22
26
|
.map((node) => ({
|
|
23
27
|
node,
|
|
24
|
-
score: scoreHomeAssistantObject(node,
|
|
28
|
+
score: scoreHomeAssistantObject(node, anchorTokens)
|
|
25
29
|
+ Math.min(64, linkedSourceQualityByNode.get(node.id) ?? 0),
|
|
26
30
|
}))
|
|
27
31
|
.filter((entry) => entry.score > 0)
|
|
@@ -58,11 +62,21 @@ function sourceIdsLinkedToAnchors(store, anchorNodeIds) {
|
|
|
58
62
|
const sourceIds = new Set();
|
|
59
63
|
if (anchorNodeIds.size === 0)
|
|
60
64
|
return sourceIds;
|
|
61
|
-
|
|
65
|
+
const edges = store.listEdges();
|
|
66
|
+
const factIds = new Set();
|
|
67
|
+
for (const edge of edges) {
|
|
62
68
|
if (edge.fromKind === 'source' && edge.toKind === 'node' && anchorNodeIds.has(edge.toId))
|
|
63
69
|
sourceIds.add(edge.fromId);
|
|
64
70
|
if (edge.fromKind === 'node' && anchorNodeIds.has(edge.fromId) && edge.toKind === 'source')
|
|
65
71
|
sourceIds.add(edge.toId);
|
|
72
|
+
if (edge.fromKind === 'node' && edge.toKind === 'node' && anchorNodeIds.has(edge.toId) && edge.relation === 'describes') {
|
|
73
|
+
factIds.add(edge.fromId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const edge of edges) {
|
|
77
|
+
if (edge.fromKind === 'source' && edge.toKind === 'node' && factIds.has(edge.toId) && edge.relation === 'supports_fact') {
|
|
78
|
+
sourceIds.add(edge.fromId);
|
|
79
|
+
}
|
|
66
80
|
}
|
|
67
81
|
return sourceIds;
|
|
68
82
|
}
|
|
@@ -85,6 +99,25 @@ function linkedSourceQualityByAnchor(store) {
|
|
|
85
99
|
scores.set(edge.fromId, (scores.get(edge.fromId) ?? 0) + score);
|
|
86
100
|
}
|
|
87
101
|
}
|
|
102
|
+
const describingFacts = new Map();
|
|
103
|
+
for (const edge of store.listEdges()) {
|
|
104
|
+
if (edge.fromKind === 'node' && edge.toKind === 'node' && edge.relation === 'describes') {
|
|
105
|
+
const current = describingFacts.get(edge.fromId) ?? new Set();
|
|
106
|
+
current.add(edge.toId);
|
|
107
|
+
describingFacts.set(edge.fromId, current);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const edge of store.listEdges()) {
|
|
111
|
+
if (edge.fromKind !== 'source' || edge.toKind !== 'node' || edge.relation !== 'supports_fact')
|
|
112
|
+
continue;
|
|
113
|
+
const source = sourcesById.get(edge.fromId);
|
|
114
|
+
if (!source)
|
|
115
|
+
continue;
|
|
116
|
+
const score = linkedSourceQuality(source, edge.relation);
|
|
117
|
+
for (const anchorId of describingFacts.get(edge.toId) ?? []) {
|
|
118
|
+
scores.set(anchorId, (scores.get(anchorId) ?? 0) + score);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
88
121
|
return scores;
|
|
89
122
|
}
|
|
90
123
|
function linkedSourceQuality(source, relation) {
|
|
@@ -169,6 +202,31 @@ function isSingularObjectQuery(query, tokens) {
|
|
|
169
202
|
function isStrongIdentityToken(token) {
|
|
170
203
|
return token.length >= 4 && !GENERIC_ANCHOR_TOKENS.has(token);
|
|
171
204
|
}
|
|
205
|
+
function hasSpecificObjectTypeToken(tokens) {
|
|
206
|
+
return tokens.some((token) => SPECIFIC_OBJECT_TYPE_TOKENS.has(token));
|
|
207
|
+
}
|
|
208
|
+
const SPECIFIC_OBJECT_TYPE_TOKENS = new Set([
|
|
209
|
+
'appliance',
|
|
210
|
+
'bridge',
|
|
211
|
+
'camera',
|
|
212
|
+
'console',
|
|
213
|
+
'doorbell',
|
|
214
|
+
'garage',
|
|
215
|
+
'hub',
|
|
216
|
+
'lock',
|
|
217
|
+
'phone',
|
|
218
|
+
'printer',
|
|
219
|
+
'projector',
|
|
220
|
+
'remote',
|
|
221
|
+
'router',
|
|
222
|
+
'sensor',
|
|
223
|
+
'speaker',
|
|
224
|
+
'switch',
|
|
225
|
+
'television',
|
|
226
|
+
'thermostat',
|
|
227
|
+
'tv',
|
|
228
|
+
'xbox',
|
|
229
|
+
]);
|
|
172
230
|
function sourceMatchesAnchor(sourceText, anchorText) {
|
|
173
231
|
const tokens = anchorText
|
|
174
232
|
.toLowerCase()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { KnowledgeSourceRecord } from '../types.js';
|
|
2
|
+
export interface RepairProfileFact {
|
|
3
|
+
readonly kind: 'feature' | 'capability' | 'specification' | 'compatibility' | 'configuration';
|
|
4
|
+
readonly title: string;
|
|
5
|
+
readonly value?: string;
|
|
6
|
+
readonly summary: string;
|
|
7
|
+
readonly evidence: string;
|
|
8
|
+
readonly labels: readonly string[];
|
|
9
|
+
readonly aliases: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function deriveRepairProfileFacts(input: {
|
|
12
|
+
readonly query: string;
|
|
13
|
+
readonly source: KnowledgeSourceRecord;
|
|
14
|
+
readonly text: string;
|
|
15
|
+
}): readonly RepairProfileFact[];
|
|
16
|
+
//# sourceMappingURL=repair-profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repair-profile.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/repair-profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,CAAC;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AA0HD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,GAAG,SAAS,iBAAiB,EAAE,CA8B/B"}
|