@pellux/goodvibes-sdk 0.26.7 → 0.26.9
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 +400 -4
- package/dist/_internal/contracts/generated/foundation-metadata.d.ts +2 -2
- package/dist/_internal/contracts/generated/foundation-metadata.js +2 -2
- package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
- package/dist/_internal/contracts/generated/operator-contract.js +400 -4
- package/dist/_internal/contracts/generated/operator-method-ids.d.ts +1 -1
- package/dist/_internal/contracts/generated/operator-method-ids.d.ts.map +1 -1
- package/dist/_internal/contracts/generated/operator-method-ids.js +1 -0
- package/dist/_internal/daemon/context.d.ts +1 -0
- package/dist/_internal/daemon/context.d.ts.map +1 -1
- package/dist/_internal/daemon/knowledge-route-types.d.ts +1 -0
- package/dist/_internal/daemon/knowledge-route-types.d.ts.map +1 -1
- package/dist/_internal/daemon/knowledge-routes.d.ts +1 -1
- package/dist/_internal/daemon/knowledge-routes.d.ts.map +1 -1
- package/dist/_internal/daemon/knowledge-routes.js +27 -0
- package/dist/_internal/daemon/operator.d.ts +1 -1
- package/dist/_internal/daemon/operator.d.ts.map +1 -1
- package/dist/_internal/daemon/operator.js +3 -0
- package/dist/_internal/platform/control-plane/method-catalog-knowledge.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/method-catalog-knowledge.js +18 -2
- package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts +1 -0
- package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.js +8 -0
- package/dist/_internal/platform/control-plane/routes/operator.d.ts +1 -1
- package/dist/_internal/platform/control-plane/routes/operator.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/routes/operator.js +3 -0
- package/dist/_internal/platform/daemon/http/router-route-contexts.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/http/router-route-contexts.js +1 -0
- package/dist/_internal/platform/knowledge/home-graph/documentation.d.ts +5 -0
- package/dist/_internal/platform/knowledge/home-graph/documentation.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/documentation.js +80 -0
- package/dist/_internal/platform/knowledge/home-graph/quality.d.ts +5 -0
- package/dist/_internal/platform/knowledge/home-graph/quality.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/quality.js +278 -0
- package/dist/_internal/platform/knowledge/home-graph/review.d.ts +14 -0
- package/dist/_internal/platform/knowledge/home-graph/review.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/review.js +183 -0
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.js +310 -25
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts +2 -7
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/service.js +11 -83
- package/dist/_internal/platform/knowledge/home-graph/state.d.ts +2 -0
- package/dist/_internal/platform/knowledge/home-graph/state.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/state.js +1 -1
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts +2 -1
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/types.js +17 -1
- package/dist/_internal/platform/knowledge/knowledge-api.d.ts +2 -0
- package/dist/_internal/platform/knowledge/knowledge-api.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/knowledge-api.js +1 -0
- package/dist/_internal/platform/knowledge/review.d.ts +19 -0
- package/dist/_internal/platform/knowledge/review.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/review.js +117 -0
- package/dist/_internal/platform/knowledge/service.d.ts +2 -0
- package/dist/_internal/platform/knowledge/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/service.js +4 -0
- package/dist/_internal/platform/knowledge/store-schema.d.ts +2 -1
- package/dist/_internal/platform/knowledge/store-schema.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/store-schema.js +9 -0
- package/dist/_internal/platform/knowledge/store.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/store.js +14 -10
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { belongsToSpace, buildHomeGraphMetadata, readRecord } from './helpers.js';
|
|
2
|
+
export async function reviewHomeGraphFact(store, spaceId, installationId, input) {
|
|
3
|
+
const reviewedAt = Date.now();
|
|
4
|
+
if (input.issueId) {
|
|
5
|
+
const issue = store.getIssue(input.issueId);
|
|
6
|
+
if (!issue || !belongsToSpace(issue, spaceId))
|
|
7
|
+
throw new Error(`Unknown Home Graph issue: ${input.issueId}`);
|
|
8
|
+
const subjectNode = issue.nodeId ? store.getNode(issue.nodeId) : null;
|
|
9
|
+
const appliedFacts = await applyHomeGraphReviewFacts(store, spaceId, issue, subjectNode, input);
|
|
10
|
+
const suppression = buildSuppression(input, issue, reviewedAt);
|
|
11
|
+
const updatedIssue = await store.upsertIssue({
|
|
12
|
+
id: issue.id,
|
|
13
|
+
severity: issue.severity,
|
|
14
|
+
code: issue.code,
|
|
15
|
+
message: issue.message,
|
|
16
|
+
status: issueStatusForAction(input.action, issue.status),
|
|
17
|
+
sourceId: issue.sourceId,
|
|
18
|
+
nodeId: issue.nodeId,
|
|
19
|
+
metadata: buildHomeGraphMetadata(spaceId, installationId, {
|
|
20
|
+
review: reviewMetadata(input, reviewedAt),
|
|
21
|
+
...(suppression ? { suppression } : {}),
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
const node = appliedFacts?.node ?? subjectNode ?? undefined;
|
|
25
|
+
return {
|
|
26
|
+
ok: true,
|
|
27
|
+
spaceId,
|
|
28
|
+
issue: updatedIssue,
|
|
29
|
+
...(node ? { node } : {}),
|
|
30
|
+
...(suppression ? { suppression } : {}),
|
|
31
|
+
...(appliedFacts?.facts ? { appliedFacts: appliedFacts.facts } : {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (input.nodeId) {
|
|
35
|
+
const node = store.getNode(input.nodeId);
|
|
36
|
+
if (!node || !belongsToSpace(node, spaceId))
|
|
37
|
+
throw new Error(`Unknown Home Graph node: ${input.nodeId}`);
|
|
38
|
+
const facts = normalizeReviewFacts(input);
|
|
39
|
+
const updated = await store.upsertNode({
|
|
40
|
+
id: node.id,
|
|
41
|
+
kind: node.kind,
|
|
42
|
+
slug: node.slug,
|
|
43
|
+
title: node.title,
|
|
44
|
+
summary: node.summary,
|
|
45
|
+
aliases: node.aliases,
|
|
46
|
+
status: input.action === 'forget' ? 'stale' : node.status,
|
|
47
|
+
confidence: input.action === 'accept' ? 100 : node.confidence,
|
|
48
|
+
sourceId: node.sourceId,
|
|
49
|
+
metadata: buildHomeGraphMetadata(spaceId, installationId, {
|
|
50
|
+
...facts,
|
|
51
|
+
review: reviewMetadata(input, reviewedAt),
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
return { ok: true, spaceId, node: updated, ...(Object.keys(facts).length > 0 ? { appliedFacts: facts } : {}) };
|
|
55
|
+
}
|
|
56
|
+
if (input.sourceId) {
|
|
57
|
+
const source = store.getSource(input.sourceId);
|
|
58
|
+
if (!source || !belongsToSpace(source, spaceId))
|
|
59
|
+
throw new Error(`Unknown Home Graph source: ${input.sourceId}`);
|
|
60
|
+
const updated = await store.upsertSource({
|
|
61
|
+
id: source.id,
|
|
62
|
+
connectorId: source.connectorId,
|
|
63
|
+
sourceType: source.sourceType,
|
|
64
|
+
title: source.title,
|
|
65
|
+
sourceUri: source.sourceUri,
|
|
66
|
+
canonicalUri: source.canonicalUri,
|
|
67
|
+
summary: source.summary,
|
|
68
|
+
description: source.description,
|
|
69
|
+
tags: source.tags,
|
|
70
|
+
folderPath: source.folderPath,
|
|
71
|
+
status: input.action === 'forget' ? 'stale' : source.status,
|
|
72
|
+
artifactId: source.artifactId,
|
|
73
|
+
contentHash: source.contentHash,
|
|
74
|
+
lastCrawledAt: source.lastCrawledAt,
|
|
75
|
+
crawlError: source.crawlError,
|
|
76
|
+
sessionId: source.sessionId,
|
|
77
|
+
metadata: buildHomeGraphMetadata(spaceId, installationId, {
|
|
78
|
+
review: reviewMetadata(input, reviewedAt),
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
81
|
+
return { ok: true, spaceId, source: updated };
|
|
82
|
+
}
|
|
83
|
+
throw new Error('reviewFact requires issueId, nodeId, or sourceId.');
|
|
84
|
+
}
|
|
85
|
+
async function applyHomeGraphReviewFacts(store, spaceId, issue, node, input) {
|
|
86
|
+
if (!node || !belongsToSpace(node, spaceId))
|
|
87
|
+
return undefined;
|
|
88
|
+
const facts = deriveIssueFacts(issue, input);
|
|
89
|
+
if (Object.keys(facts).length === 0)
|
|
90
|
+
return undefined;
|
|
91
|
+
const updated = await store.upsertNode({
|
|
92
|
+
id: node.id,
|
|
93
|
+
kind: node.kind,
|
|
94
|
+
slug: node.slug,
|
|
95
|
+
title: node.title,
|
|
96
|
+
summary: node.summary,
|
|
97
|
+
aliases: node.aliases,
|
|
98
|
+
status: node.status,
|
|
99
|
+
confidence: Math.max(node.confidence, input.action === 'accept' ? 100 : node.confidence),
|
|
100
|
+
sourceId: node.sourceId,
|
|
101
|
+
metadata: facts,
|
|
102
|
+
});
|
|
103
|
+
return { node: updated, facts };
|
|
104
|
+
}
|
|
105
|
+
function deriveIssueFacts(issue, input) {
|
|
106
|
+
const facts = normalizeReviewFacts(input);
|
|
107
|
+
if (issue.code === 'homegraph.device.unknown_battery') {
|
|
108
|
+
const category = readCategory(input);
|
|
109
|
+
const batteryType = readString(facts.batteryType);
|
|
110
|
+
if (batteryType && batteryType !== 'none' && typeof facts.batteryPowered !== 'boolean') {
|
|
111
|
+
return { ...facts, batteryPowered: true };
|
|
112
|
+
}
|
|
113
|
+
if (facts.batteryPowered === false && !batteryType) {
|
|
114
|
+
return { ...facts, batteryType: 'none' };
|
|
115
|
+
}
|
|
116
|
+
if (Object.keys(facts).length > 0)
|
|
117
|
+
return facts;
|
|
118
|
+
if (input.action === 'reject' || input.action === 'resolve' || category === 'not_applicable') {
|
|
119
|
+
return { batteryPowered: false, batteryType: 'none' };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (issue.code === 'homegraph.device.missing_manual') {
|
|
123
|
+
const category = readCategory(input);
|
|
124
|
+
if (Object.keys(facts).length > 0)
|
|
125
|
+
return facts;
|
|
126
|
+
if (input.action === 'reject' || input.action === 'resolve' || category === 'not_applicable') {
|
|
127
|
+
return { manualRequired: false };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return facts;
|
|
131
|
+
}
|
|
132
|
+
function normalizeReviewFacts(input) {
|
|
133
|
+
const value = readRecord(input.value);
|
|
134
|
+
const fact = readRecord(value.fact);
|
|
135
|
+
const source = Object.keys(fact).length > 0 ? fact : value;
|
|
136
|
+
const allowed = {};
|
|
137
|
+
for (const key of [
|
|
138
|
+
'batteryPowered',
|
|
139
|
+
'batteryType',
|
|
140
|
+
'manualRequired',
|
|
141
|
+
'manufacturer',
|
|
142
|
+
'model',
|
|
143
|
+
'serial',
|
|
144
|
+
'firmware',
|
|
145
|
+
'installDate',
|
|
146
|
+
'purchaseDate',
|
|
147
|
+
'warrantyExpiration',
|
|
148
|
+
]) {
|
|
149
|
+
if (key in source)
|
|
150
|
+
allowed[key] = source[key];
|
|
151
|
+
}
|
|
152
|
+
return allowed;
|
|
153
|
+
}
|
|
154
|
+
function reviewMetadata(input, reviewedAt) {
|
|
155
|
+
return {
|
|
156
|
+
action: input.action,
|
|
157
|
+
reviewer: input.reviewer ?? 'homeassistant',
|
|
158
|
+
reviewedAt,
|
|
159
|
+
...(input.value ? { value: input.value } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function buildSuppression(input, issue, reviewedAt) {
|
|
163
|
+
if (!['accept', 'reject', 'resolve'].includes(input.action))
|
|
164
|
+
return undefined;
|
|
165
|
+
return {
|
|
166
|
+
suppressed: true,
|
|
167
|
+
action: input.action,
|
|
168
|
+
reviewedAt,
|
|
169
|
+
reviewer: input.reviewer ?? 'homeassistant',
|
|
170
|
+
issueId: issue.id,
|
|
171
|
+
code: issue.code,
|
|
172
|
+
subjectId: issue.metadata.subjectId ?? issue.nodeId ?? issue.sourceId ?? issue.id,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function issueStatusForAction(action, current) {
|
|
176
|
+
return action === 'reject' || action === 'resolve' || action === 'accept' ? 'resolved' : current;
|
|
177
|
+
}
|
|
178
|
+
function readCategory(input) {
|
|
179
|
+
return readString(readRecord(input.value).category)?.toLowerCase().replace(/[-\s]+/g, '_');
|
|
180
|
+
}
|
|
181
|
+
function readString(value) {
|
|
182
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
183
|
+
}
|
|
@@ -9,5 +9,5 @@ export interface HomeGraphSearchState {
|
|
|
9
9
|
readonly extractionBySourceId: ReadonlyMap<string, KnowledgeExtractionRecord>;
|
|
10
10
|
}
|
|
11
11
|
export declare function readHomeGraphSearchState(store: KnowledgeStore, spaceId: string): HomeGraphSearchState;
|
|
12
|
-
export declare function scoreHomeGraphResults(query: string, sources: readonly KnowledgeSourceRecord[], nodes: readonly KnowledgeNodeRecord[], extractionBySourceId: (sourceId: string) => KnowledgeExtractionRecord | null | undefined, limit: number): HomeGraphSearchResult[];
|
|
12
|
+
export declare function scoreHomeGraphResults(query: string, sources: readonly KnowledgeSourceRecord[], nodes: readonly KnowledgeNodeRecord[], edges: readonly KnowledgeEdgeRecord[], extractionBySourceId: (sourceId: string) => KnowledgeExtractionRecord | null | undefined, limit: number): HomeGraphSearchResult[];
|
|
13
13
|
//# sourceMappingURL=search.d.ts.map
|
|
@@ -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;AAErB,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;AAErB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA6DxD,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,CAqBrG;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,CA+DzB"}
|
|
@@ -2,6 +2,58 @@ import { belongsToSpace, edgeIsActive, readRecord } from './helpers.js';
|
|
|
2
2
|
const MAX_FIELD_CHARS = 4_096;
|
|
3
3
|
const MAX_SECTION_COUNT = 32;
|
|
4
4
|
const MAX_SEARCH_TEXT_CHARS = 64 * 1024;
|
|
5
|
+
const MAX_ANSWER_EXCERPT_CHARS = 640;
|
|
6
|
+
const ANCHOR_SCOPE_LIMIT = 5;
|
|
7
|
+
const STOPWORDS = new Set([
|
|
8
|
+
'a',
|
|
9
|
+
'about',
|
|
10
|
+
'all',
|
|
11
|
+
'an',
|
|
12
|
+
'and',
|
|
13
|
+
'are',
|
|
14
|
+
'as',
|
|
15
|
+
'at',
|
|
16
|
+
'available',
|
|
17
|
+
'be',
|
|
18
|
+
'can',
|
|
19
|
+
'could',
|
|
20
|
+
'do',
|
|
21
|
+
'does',
|
|
22
|
+
'for',
|
|
23
|
+
'from',
|
|
24
|
+
'has',
|
|
25
|
+
'have',
|
|
26
|
+
'how',
|
|
27
|
+
'i',
|
|
28
|
+
'in',
|
|
29
|
+
'is',
|
|
30
|
+
'it',
|
|
31
|
+
'me',
|
|
32
|
+
'my',
|
|
33
|
+
'of',
|
|
34
|
+
'on',
|
|
35
|
+
'or',
|
|
36
|
+
'our',
|
|
37
|
+
'show',
|
|
38
|
+
'tell',
|
|
39
|
+
'that',
|
|
40
|
+
'the',
|
|
41
|
+
'this',
|
|
42
|
+
'to',
|
|
43
|
+
'use',
|
|
44
|
+
'what',
|
|
45
|
+
'which',
|
|
46
|
+
'with',
|
|
47
|
+
]);
|
|
48
|
+
const SHORT_MEANINGFUL_TOKENS = new Set(['ac', 'av', 'dc', 'ha', 'ip', 'ir', 'lg', 'pc', 'tv']);
|
|
49
|
+
const QUERY_EXPANSIONS = {
|
|
50
|
+
capability: ['capabilities', 'feature', 'features', 'function', 'functions', 'mode', 'modes', 'spec', 'specs', 'support', 'supports'],
|
|
51
|
+
capabilities: ['capability', 'feature', 'features', 'function', 'functions', 'mode', 'modes', 'spec', 'specs', 'support', 'supports'],
|
|
52
|
+
feature: ['features', 'capability', 'capabilities', 'function', 'functions', 'mode', 'modes', 'spec', 'specs', 'support', 'supports'],
|
|
53
|
+
features: ['feature', 'capability', 'capabilities', 'function', 'functions', 'mode', 'modes', 'spec', 'specs', 'support', 'supports'],
|
|
54
|
+
television: ['tv', 'media_player'],
|
|
55
|
+
tv: ['television', 'media_player'],
|
|
56
|
+
};
|
|
5
57
|
export function readHomeGraphSearchState(store, spaceId) {
|
|
6
58
|
const sources = store.listSources(10_000).filter((source) => belongsToSpace(source, spaceId));
|
|
7
59
|
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId));
|
|
@@ -23,54 +75,81 @@ export function readHomeGraphSearchState(store, spaceId) {
|
|
|
23
75
|
}
|
|
24
76
|
return { spaceId, sources, nodes, edges, extractionBySourceId };
|
|
25
77
|
}
|
|
26
|
-
export function scoreHomeGraphResults(query, sources, nodes, extractionBySourceId, limit) {
|
|
27
|
-
const tokens =
|
|
78
|
+
export function scoreHomeGraphResults(query, sources, nodes, edges, extractionBySourceId, limit) {
|
|
79
|
+
const tokens = tokenizeQuery(query);
|
|
28
80
|
if (tokens.length === 0)
|
|
29
81
|
return [];
|
|
82
|
+
const expandedTokens = expandTokens(tokens);
|
|
83
|
+
const anchors = selectAnchorNodes(tokens, nodes);
|
|
84
|
+
const anchorIds = new Set(anchors.map((anchor) => anchor.node.id));
|
|
85
|
+
const sourceLinks = buildSourceLinkIndex(edges);
|
|
86
|
+
const useAnchorScope = anchors.length > 0 && anchors.length <= ANCHOR_SCOPE_LIMIT;
|
|
30
87
|
const sourceResults = sources.map((source) => {
|
|
31
88
|
const extraction = extractionBySourceId(source.id);
|
|
32
|
-
|
|
89
|
+
if (isPendingDocumentationCandidate(source, extraction)) {
|
|
90
|
+
return sourceResult(source, extraction, 0);
|
|
91
|
+
}
|
|
92
|
+
const linkedNodeIds = sourceLinks.get(source.id) ?? new Set();
|
|
93
|
+
const linkedToAnchor = useAnchorScope && intersects(linkedNodeIds, anchorIds);
|
|
94
|
+
const identityScore = scoreFields(tokens, [
|
|
33
95
|
source.title,
|
|
34
96
|
source.summary,
|
|
35
97
|
source.description,
|
|
36
98
|
source.sourceUri,
|
|
37
99
|
source.canonicalUri,
|
|
38
100
|
source.tags.join(' '),
|
|
101
|
+
]);
|
|
102
|
+
const contentScore = scoreFields(expandedTokens, [
|
|
39
103
|
extraction?.title,
|
|
40
104
|
extraction?.summary,
|
|
41
105
|
extraction?.excerpt,
|
|
42
106
|
readSearchText(extraction),
|
|
43
107
|
...limitedSections(extraction),
|
|
44
108
|
]);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
109
|
+
const baseScore = identityScore + contentScore;
|
|
110
|
+
const linkBoost = linkedToAnchor ? 120 + relationBoost(source.id, anchorIds, edges) : 0;
|
|
111
|
+
const indexedBoost = source.status === 'indexed' ? 18 : source.status === 'stale' ? 6 : 0;
|
|
112
|
+
const extractionBoost = extraction ? 20 : 0;
|
|
113
|
+
const score = baseScore > 0 || linkBoost > 0
|
|
114
|
+
? baseScore + linkBoost + indexedBoost + extractionBoost
|
|
115
|
+
: 0;
|
|
116
|
+
return sourceResult(source, extraction, score, selectRelevantExcerpt(expandedTokens, source, extraction));
|
|
53
117
|
});
|
|
54
118
|
const nodeResults = nodes.map((node) => {
|
|
55
|
-
const baseScore = scoreFields(tokens,
|
|
56
|
-
|
|
57
|
-
node.summary,
|
|
58
|
-
node.aliases.join(' '),
|
|
59
|
-
readNodeMetadataText(node),
|
|
60
|
-
]);
|
|
119
|
+
const baseScore = scoreFields(tokens, nodeIdentityFields(node));
|
|
120
|
+
const anchorBoost = anchorIds.has(node.id) ? 40 + nodeKindBoost(node.kind) : 0;
|
|
61
121
|
return {
|
|
62
122
|
kind: 'node',
|
|
63
123
|
id: node.id,
|
|
64
|
-
score: baseScore > 0 ? baseScore + Math.round(node.confidence / 20) : 0,
|
|
124
|
+
score: baseScore > 0 ? baseScore + anchorBoost + Math.round(node.confidence / 20) : 0,
|
|
65
125
|
title: node.title,
|
|
66
126
|
summary: node.summary,
|
|
127
|
+
excerpt: node.summary,
|
|
67
128
|
node,
|
|
68
129
|
};
|
|
69
130
|
});
|
|
70
|
-
|
|
131
|
+
let results = [...sourceResults, ...nodeResults]
|
|
71
132
|
.filter((entry) => entry.score > 0)
|
|
72
|
-
.sort(
|
|
73
|
-
|
|
133
|
+
.sort(compareHomeGraphResults);
|
|
134
|
+
const anchoredSourceResults = useAnchorScope
|
|
135
|
+
? results.filter((result) => result.source && intersects(sourceLinks.get(result.source.id) ?? new Set(), anchorIds))
|
|
136
|
+
: [];
|
|
137
|
+
if (anchoredSourceResults.length > 0) {
|
|
138
|
+
results = anchoredSourceResults.sort(compareHomeGraphResults);
|
|
139
|
+
}
|
|
140
|
+
const strongResults = pruneWeakTokenCoverage(results, tokens);
|
|
141
|
+
return strongResults.slice(0, Math.max(1, limit));
|
|
142
|
+
}
|
|
143
|
+
function sourceResult(source, extraction, score, excerpt) {
|
|
144
|
+
return {
|
|
145
|
+
kind: 'source',
|
|
146
|
+
id: source.id,
|
|
147
|
+
score,
|
|
148
|
+
title: source.title ?? source.sourceUri ?? source.id,
|
|
149
|
+
summary: extraction?.summary ?? source.summary,
|
|
150
|
+
...(excerpt ? { excerpt } : {}),
|
|
151
|
+
source,
|
|
152
|
+
};
|
|
74
153
|
}
|
|
75
154
|
function limitedSections(extraction) {
|
|
76
155
|
if (!extraction)
|
|
@@ -103,6 +182,168 @@ function readNodeMetadataText(node) {
|
|
|
103
182
|
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
104
183
|
return values.length > 0 ? values.join(' ') : undefined;
|
|
105
184
|
}
|
|
185
|
+
function nodeIdentityFields(node) {
|
|
186
|
+
return [
|
|
187
|
+
node.title,
|
|
188
|
+
node.summary,
|
|
189
|
+
node.aliases.join(' '),
|
|
190
|
+
readNodeMetadataText(node),
|
|
191
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
192
|
+
}
|
|
193
|
+
function selectAnchorNodes(tokens, nodes) {
|
|
194
|
+
return nodes.map((node) => {
|
|
195
|
+
const baseScore = scoreFields(tokens, nodeIdentityFields(node));
|
|
196
|
+
return {
|
|
197
|
+
node,
|
|
198
|
+
score: baseScore > 0 ? baseScore + nodeKindBoost(node.kind) : 0,
|
|
199
|
+
};
|
|
200
|
+
})
|
|
201
|
+
.filter((entry) => entry.score >= 10)
|
|
202
|
+
.sort((a, b) => b.score - a.score || a.node.id.localeCompare(b.node.id))
|
|
203
|
+
.slice(0, 12);
|
|
204
|
+
}
|
|
205
|
+
function buildSourceLinkIndex(edges) {
|
|
206
|
+
const links = new Map();
|
|
207
|
+
for (const edge of edges) {
|
|
208
|
+
if (edge.fromKind === 'source' && edge.toKind === 'node') {
|
|
209
|
+
addSourceLink(links, edge.fromId, edge.toId);
|
|
210
|
+
}
|
|
211
|
+
else if (edge.fromKind === 'node' && edge.toKind === 'source') {
|
|
212
|
+
addSourceLink(links, edge.toId, edge.fromId);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return links;
|
|
216
|
+
}
|
|
217
|
+
function addSourceLink(links, sourceId, nodeId) {
|
|
218
|
+
const current = links.get(sourceId) ?? new Set();
|
|
219
|
+
current.add(nodeId);
|
|
220
|
+
links.set(sourceId, current);
|
|
221
|
+
}
|
|
222
|
+
function relationBoost(sourceId, anchorIds, edges) {
|
|
223
|
+
let boost = 0;
|
|
224
|
+
for (const edge of edges) {
|
|
225
|
+
const connectsAnchor = (edge.fromKind === 'source' && edge.fromId === sourceId && edge.toKind === 'node' && anchorIds.has(edge.toId))
|
|
226
|
+
|| (edge.fromKind === 'node' && anchorIds.has(edge.fromId) && edge.toKind === 'source' && edge.toId === sourceId);
|
|
227
|
+
if (!connectsAnchor)
|
|
228
|
+
continue;
|
|
229
|
+
if (edge.relation === 'has_manual')
|
|
230
|
+
boost = Math.max(boost, 45);
|
|
231
|
+
else if (edge.relation === 'source_for')
|
|
232
|
+
boost = Math.max(boost, 25);
|
|
233
|
+
else
|
|
234
|
+
boost = Math.max(boost, 15);
|
|
235
|
+
}
|
|
236
|
+
return boost;
|
|
237
|
+
}
|
|
238
|
+
function nodeKindBoost(kind) {
|
|
239
|
+
switch (kind) {
|
|
240
|
+
case 'ha_device':
|
|
241
|
+
case 'ha_entity':
|
|
242
|
+
return 20;
|
|
243
|
+
case 'ha_area':
|
|
244
|
+
case 'ha_room':
|
|
245
|
+
case 'ha_automation':
|
|
246
|
+
case 'ha_script':
|
|
247
|
+
case 'ha_scene':
|
|
248
|
+
return 12;
|
|
249
|
+
case 'ha_integration':
|
|
250
|
+
return 6;
|
|
251
|
+
default:
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function intersects(left, right) {
|
|
256
|
+
for (const value of left) {
|
|
257
|
+
if (right.has(value))
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
function isPendingDocumentationCandidate(source, extraction) {
|
|
263
|
+
return !extraction
|
|
264
|
+
&& source.status !== 'indexed'
|
|
265
|
+
&& source.metadata.homeGraphSourceKind === 'documentation-candidate';
|
|
266
|
+
}
|
|
267
|
+
function selectRelevantExcerpt(tokens, source, extraction) {
|
|
268
|
+
const chunks = candidateExcerptChunks(source, extraction);
|
|
269
|
+
let best;
|
|
270
|
+
for (const chunk of chunks) {
|
|
271
|
+
const text = cleanWhitespace(chunk);
|
|
272
|
+
if (!text)
|
|
273
|
+
continue;
|
|
274
|
+
const score = scoreFields(tokens, [text]);
|
|
275
|
+
if (!best || score > best.score || (score === best.score && text.length < best.text.length)) {
|
|
276
|
+
best = { score, text };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!best || best.score <= 0)
|
|
280
|
+
return firstBoundedText(chunks, MAX_ANSWER_EXCERPT_CHARS);
|
|
281
|
+
return clampAroundBestToken(best.text, tokens, MAX_ANSWER_EXCERPT_CHARS);
|
|
282
|
+
}
|
|
283
|
+
function candidateExcerptChunks(source, extraction) {
|
|
284
|
+
const searchText = readSearchText(extraction);
|
|
285
|
+
return [
|
|
286
|
+
extraction?.excerpt,
|
|
287
|
+
extraction?.summary,
|
|
288
|
+
...limitedSections(extraction),
|
|
289
|
+
...sentenceChunks(searchText),
|
|
290
|
+
source.description,
|
|
291
|
+
source.summary,
|
|
292
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
293
|
+
}
|
|
294
|
+
function sentenceChunks(value) {
|
|
295
|
+
const text = cleanWhitespace(value ?? '');
|
|
296
|
+
if (!text)
|
|
297
|
+
return [];
|
|
298
|
+
const matches = text.match(/[^.!?\n]+[.!?]?/g) ?? [text];
|
|
299
|
+
return matches.map((entry) => entry.trim()).filter(Boolean).slice(0, 80);
|
|
300
|
+
}
|
|
301
|
+
function clampAroundBestToken(value, tokens, maxLength) {
|
|
302
|
+
const text = cleanWhitespace(value);
|
|
303
|
+
if (text.length <= maxLength)
|
|
304
|
+
return text;
|
|
305
|
+
const lower = text.toLowerCase();
|
|
306
|
+
const index = tokens
|
|
307
|
+
.map((token) => lower.indexOf(token.toLowerCase()))
|
|
308
|
+
.filter((entry) => entry >= 0)
|
|
309
|
+
.sort((a, b) => a - b)[0] ?? 0;
|
|
310
|
+
const start = Math.max(0, index - Math.floor(maxLength / 3));
|
|
311
|
+
const end = Math.min(text.length, start + maxLength);
|
|
312
|
+
const prefix = start > 0 ? '...' : '';
|
|
313
|
+
const suffix = end < text.length ? '...' : '';
|
|
314
|
+
return `${prefix}${text.slice(start, end).trim()}${suffix}`;
|
|
315
|
+
}
|
|
316
|
+
function cleanWhitespace(value) {
|
|
317
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
318
|
+
}
|
|
319
|
+
function pruneWeakTokenCoverage(results, tokens) {
|
|
320
|
+
if (results.length <= 1 || tokens.length <= 1)
|
|
321
|
+
return [...results];
|
|
322
|
+
const topCoverage = tokenCoverage(tokens, resultText(results[0]));
|
|
323
|
+
if (topCoverage < 2)
|
|
324
|
+
return [...results];
|
|
325
|
+
return results.filter((result) => tokenCoverage(tokens, resultText(result)) >= Math.max(1, topCoverage - 1));
|
|
326
|
+
}
|
|
327
|
+
function tokenCoverage(tokens, text) {
|
|
328
|
+
const haystack = text.toLowerCase();
|
|
329
|
+
let count = 0;
|
|
330
|
+
for (const token of tokens) {
|
|
331
|
+
if (fieldIncludesToken(haystack, token))
|
|
332
|
+
count += 1;
|
|
333
|
+
}
|
|
334
|
+
return count;
|
|
335
|
+
}
|
|
336
|
+
function resultText(result) {
|
|
337
|
+
return [
|
|
338
|
+
result.title,
|
|
339
|
+
result.summary,
|
|
340
|
+
result.excerpt,
|
|
341
|
+
result.source?.description,
|
|
342
|
+
result.source?.sourceUri,
|
|
343
|
+
result.source?.canonicalUri,
|
|
344
|
+
result.source?.tags.join(' '),
|
|
345
|
+
].filter((value) => typeof value === 'string').join(' ');
|
|
346
|
+
}
|
|
106
347
|
function firstBoundedText(values, maxLength) {
|
|
107
348
|
for (const value of values) {
|
|
108
349
|
if (typeof value !== 'string')
|
|
@@ -117,8 +358,31 @@ function firstBoundedText(values, maxLength) {
|
|
|
117
358
|
function clampText(value, maxLength) {
|
|
118
359
|
return value.length <= maxLength ? value : value.slice(0, maxLength);
|
|
119
360
|
}
|
|
120
|
-
function
|
|
121
|
-
|
|
361
|
+
function tokenizeQuery(value) {
|
|
362
|
+
const tokens = value.toLowerCase()
|
|
363
|
+
.split(/[^a-z0-9_.:-]+/)
|
|
364
|
+
.map((entry) => entry.trim())
|
|
365
|
+
.filter((entry) => isMeaningfulToken(entry));
|
|
366
|
+
return [...new Set(tokens)];
|
|
367
|
+
}
|
|
368
|
+
function expandTokens(tokens) {
|
|
369
|
+
const expanded = new Set();
|
|
370
|
+
for (const token of tokens) {
|
|
371
|
+
expanded.add(token);
|
|
372
|
+
for (const synonym of QUERY_EXPANSIONS[token] ?? []) {
|
|
373
|
+
expanded.add(synonym);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return [...expanded];
|
|
377
|
+
}
|
|
378
|
+
function isMeaningfulToken(token) {
|
|
379
|
+
if (!token || STOPWORDS.has(token))
|
|
380
|
+
return false;
|
|
381
|
+
if (token.length === 1)
|
|
382
|
+
return false;
|
|
383
|
+
if (token.length <= 2 && !SHORT_MEANINGFUL_TOKENS.has(token))
|
|
384
|
+
return false;
|
|
385
|
+
return true;
|
|
122
386
|
}
|
|
123
387
|
function scoreFields(tokens, fields) {
|
|
124
388
|
let score = 0;
|
|
@@ -128,9 +392,30 @@ function scoreFields(tokens, fields) {
|
|
|
128
392
|
if (!haystack)
|
|
129
393
|
continue;
|
|
130
394
|
for (const token of tokens) {
|
|
131
|
-
if (haystack
|
|
132
|
-
score += 10;
|
|
395
|
+
if (fieldIncludesToken(haystack, token))
|
|
396
|
+
score += token.length <= 3 ? 14 : 10;
|
|
133
397
|
}
|
|
134
398
|
}
|
|
135
399
|
return score;
|
|
136
400
|
}
|
|
401
|
+
function fieldIncludesToken(haystack, token) {
|
|
402
|
+
if (token.length <= 3 || token.includes('_') || token.includes('-')) {
|
|
403
|
+
return new RegExp(`(?:^|[^a-z0-9])${escapeRegExp(token)}(?:$|[^a-z0-9])`).test(haystack);
|
|
404
|
+
}
|
|
405
|
+
return haystack.includes(token);
|
|
406
|
+
}
|
|
407
|
+
function escapeRegExp(value) {
|
|
408
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
409
|
+
}
|
|
410
|
+
function compareHomeGraphResults(left, right) {
|
|
411
|
+
return right.score - left.score
|
|
412
|
+
|| resultKindPriority(right) - resultKindPriority(left)
|
|
413
|
+
|| left.id.localeCompare(right.id);
|
|
414
|
+
}
|
|
415
|
+
function resultKindPriority(result) {
|
|
416
|
+
if (result.source)
|
|
417
|
+
return 2;
|
|
418
|
+
if (result.node)
|
|
419
|
+
return 1;
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ArtifactStore } from '../../artifacts/index.js';
|
|
2
2
|
import type { KnowledgeStore } from '../store.js';
|
|
3
3
|
import type { KnowledgeEdgeRecord, KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
|
|
4
|
+
import { type HomeGraphReviewResult } from './review.js';
|
|
4
5
|
import type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReviewInput, HomeGraphSpaceInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult } from './types.js';
|
|
5
6
|
export declare class HomeGraphService {
|
|
6
7
|
private readonly store;
|
|
@@ -30,13 +31,7 @@ export declare class HomeGraphService {
|
|
|
30
31
|
readonly spaceId: string;
|
|
31
32
|
readonly issues: readonly KnowledgeIssueRecord[];
|
|
32
33
|
}>;
|
|
33
|
-
reviewFact(input: HomeGraphReviewInput): Promise<
|
|
34
|
-
readonly ok: true;
|
|
35
|
-
readonly spaceId: string;
|
|
36
|
-
readonly issue?: KnowledgeIssueRecord;
|
|
37
|
-
readonly node?: KnowledgeNodeRecord;
|
|
38
|
-
readonly source?: KnowledgeSourceRecord;
|
|
39
|
-
}>;
|
|
34
|
+
reviewFact(input: HomeGraphReviewInput): Promise<HomeGraphReviewResult>;
|
|
40
35
|
listSources(input?: HomeGraphSpaceInput & {
|
|
41
36
|
readonly limit?: number;
|
|
42
37
|
}): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EAEnB,oBAAoB,EACpB,mBAAmB,EAEnB,qBAAqB,EAEtB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/service.ts"],"names":[],"mappings":"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"}
|