@pellux/goodvibes-sdk 0.26.6 → 0.26.8
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/extractors.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/extractors.js +26 -2
- 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/helpers.d.ts +2 -7
- package/dist/_internal/platform/knowledge/home-graph/helpers.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/helpers.js +0 -54
- 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 +13 -0
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/search.js +136 -0
- 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 +13 -84
- package/dist/_internal/platform/knowledge/home-graph/state.d.ts +4 -1
- package/dist/_internal/platform/knowledge/home-graph/state.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts +1 -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,278 @@
|
|
|
1
|
+
import { buildIssue, readRecord, stableHash, uniqueStrings } from './helpers.js';
|
|
2
|
+
import { readHomeGraphState, sourcesLinkedToNode } from './state.js';
|
|
3
|
+
const QUALITY_NAMESPACE_PREFIX = 'homegraph';
|
|
4
|
+
const EXCLUDED_SOFTWARE_TERMS = [
|
|
5
|
+
'add-on',
|
|
6
|
+
'addon',
|
|
7
|
+
'automation',
|
|
8
|
+
'cloud',
|
|
9
|
+
'core',
|
|
10
|
+
'helper',
|
|
11
|
+
'home assistant',
|
|
12
|
+
'integration',
|
|
13
|
+
'operating system',
|
|
14
|
+
'scene',
|
|
15
|
+
'script',
|
|
16
|
+
'service',
|
|
17
|
+
'software',
|
|
18
|
+
'supervisor',
|
|
19
|
+
'template',
|
|
20
|
+
'virtual',
|
|
21
|
+
];
|
|
22
|
+
const EXCLUDED_INFRASTRUCTURE_TERMS = [
|
|
23
|
+
'adapter',
|
|
24
|
+
'bridge',
|
|
25
|
+
'coordinator',
|
|
26
|
+
'dongle',
|
|
27
|
+
'gateway',
|
|
28
|
+
'hub',
|
|
29
|
+
];
|
|
30
|
+
const EXCLUDED_MAINS_TERMS = [
|
|
31
|
+
'air conditioner',
|
|
32
|
+
'appliance',
|
|
33
|
+
'bulb',
|
|
34
|
+
'dishwasher',
|
|
35
|
+
'dryer',
|
|
36
|
+
'furnace',
|
|
37
|
+
'heat pump',
|
|
38
|
+
'hvac',
|
|
39
|
+
'light',
|
|
40
|
+
'microwave',
|
|
41
|
+
'outlet',
|
|
42
|
+
'plug',
|
|
43
|
+
'power strip',
|
|
44
|
+
'receiver',
|
|
45
|
+
'refrigerator',
|
|
46
|
+
'soundbar',
|
|
47
|
+
'speaker',
|
|
48
|
+
'switch',
|
|
49
|
+
'television',
|
|
50
|
+
'tv',
|
|
51
|
+
'washer',
|
|
52
|
+
];
|
|
53
|
+
const BATTERY_EVIDENCE_TERMS = [
|
|
54
|
+
'battery',
|
|
55
|
+
'battery powered',
|
|
56
|
+
'button cell',
|
|
57
|
+
'cr123',
|
|
58
|
+
'cr2032',
|
|
59
|
+
'keypad',
|
|
60
|
+
'leak sensor',
|
|
61
|
+
'lithium',
|
|
62
|
+
'motion sensor',
|
|
63
|
+
'remote',
|
|
64
|
+
'rechargeable',
|
|
65
|
+
];
|
|
66
|
+
const BATTERY_ENTITY_DOMAINS = new Set(['binary_sensor', 'lock', 'remote']);
|
|
67
|
+
const BATTERY_DEVICE_CLASSES = new Set([
|
|
68
|
+
'battery',
|
|
69
|
+
'door',
|
|
70
|
+
'gas',
|
|
71
|
+
'moisture',
|
|
72
|
+
'motion',
|
|
73
|
+
'occupancy',
|
|
74
|
+
'opening',
|
|
75
|
+
'safety',
|
|
76
|
+
'smoke',
|
|
77
|
+
'tamper',
|
|
78
|
+
'vibration',
|
|
79
|
+
'window',
|
|
80
|
+
]);
|
|
81
|
+
const MANUAL_ENTITY_DOMAINS = new Set([
|
|
82
|
+
'alarm_control_panel',
|
|
83
|
+
'binary_sensor',
|
|
84
|
+
'camera',
|
|
85
|
+
'climate',
|
|
86
|
+
'cover',
|
|
87
|
+
'fan',
|
|
88
|
+
'humidifier',
|
|
89
|
+
'light',
|
|
90
|
+
'lock',
|
|
91
|
+
'media_player',
|
|
92
|
+
'remote',
|
|
93
|
+
'sensor',
|
|
94
|
+
'switch',
|
|
95
|
+
'vacuum',
|
|
96
|
+
'water_heater',
|
|
97
|
+
]);
|
|
98
|
+
export function homeGraphQualityNamespace(spaceId) {
|
|
99
|
+
return `${QUALITY_NAMESPACE_PREFIX}:${spaceId}:quality`;
|
|
100
|
+
}
|
|
101
|
+
export async function refreshHomeGraphQualityIssues(store, spaceId, installationId) {
|
|
102
|
+
const state = readHomeGraphState(store, spaceId);
|
|
103
|
+
const issues = [];
|
|
104
|
+
for (const node of state.nodes) {
|
|
105
|
+
if (node.kind !== 'ha_device')
|
|
106
|
+
continue;
|
|
107
|
+
if (sourcesLinkedToNode(node.id, state).length === 0 && shouldRequireManual(node, state)) {
|
|
108
|
+
issues.push(qualityIssue(spaceId, installationId, 'homegraph.device.missing_manual', `${node.title} has no linked manual or source.`, node));
|
|
109
|
+
}
|
|
110
|
+
if (shouldRequireBatteryType(node, state)) {
|
|
111
|
+
issues.push(qualityIssue(spaceId, installationId, 'homegraph.device.unknown_battery', `${node.title} has no known battery type.`, node));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return store.replaceIssues(issues.filter((issue) => !isSuppressedGeneratedIssue(store.getIssue(issue.id), issue)), homeGraphQualityNamespace(spaceId));
|
|
115
|
+
}
|
|
116
|
+
function qualityIssue(spaceId, installationId, code, message, node) {
|
|
117
|
+
const issue = buildIssue(spaceId, installationId, code, message, { nodeId: node.id });
|
|
118
|
+
return {
|
|
119
|
+
...issue,
|
|
120
|
+
metadata: {
|
|
121
|
+
...(issue.metadata ?? {}),
|
|
122
|
+
generated: true,
|
|
123
|
+
subjectFingerprint: subjectFingerprint(node, code),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function isSuppressedGeneratedIssue(existing, input) {
|
|
128
|
+
if (!existing || existing.status !== 'resolved')
|
|
129
|
+
return false;
|
|
130
|
+
const existingFingerprint = typeof existing.metadata.subjectFingerprint === 'string'
|
|
131
|
+
? existing.metadata.subjectFingerprint
|
|
132
|
+
: undefined;
|
|
133
|
+
const inputFingerprint = typeof input.metadata?.subjectFingerprint === 'string'
|
|
134
|
+
? input.metadata.subjectFingerprint
|
|
135
|
+
: undefined;
|
|
136
|
+
return existingFingerprint === inputFingerprint;
|
|
137
|
+
}
|
|
138
|
+
function shouldRequireBatteryType(node, state) {
|
|
139
|
+
if (readNonEmptyString(node.metadata.batteryType))
|
|
140
|
+
return false;
|
|
141
|
+
const powered = readBooleanLike(node.metadata.batteryPowered);
|
|
142
|
+
if (powered === false)
|
|
143
|
+
return false;
|
|
144
|
+
if (powered === true)
|
|
145
|
+
return true;
|
|
146
|
+
if (isSoftwareOrInfrastructure(node, state))
|
|
147
|
+
return false;
|
|
148
|
+
if (hasAny(qualityText(node, state), EXCLUDED_MAINS_TERMS))
|
|
149
|
+
return false;
|
|
150
|
+
if (hasAny(qualityText(node, state), BATTERY_EVIDENCE_TERMS))
|
|
151
|
+
return true;
|
|
152
|
+
return relatedEntities(node, state).some((entity) => {
|
|
153
|
+
const domain = entityDomain(entity);
|
|
154
|
+
const deviceClass = entityDeviceClass(entity);
|
|
155
|
+
return (domain ? BATTERY_ENTITY_DOMAINS.has(domain) : false)
|
|
156
|
+
|| (deviceClass ? BATTERY_DEVICE_CLASSES.has(deviceClass) : false);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function shouldRequireManual(node, state) {
|
|
160
|
+
const required = readBooleanLike(node.metadata.manualRequired);
|
|
161
|
+
if (required === false)
|
|
162
|
+
return false;
|
|
163
|
+
if (required === true)
|
|
164
|
+
return true;
|
|
165
|
+
if (isSoftwareOrInfrastructure(node, state))
|
|
166
|
+
return false;
|
|
167
|
+
if (readNonEmptyString(node.metadata.manufacturer) || readNonEmptyString(node.metadata.model))
|
|
168
|
+
return true;
|
|
169
|
+
return relatedEntities(node, state).some((entity) => {
|
|
170
|
+
const domain = entityDomain(entity);
|
|
171
|
+
return domain ? MANUAL_ENTITY_DOMAINS.has(domain) : false;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function isSoftwareOrInfrastructure(node, state) {
|
|
175
|
+
const text = qualityText(node, state);
|
|
176
|
+
if (hasAny(text, EXCLUDED_SOFTWARE_TERMS))
|
|
177
|
+
return true;
|
|
178
|
+
if (hasAny(text, EXCLUDED_INFRASTRUCTURE_TERMS))
|
|
179
|
+
return true;
|
|
180
|
+
return relatedEntities(node, state).every((entity) => {
|
|
181
|
+
const domain = entityDomain(entity);
|
|
182
|
+
return domain === 'sun' || domain === 'weather';
|
|
183
|
+
}) && relatedEntities(node, state).length > 0;
|
|
184
|
+
}
|
|
185
|
+
function subjectFingerprint(node, code) {
|
|
186
|
+
const homeAssistant = readRecord(node.metadata.homeAssistant);
|
|
187
|
+
const attributes = readRecord(node.metadata.attributes);
|
|
188
|
+
return stableHash(JSON.stringify({
|
|
189
|
+
code,
|
|
190
|
+
kind: node.kind,
|
|
191
|
+
title: node.title,
|
|
192
|
+
manufacturer: node.metadata.manufacturer,
|
|
193
|
+
model: node.metadata.model,
|
|
194
|
+
batteryPowered: node.metadata.batteryPowered,
|
|
195
|
+
batteryType: node.metadata.batteryType,
|
|
196
|
+
manualRequired: node.metadata.manualRequired,
|
|
197
|
+
objectKind: homeAssistant.objectKind,
|
|
198
|
+
objectId: homeAssistant.objectId,
|
|
199
|
+
entityId: homeAssistant.entityId,
|
|
200
|
+
deviceId: homeAssistant.deviceId,
|
|
201
|
+
integrationId: homeAssistant.integrationId,
|
|
202
|
+
domain: homeAssistant.domain,
|
|
203
|
+
deviceClass: attributes.device_class,
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
function relatedEntities(node, state) {
|
|
207
|
+
const byId = new Map(state.nodes.map((entry) => [entry.id, entry]));
|
|
208
|
+
return state.edges
|
|
209
|
+
.filter((edge) => edge.fromKind === 'node' && edge.toKind === 'node' && edge.toId === node.id && edge.relation === 'belongs_to_device')
|
|
210
|
+
.flatMap((edge) => {
|
|
211
|
+
const entry = byId.get(edge.fromId);
|
|
212
|
+
return entry?.kind === 'ha_entity' ? [entry] : [];
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function qualityText(node, state) {
|
|
216
|
+
const values = uniqueStrings([
|
|
217
|
+
node.title,
|
|
218
|
+
node.summary,
|
|
219
|
+
...node.aliases,
|
|
220
|
+
...metadataStrings(node.metadata),
|
|
221
|
+
...relatedEntities(node, state).flatMap((entity) => [
|
|
222
|
+
entity.title,
|
|
223
|
+
entity.summary,
|
|
224
|
+
...entity.aliases,
|
|
225
|
+
...metadataStrings(entity.metadata),
|
|
226
|
+
]),
|
|
227
|
+
]);
|
|
228
|
+
return values.join(' ').toLowerCase();
|
|
229
|
+
}
|
|
230
|
+
function metadataStrings(metadata) {
|
|
231
|
+
const homeAssistant = readRecord(metadata.homeAssistant);
|
|
232
|
+
const attributes = readRecord(metadata.attributes);
|
|
233
|
+
return uniqueStrings([
|
|
234
|
+
readNonEmptyString(metadata.manufacturer),
|
|
235
|
+
readNonEmptyString(metadata.model),
|
|
236
|
+
readNonEmptyString(metadata.entryType),
|
|
237
|
+
readNonEmptyString(metadata.entry_type),
|
|
238
|
+
readNonEmptyString(homeAssistant.objectKind),
|
|
239
|
+
readNonEmptyString(homeAssistant.objectId),
|
|
240
|
+
readNonEmptyString(homeAssistant.entityId),
|
|
241
|
+
readNonEmptyString(homeAssistant.deviceId),
|
|
242
|
+
readNonEmptyString(homeAssistant.integrationId),
|
|
243
|
+
readNonEmptyString(homeAssistant.domain),
|
|
244
|
+
readNonEmptyString(attributes.device_class),
|
|
245
|
+
readNonEmptyString(attributes.friendly_name),
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
function entityDomain(node) {
|
|
249
|
+
const homeAssistant = readRecord(node.metadata.homeAssistant);
|
|
250
|
+
const entityId = readNonEmptyString(homeAssistant.entityId);
|
|
251
|
+
if (entityId?.includes('.'))
|
|
252
|
+
return entityId.split('.', 1)[0];
|
|
253
|
+
return readNonEmptyString(homeAssistant.domain);
|
|
254
|
+
}
|
|
255
|
+
function entityDeviceClass(node) {
|
|
256
|
+
return readNonEmptyString(readRecord(node.metadata.attributes).device_class)?.toLowerCase();
|
|
257
|
+
}
|
|
258
|
+
function hasAny(text, terms) {
|
|
259
|
+
return terms.some((term) => text.includes(term));
|
|
260
|
+
}
|
|
261
|
+
function readBooleanLike(value) {
|
|
262
|
+
if (typeof value === 'boolean')
|
|
263
|
+
return value;
|
|
264
|
+
if (typeof value === 'string') {
|
|
265
|
+
const normalized = value.trim().toLowerCase();
|
|
266
|
+
if (['true', 'yes', '1'].includes(normalized))
|
|
267
|
+
return true;
|
|
268
|
+
if (['false', 'no', '0', 'none', 'not_applicable', 'not applicable'].includes(normalized))
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
function readNonEmptyString(value) {
|
|
274
|
+
if (typeof value !== 'string')
|
|
275
|
+
return undefined;
|
|
276
|
+
const trimmed = value.trim();
|
|
277
|
+
return trimmed ? trimmed : undefined;
|
|
278
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
|
|
2
|
+
import type { KnowledgeStore } from '../store.js';
|
|
3
|
+
import type { HomeGraphReviewInput } from './types.js';
|
|
4
|
+
export interface HomeGraphReviewResult {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly spaceId: string;
|
|
7
|
+
readonly issue?: KnowledgeIssueRecord;
|
|
8
|
+
readonly node?: KnowledgeNodeRecord;
|
|
9
|
+
readonly source?: KnowledgeSourceRecord;
|
|
10
|
+
readonly suppression?: Record<string, unknown>;
|
|
11
|
+
readonly appliedFacts?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export declare function reviewHomeGraphFact(store: KnowledgeStore, spaceId: string, installationId: string, input: HomeGraphReviewInput): Promise<HomeGraphReviewResult>;
|
|
14
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjD;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,qBAAqB,CAAC,CA+EhC"}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { KnowledgeStore } from '../store.js';
|
|
2
|
+
import type { KnowledgeEdgeRecord, KnowledgeExtractionRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
|
|
3
|
+
import type { HomeGraphSearchResult } from './types.js';
|
|
4
|
+
export interface HomeGraphSearchState {
|
|
5
|
+
readonly spaceId: string;
|
|
6
|
+
readonly sources: readonly KnowledgeSourceRecord[];
|
|
7
|
+
readonly nodes: readonly KnowledgeNodeRecord[];
|
|
8
|
+
readonly edges: readonly KnowledgeEdgeRecord[];
|
|
9
|
+
readonly extractionBySourceId: ReadonlyMap<string, KnowledgeExtractionRecord>;
|
|
10
|
+
}
|
|
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[];
|
|
13
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +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;AAMxD,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,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,yBAAyB,GAAG,IAAI,GAAG,SAAS,EACxF,KAAK,EAAE,MAAM,GACZ,qBAAqB,EAAE,CA+CzB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { belongsToSpace, edgeIsActive, readRecord } from './helpers.js';
|
|
2
|
+
const MAX_FIELD_CHARS = 4_096;
|
|
3
|
+
const MAX_SECTION_COUNT = 32;
|
|
4
|
+
const MAX_SEARCH_TEXT_CHARS = 64 * 1024;
|
|
5
|
+
export function readHomeGraphSearchState(store, spaceId) {
|
|
6
|
+
const sources = store.listSources(10_000).filter((source) => belongsToSpace(source, spaceId));
|
|
7
|
+
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId));
|
|
8
|
+
const sourceIds = new Set(sources.map((source) => source.id));
|
|
9
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
10
|
+
const edges = store.listEdges().filter((edge) => (edgeIsActive(edge)
|
|
11
|
+
&& belongsToSpace(edge, spaceId)
|
|
12
|
+
&& (edge.fromKind !== 'source' || sourceIds.has(edge.fromId))
|
|
13
|
+
&& (edge.toKind !== 'source' || sourceIds.has(edge.toId))
|
|
14
|
+
&& (edge.fromKind !== 'node' || nodeIds.has(edge.fromId))
|
|
15
|
+
&& (edge.toKind !== 'node' || nodeIds.has(edge.toId))));
|
|
16
|
+
const extractionBySourceId = new Map();
|
|
17
|
+
for (const extraction of store.listExtractions(10_000)) {
|
|
18
|
+
if (!sourceIds.has(extraction.sourceId) && !belongsToSpace(extraction, spaceId))
|
|
19
|
+
continue;
|
|
20
|
+
if (!extractionBySourceId.has(extraction.sourceId)) {
|
|
21
|
+
extractionBySourceId.set(extraction.sourceId, extraction);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { spaceId, sources, nodes, edges, extractionBySourceId };
|
|
25
|
+
}
|
|
26
|
+
export function scoreHomeGraphResults(query, sources, nodes, extractionBySourceId, limit) {
|
|
27
|
+
const tokens = tokenize(query);
|
|
28
|
+
if (tokens.length === 0)
|
|
29
|
+
return [];
|
|
30
|
+
const sourceResults = sources.map((source) => {
|
|
31
|
+
const extraction = extractionBySourceId(source.id);
|
|
32
|
+
const baseScore = scoreFields(tokens, [
|
|
33
|
+
source.title,
|
|
34
|
+
source.summary,
|
|
35
|
+
source.description,
|
|
36
|
+
source.sourceUri,
|
|
37
|
+
source.canonicalUri,
|
|
38
|
+
source.tags.join(' '),
|
|
39
|
+
extraction?.title,
|
|
40
|
+
extraction?.summary,
|
|
41
|
+
extraction?.excerpt,
|
|
42
|
+
readSearchText(extraction),
|
|
43
|
+
...limitedSections(extraction),
|
|
44
|
+
]);
|
|
45
|
+
return {
|
|
46
|
+
kind: 'source',
|
|
47
|
+
id: source.id,
|
|
48
|
+
score: baseScore > 0 ? baseScore + (extraction ? 8 : 0) : 0,
|
|
49
|
+
title: source.title ?? source.sourceUri ?? source.id,
|
|
50
|
+
summary: extraction?.summary ?? source.summary,
|
|
51
|
+
source,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
const nodeResults = nodes.map((node) => {
|
|
55
|
+
const baseScore = scoreFields(tokens, [
|
|
56
|
+
node.title,
|
|
57
|
+
node.summary,
|
|
58
|
+
node.aliases.join(' '),
|
|
59
|
+
readNodeMetadataText(node),
|
|
60
|
+
]);
|
|
61
|
+
return {
|
|
62
|
+
kind: 'node',
|
|
63
|
+
id: node.id,
|
|
64
|
+
score: baseScore > 0 ? baseScore + Math.round(node.confidence / 20) : 0,
|
|
65
|
+
title: node.title,
|
|
66
|
+
summary: node.summary,
|
|
67
|
+
node,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
return [...sourceResults, ...nodeResults]
|
|
71
|
+
.filter((entry) => entry.score > 0)
|
|
72
|
+
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
73
|
+
.slice(0, Math.max(1, limit));
|
|
74
|
+
}
|
|
75
|
+
function limitedSections(extraction) {
|
|
76
|
+
if (!extraction)
|
|
77
|
+
return [];
|
|
78
|
+
return extraction.sections.slice(0, MAX_SECTION_COUNT).map((section) => clampText(section, MAX_FIELD_CHARS));
|
|
79
|
+
}
|
|
80
|
+
function readSearchText(extraction) {
|
|
81
|
+
if (!extraction)
|
|
82
|
+
return undefined;
|
|
83
|
+
const structure = readRecord(extraction.structure);
|
|
84
|
+
const metadata = readRecord(extraction.metadata);
|
|
85
|
+
return firstBoundedText([
|
|
86
|
+
structure.searchText,
|
|
87
|
+
structure.text,
|
|
88
|
+
structure.content,
|
|
89
|
+
metadata.searchText,
|
|
90
|
+
], MAX_SEARCH_TEXT_CHARS);
|
|
91
|
+
}
|
|
92
|
+
function readNodeMetadataText(node) {
|
|
93
|
+
const homeAssistant = readRecord(node.metadata.homeAssistant);
|
|
94
|
+
const values = [
|
|
95
|
+
homeAssistant.objectKind,
|
|
96
|
+
homeAssistant.objectId,
|
|
97
|
+
homeAssistant.entityId,
|
|
98
|
+
homeAssistant.deviceId,
|
|
99
|
+
homeAssistant.areaId,
|
|
100
|
+
homeAssistant.integrationId,
|
|
101
|
+
node.metadata.manufacturer,
|
|
102
|
+
node.metadata.model,
|
|
103
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
104
|
+
return values.length > 0 ? values.join(' ') : undefined;
|
|
105
|
+
}
|
|
106
|
+
function firstBoundedText(values, maxLength) {
|
|
107
|
+
for (const value of values) {
|
|
108
|
+
if (typeof value !== 'string')
|
|
109
|
+
continue;
|
|
110
|
+
const trimmed = value.trim();
|
|
111
|
+
if (!trimmed)
|
|
112
|
+
continue;
|
|
113
|
+
return clampText(trimmed, maxLength);
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
function clampText(value, maxLength) {
|
|
118
|
+
return value.length <= maxLength ? value : value.slice(0, maxLength);
|
|
119
|
+
}
|
|
120
|
+
function tokenize(value) {
|
|
121
|
+
return value.toLowerCase().split(/[^a-z0-9_.:-]+/).map((entry) => entry.trim()).filter(Boolean);
|
|
122
|
+
}
|
|
123
|
+
function scoreFields(tokens, fields) {
|
|
124
|
+
let score = 0;
|
|
125
|
+
for (const field of fields) {
|
|
126
|
+
const raw = typeof field === 'string' ? field.trim() : '';
|
|
127
|
+
const haystack = clampText(raw, MAX_SEARCH_TEXT_CHARS).toLowerCase();
|
|
128
|
+
if (!haystack)
|
|
129
|
+
continue;
|
|
130
|
+
for (const token of tokens) {
|
|
131
|
+
if (haystack.includes(token))
|
|
132
|
+
score += 10;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return score;
|
|
136
|
+
}
|
|
@@ -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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"}
|