@pellux/goodvibes-sdk 0.27.1 → 0.27.3
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 +870 -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 +870 -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/knowledge-routes.d.ts.map +1 -1
- package/dist/_internal/daemon/knowledge-routes.js +27 -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 +41 -2
- package/dist/_internal/platform/control-plane/method-catalog-knowledge.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/method-catalog-knowledge.js +15 -1
- 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 +28 -0
- package/dist/_internal/platform/daemon/http/home-graph-routes.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/http/home-graph-routes.js +50 -0
- package/dist/_internal/platform/knowledge/extractors.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/extractors.js +1 -116
- package/dist/_internal/platform/knowledge/home-graph/auto-link.d.ts +27 -0
- package/dist/_internal/platform/knowledge/home-graph/auto-link.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/auto-link.js +236 -0
- package/dist/_internal/platform/knowledge/home-graph/extraction-quality.d.ts +2 -0
- package/dist/_internal/platform/knowledge/home-graph/extraction-quality.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/extraction-quality.js +52 -0
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts +1 -0
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +14 -8
- package/dist/_internal/platform/knowledge/home-graph/index.d.ts +1 -1
- package/dist/_internal/platform/knowledge/home-graph/index.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/pages.d.ts +11 -0
- package/dist/_internal/platform/knowledge/home-graph/pages.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/pages.js +44 -0
- package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts +5 -6
- package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/rendering.js +284 -5
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.js +4 -6
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts +6 -1
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/service.js +60 -6
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts +47 -1
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/index.d.ts +2 -2
- package/dist/_internal/platform/knowledge/index.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/map-filters.d.ts +24 -0
- package/dist/_internal/platform/knowledge/map-filters.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/map-filters.js +199 -0
- package/dist/_internal/platform/knowledge/map.d.ts +16 -1
- package/dist/_internal/platform/knowledge/map.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/map.js +19 -8
- package/dist/_internal/platform/knowledge/pdf-extractor.d.ts +3 -0
- package/dist/_internal/platform/knowledge/pdf-extractor.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/pdf-extractor.js +346 -0
- package/dist/_internal/platform/knowledge/service.d.ts +2 -6
- package/dist/_internal/platform/knowledge/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/types.d.ts +39 -1
- package/dist/_internal/platform/knowledge/types.d.ts.map +1 -1
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { buildHomeGraphMetadata, edgeIsActive, isGeneratedPageSource, readRecord, uniqueStrings, } from './helpers.js';
|
|
2
|
+
const MIN_AUTO_LINK_SCORE = 90;
|
|
3
|
+
const MIN_SOURCE_TEXT_CHARS = 16;
|
|
4
|
+
const MAX_TEXT_FIELD_CHARS = 32_768;
|
|
5
|
+
const GENERIC_TOKENS = new Set(['device', 'home', 'assistant', 'smart', 'manual', 'owner', 'guide', 'user', 'the']);
|
|
6
|
+
export async function autoLinkHomeGraphSource(input) {
|
|
7
|
+
if (isGeneratedPageSource(input.source) || shouldSkipAutoLink(input.source))
|
|
8
|
+
return undefined;
|
|
9
|
+
if (hasActiveSourceLink(input.source.id, input.state.edges))
|
|
10
|
+
return undefined;
|
|
11
|
+
const candidates = scoreCandidates(input.source, input.extraction, input.state);
|
|
12
|
+
const best = candidates[0];
|
|
13
|
+
if (!best || best.score < MIN_AUTO_LINK_SCORE)
|
|
14
|
+
return undefined;
|
|
15
|
+
const next = candidates[1];
|
|
16
|
+
if (next && best.score - next.score < 20 && !best.reasons.some((reason) => reason.startsWith('exact-model:'))) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const relation = inferRelation(input.source);
|
|
20
|
+
const edge = await input.store.upsertEdge({
|
|
21
|
+
fromKind: 'source',
|
|
22
|
+
fromId: input.source.id,
|
|
23
|
+
toKind: 'node',
|
|
24
|
+
toId: best.node.id,
|
|
25
|
+
relation,
|
|
26
|
+
weight: Math.min(5, Math.max(1, Math.round(best.score / 60))),
|
|
27
|
+
metadata: buildHomeGraphMetadata(input.spaceId, input.installationId, {
|
|
28
|
+
linkStatus: 'active',
|
|
29
|
+
linkMethod: 'homegraph-auto-link',
|
|
30
|
+
autoLinkedAt: Date.now(),
|
|
31
|
+
autoLinkScore: best.score,
|
|
32
|
+
autoLinkReasons: best.reasons,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
return { edge, node: best.node, relation, score: best.score, reasons: best.reasons };
|
|
36
|
+
}
|
|
37
|
+
export async function autoLinkHomeGraphSources(input) {
|
|
38
|
+
const linked = [];
|
|
39
|
+
for (const source of input.sources) {
|
|
40
|
+
const result = await autoLinkHomeGraphSource({
|
|
41
|
+
store: input.store,
|
|
42
|
+
spaceId: input.spaceId,
|
|
43
|
+
installationId: input.installationId,
|
|
44
|
+
source,
|
|
45
|
+
extraction: input.extractionBySourceId.get(source.id),
|
|
46
|
+
state: {
|
|
47
|
+
...input.state,
|
|
48
|
+
edges: [...input.state.edges, ...linked.map((entry) => entry.edge)],
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
if (result)
|
|
52
|
+
linked.push(result);
|
|
53
|
+
}
|
|
54
|
+
return linked;
|
|
55
|
+
}
|
|
56
|
+
function scoreCandidates(source, extraction, state) {
|
|
57
|
+
const sourceText = sourceEvidenceText(source, extraction);
|
|
58
|
+
if (sourceText.length < MIN_SOURCE_TEXT_CHARS)
|
|
59
|
+
return [];
|
|
60
|
+
const lower = sourceText.toLowerCase();
|
|
61
|
+
const sourceTokens = new Set(tokenize(sourceText));
|
|
62
|
+
return state.nodes
|
|
63
|
+
.filter((node) => isAutoLinkCandidateNode(node))
|
|
64
|
+
.map((node) => {
|
|
65
|
+
const reasons = [];
|
|
66
|
+
let score = 0;
|
|
67
|
+
const identity = nodeIdentity(node, state);
|
|
68
|
+
for (const model of identity.models) {
|
|
69
|
+
if (model.length >= 4 && includesIdentity(lower, model)) {
|
|
70
|
+
score += model.length >= 8 ? 180 : 120;
|
|
71
|
+
reasons.push(`exact-model:${model}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const entityId of identity.entityIds) {
|
|
75
|
+
if (includesIdentity(lower, entityId)) {
|
|
76
|
+
score += 140;
|
|
77
|
+
reasons.push(`entity-id:${entityId}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const deviceId of identity.deviceIds) {
|
|
81
|
+
if (includesIdentity(lower, deviceId)) {
|
|
82
|
+
score += 120;
|
|
83
|
+
reasons.push(`device-id:${deviceId}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const titleTokens = tokenize(node.title).filter((token) => !isGenericToken(token));
|
|
87
|
+
const overlap = titleTokens.filter((token) => sourceTokens.has(token));
|
|
88
|
+
if (overlap.length > 0) {
|
|
89
|
+
score += overlap.reduce((sum, token) => sum + (token.length <= 3 ? 12 : 18), 0);
|
|
90
|
+
reasons.push(`title:${overlap.slice(0, 5).join(',')}`);
|
|
91
|
+
}
|
|
92
|
+
const manufacturerMatches = identity.manufacturers.filter((manufacturer) => includesIdentity(lower, manufacturer));
|
|
93
|
+
if (manufacturerMatches.length > 0) {
|
|
94
|
+
score += 28;
|
|
95
|
+
reasons.push(`manufacturer:${manufacturerMatches[0]}`);
|
|
96
|
+
}
|
|
97
|
+
const relatedEntityMatches = identity.relatedEntityTokens.filter((token) => sourceTokens.has(token));
|
|
98
|
+
if (relatedEntityMatches.length > 0) {
|
|
99
|
+
score += Math.min(50, relatedEntityMatches.length * 10);
|
|
100
|
+
reasons.push(`related-entity:${relatedEntityMatches.slice(0, 5).join(',')}`);
|
|
101
|
+
}
|
|
102
|
+
if (node.kind === 'ha_device' && isManualLikeSource(source))
|
|
103
|
+
score += 16;
|
|
104
|
+
if (node.kind === 'ha_integration' && isIntegrationDocumentationSource(source))
|
|
105
|
+
score += 40;
|
|
106
|
+
if (node.kind === 'ha_integration' && isManualLikeSource(source))
|
|
107
|
+
score -= 45;
|
|
108
|
+
return { node, score, reasons };
|
|
109
|
+
})
|
|
110
|
+
.filter((entry) => entry.score > 0)
|
|
111
|
+
.sort((left, right) => right.score - left.score || left.node.id.localeCompare(right.node.id));
|
|
112
|
+
}
|
|
113
|
+
function shouldSkipAutoLink(source) {
|
|
114
|
+
const kind = typeof source.metadata.homeGraphSourceKind === 'string' ? source.metadata.homeGraphSourceKind : '';
|
|
115
|
+
return kind === 'snapshot' || kind === 'generated-page';
|
|
116
|
+
}
|
|
117
|
+
function hasActiveSourceLink(sourceId, edges) {
|
|
118
|
+
return edges.some((edge) => edgeIsActive(edge) && ((edge.fromKind === 'source' && edge.fromId === sourceId && edge.toKind === 'node')
|
|
119
|
+
|| (edge.fromKind === 'node' && edge.toKind === 'source' && edge.toId === sourceId)));
|
|
120
|
+
}
|
|
121
|
+
function isAutoLinkCandidateNode(node) {
|
|
122
|
+
return node.kind === 'ha_device'
|
|
123
|
+
|| node.kind === 'ha_entity'
|
|
124
|
+
|| node.kind === 'ha_integration'
|
|
125
|
+
|| node.kind === 'ha_area'
|
|
126
|
+
|| node.kind === 'ha_room';
|
|
127
|
+
}
|
|
128
|
+
function sourceEvidenceText(source, extraction) {
|
|
129
|
+
const structure = readRecord(extraction?.structure);
|
|
130
|
+
return uniqueStrings([
|
|
131
|
+
source.title,
|
|
132
|
+
source.summary,
|
|
133
|
+
source.description,
|
|
134
|
+
source.sourceUri,
|
|
135
|
+
source.canonicalUri,
|
|
136
|
+
source.tags.join(' '),
|
|
137
|
+
extraction?.title,
|
|
138
|
+
extraction?.summary,
|
|
139
|
+
extraction?.excerpt,
|
|
140
|
+
...(extraction?.sections ?? []),
|
|
141
|
+
readString(structure.searchText),
|
|
142
|
+
]).join('\n').slice(0, MAX_TEXT_FIELD_CHARS);
|
|
143
|
+
}
|
|
144
|
+
function nodeIdentity(node, state) {
|
|
145
|
+
const homeAssistant = readRecord(node.metadata.homeAssistant);
|
|
146
|
+
const relatedEntities = node.kind === 'ha_device'
|
|
147
|
+
? relatedEntityNodes(node.id, state)
|
|
148
|
+
: [];
|
|
149
|
+
return {
|
|
150
|
+
models: uniqueStrings([
|
|
151
|
+
readString(node.metadata.model),
|
|
152
|
+
readString(node.metadata.modelId),
|
|
153
|
+
readString(node.metadata.model_id),
|
|
154
|
+
]),
|
|
155
|
+
manufacturers: uniqueStrings([
|
|
156
|
+
readString(node.metadata.manufacturer),
|
|
157
|
+
readString(node.metadata.vendor),
|
|
158
|
+
]),
|
|
159
|
+
entityIds: uniqueStrings([
|
|
160
|
+
readString(homeAssistant.entityId),
|
|
161
|
+
...relatedEntities.map((entity) => readString(readRecord(entity.metadata.homeAssistant).entityId)),
|
|
162
|
+
]),
|
|
163
|
+
deviceIds: uniqueStrings([
|
|
164
|
+
readString(homeAssistant.deviceId),
|
|
165
|
+
node.kind === 'ha_device' ? readString(homeAssistant.objectId) : undefined,
|
|
166
|
+
]),
|
|
167
|
+
relatedEntityTokens: uniqueStrings(relatedEntities.flatMap((entity) => [
|
|
168
|
+
...tokenize(entity.title),
|
|
169
|
+
...tokenize(readString(readRecord(entity.metadata.homeAssistant).entityId) ?? ''),
|
|
170
|
+
readString(entity.metadata.domain),
|
|
171
|
+
readString(entity.metadata.platform),
|
|
172
|
+
])),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function relatedEntityNodes(deviceNodeId, state) {
|
|
176
|
+
const entityIds = new Set(state.edges.filter((edge) => (edgeIsActive(edge)
|
|
177
|
+
&& edge.fromKind === 'node'
|
|
178
|
+
&& edge.toKind === 'node'
|
|
179
|
+
&& edge.toId === deviceNodeId
|
|
180
|
+
&& edge.relation === 'belongs_to_device')).map((edge) => edge.fromId));
|
|
181
|
+
return state.nodes.filter((node) => entityIds.has(node.id) && node.kind === 'ha_entity');
|
|
182
|
+
}
|
|
183
|
+
function inferRelation(source) {
|
|
184
|
+
const tags = source.tags.map((tag) => tag.toLowerCase());
|
|
185
|
+
const text = [source.sourceType, source.title, source.sourceUri, ...tags].join(' ').toLowerCase();
|
|
186
|
+
if (text.includes('receipt'))
|
|
187
|
+
return 'has_receipt';
|
|
188
|
+
if (text.includes('warranty'))
|
|
189
|
+
return 'has_warranty';
|
|
190
|
+
if (isManualLikeSource(source))
|
|
191
|
+
return 'has_manual';
|
|
192
|
+
return 'source_for';
|
|
193
|
+
}
|
|
194
|
+
function isManualLikeSource(source) {
|
|
195
|
+
const tags = source.tags.map((tag) => tag.toLowerCase());
|
|
196
|
+
const text = [source.sourceType, source.title, source.sourceUri, ...tags].join(' ').toLowerCase();
|
|
197
|
+
return source.sourceType === 'manual'
|
|
198
|
+
|| tags.includes('manual')
|
|
199
|
+
|| tags.includes('artifact')
|
|
200
|
+
|| tags.includes('document')
|
|
201
|
+
|| /\bmanual\b|\.pdf\b|owner.?s guide|user guide/.test(text);
|
|
202
|
+
}
|
|
203
|
+
function isIntegrationDocumentationSource(source) {
|
|
204
|
+
const tags = source.tags.map((tag) => tag.toLowerCase());
|
|
205
|
+
const kind = typeof source.metadata.homeGraphSourceKind === 'string'
|
|
206
|
+
? source.metadata.homeGraphSourceKind.toLowerCase()
|
|
207
|
+
: '';
|
|
208
|
+
return tags.includes('integration') || tags.includes('documentation') || kind === 'documentation-candidate';
|
|
209
|
+
}
|
|
210
|
+
function tokenize(value) {
|
|
211
|
+
return uniqueStrings(value.toLowerCase().split(/[^a-z0-9_.:-]+/).filter((token) => token.length >= 2));
|
|
212
|
+
}
|
|
213
|
+
function isGenericToken(value) {
|
|
214
|
+
return GENERIC_TOKENS.has(value);
|
|
215
|
+
}
|
|
216
|
+
function includesIdentity(haystack, identity) {
|
|
217
|
+
const normalized = identity.trim().toLowerCase();
|
|
218
|
+
if (!normalized)
|
|
219
|
+
return false;
|
|
220
|
+
if (/^[a-z0-9_.:-]+$/.test(normalized)) {
|
|
221
|
+
return new RegExp(`(?:^|[^a-z0-9])${escapeRegExp(normalized)}(?:$|[^a-z0-9])`).test(haystack);
|
|
222
|
+
}
|
|
223
|
+
return haystack.includes(normalized);
|
|
224
|
+
}
|
|
225
|
+
function readString(value) {
|
|
226
|
+
if (typeof value === 'string') {
|
|
227
|
+
const trimmed = value.trim();
|
|
228
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
229
|
+
}
|
|
230
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
231
|
+
return String(value);
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
function escapeRegExp(value) {
|
|
235
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
236
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-quality.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/extraction-quality.ts"],"names":[],"mappings":"AAAA,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAYpF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function isUnusableHomeGraphExtractionText(value) {
|
|
2
|
+
const text = value?.trim() ?? '';
|
|
3
|
+
if (!text)
|
|
4
|
+
return true;
|
|
5
|
+
const normalized = text.toLowerCase();
|
|
6
|
+
if (normalized.includes('pdf extraction produced limited text')
|
|
7
|
+
|| normalized.includes('no readable text streams')
|
|
8
|
+
|| normalized.includes('no specialized extractor matched')
|
|
9
|
+
|| normalized.includes('has no specialized in-core extractor')) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (looksLikeRawPdfPayload(text))
|
|
13
|
+
return true;
|
|
14
|
+
return looksBinaryLike(text);
|
|
15
|
+
}
|
|
16
|
+
function looksLikeRawPdfPayload(value) {
|
|
17
|
+
const lower = value.toLowerCase();
|
|
18
|
+
return lower.includes('%pdf')
|
|
19
|
+
|| /\b\d+\s+\d+\s+obj\b/.test(lower)
|
|
20
|
+
|| (lower.includes(' endobj') && lower.includes(' stream'))
|
|
21
|
+
|| (lower.includes('/filter') && lower.includes('/flatedecode'));
|
|
22
|
+
}
|
|
23
|
+
function looksBinaryLike(value) {
|
|
24
|
+
const sample = value.slice(0, 4_096);
|
|
25
|
+
if (sample.length < 120)
|
|
26
|
+
return false;
|
|
27
|
+
let control = 0;
|
|
28
|
+
let extended = 0;
|
|
29
|
+
let letters = 0;
|
|
30
|
+
let whitespace = 0;
|
|
31
|
+
let punctuation = 0;
|
|
32
|
+
for (const char of sample) {
|
|
33
|
+
const code = char.charCodeAt(0);
|
|
34
|
+
if ((code < 32 && char !== '\n' && char !== '\r' && char !== '\t') || code === 65533)
|
|
35
|
+
control += 1;
|
|
36
|
+
if (code > 126)
|
|
37
|
+
extended += 1;
|
|
38
|
+
if (/[a-z0-9]/i.test(char))
|
|
39
|
+
letters += 1;
|
|
40
|
+
if (/\s/.test(char))
|
|
41
|
+
whitespace += 1;
|
|
42
|
+
if (/[^a-z0-9\s]/i.test(char))
|
|
43
|
+
punctuation += 1;
|
|
44
|
+
}
|
|
45
|
+
const length = sample.length;
|
|
46
|
+
const extendedRatio = extended / length;
|
|
47
|
+
const usefulRatio = (letters + whitespace) / length;
|
|
48
|
+
const punctuationRatio = punctuation / length;
|
|
49
|
+
return control > 0
|
|
50
|
+
|| (extendedRatio > 0.18 && usefulRatio < 0.78)
|
|
51
|
+
|| (punctuationRatio > 0.42 && whitespace / length < 0.08);
|
|
52
|
+
}
|
|
@@ -10,6 +10,7 @@ interface HomeGraphPageContext {
|
|
|
10
10
|
export declare function generateAutomaticHomeGraphPages(context: HomeGraphPageContext & {
|
|
11
11
|
readonly input: HomeGraphSnapshotInput;
|
|
12
12
|
}): Promise<HomeGraphGeneratedPagesSummary>;
|
|
13
|
+
export declare function refreshAutomaticHomeGraphPages(context: HomeGraphPageContext): Promise<HomeGraphGeneratedPagesSummary>;
|
|
13
14
|
export declare function refreshHomeGraphDevicePassport(context: HomeGraphPageContext & {
|
|
14
15
|
readonly input: HomeGraphProjectionInput;
|
|
15
16
|
}): Promise<HomeGraphDevicePassportResult & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated-pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/generated-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8BlD,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GACzE,OAAO,CAAC,8BAA8B,CAAC,
|
|
1
|
+
{"version":3,"file":"generated-pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/generated-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8BlD,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GACzE,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AA2ED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,6BAA6B,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CA0EhF;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAsC5E;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB5E"}
|
|
@@ -3,13 +3,19 @@ import { HOME_GRAPH_CONNECTOR_ID, buildHomeGraphMetadata, edgeIsActive, homeGrap
|
|
|
3
3
|
import { findHomeAssistantNode, missingDevicePassportFields, readHomeGraphState, renderHomeGraphState, safeHomeGraphFilename, sourcesLinkedToNode, } from './state.js';
|
|
4
4
|
import { renderDevicePassportPage, renderPacketPage, renderRoomPage, } from './rendering.js';
|
|
5
5
|
export async function generateAutomaticHomeGraphPages(context) {
|
|
6
|
-
|
|
6
|
+
return generateHomeGraphPagesForCurrentState(context, context.input.pageAutomation ?? {});
|
|
7
|
+
}
|
|
8
|
+
export async function refreshAutomaticHomeGraphPages(context) {
|
|
9
|
+
return generateHomeGraphPagesForCurrentState(context, {});
|
|
10
|
+
}
|
|
11
|
+
async function generateHomeGraphPagesForCurrentState(context, options) {
|
|
12
|
+
const effectiveOptions = options ?? {};
|
|
7
13
|
const summary = createGeneratedPagesSummary();
|
|
8
|
-
if (
|
|
14
|
+
if (effectiveOptions.enabled === false)
|
|
9
15
|
return summary;
|
|
10
16
|
const state = readHomeGraphState(context.store, context.spaceId);
|
|
11
|
-
if (
|
|
12
|
-
const devices = limitRecords(state.nodes.filter((node) => node.kind === 'ha_device' && node.status !== 'stale').sort(compareByTitle),
|
|
17
|
+
if (effectiveOptions.devicePassports !== false) {
|
|
18
|
+
const devices = limitRecords(state.nodes.filter((node) => node.kind === 'ha_device' && node.status !== 'stale').sort(compareByTitle), effectiveOptions.maxDevicePassports);
|
|
13
19
|
for (const device of devices) {
|
|
14
20
|
const deviceId = readHomeAssistantObjectId(device, 'objectId', 'deviceId') ?? device.id;
|
|
15
21
|
try {
|
|
@@ -36,10 +42,10 @@ export async function generateAutomaticHomeGraphPages(context) {
|
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
}
|
|
39
|
-
if (
|
|
45
|
+
if (effectiveOptions.roomPages !== false) {
|
|
40
46
|
const rooms = limitRecords(state.nodes
|
|
41
47
|
.filter((node) => (node.kind === 'ha_area' || node.kind === 'ha_room') && node.status !== 'stale')
|
|
42
|
-
.sort(compareByTitle),
|
|
48
|
+
.sort(compareByTitle), effectiveOptions.maxRoomPages);
|
|
43
49
|
for (const room of rooms) {
|
|
44
50
|
const areaId = readHomeAssistantObjectId(room, 'objectId', 'areaId') ?? room.id;
|
|
45
51
|
try {
|
|
@@ -109,7 +115,7 @@ export async function refreshHomeGraphDevicePassport(context) {
|
|
|
109
115
|
relation: 'source_for',
|
|
110
116
|
metadata: buildHomeGraphMetadata(spaceId, installationId),
|
|
111
117
|
});
|
|
112
|
-
const markdown = renderDevicePassportPage({ spaceId, device, entities, sources, issues, missingFields });
|
|
118
|
+
const markdown = renderDevicePassportPage({ spaceId, device, entities, sources, extractions: state.extractions, issues, missingFields });
|
|
113
119
|
const generated = await materializeGeneratedMarkdown({
|
|
114
120
|
store,
|
|
115
121
|
artifactStore,
|
|
@@ -147,7 +153,7 @@ export async function generateHomeGraphRoomPage(context) {
|
|
|
147
153
|
const state = readHomeGraphState(store, spaceId);
|
|
148
154
|
const areaId = input.areaId ?? input.roomId;
|
|
149
155
|
const title = input.title ?? resolveRoomTitle(state.nodes, areaId) ?? 'Home Graph Room';
|
|
150
|
-
const markdown = renderRoomPage({ ...state, title }, areaId);
|
|
156
|
+
const markdown = renderRoomPage({ ...state, title, extractions: state.extractions }, areaId);
|
|
151
157
|
const filename = `${safeHomeGraphFilename(title)}.md`;
|
|
152
158
|
const targetNode = areaId
|
|
153
159
|
? findHomeAssistantNode(state.nodes, 'ha_area', areaId) ?? findHomeAssistantNode(state.nodes, 'ha_room', areaId)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { HomeGraphService } from './service.js';
|
|
2
2
|
export { HOME_GRAPH_NODE_KINDS, HOME_GRAPH_RELATIONS, } from './types.js';
|
|
3
|
-
export type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphGeneratedPagesSummary, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphKnowledgeTarget, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphMapEdge, HomeGraphMapInput, HomeGraphMapNode, HomeGraphMapResult, HomeGraphNodeKind, HomeGraphObjectInput, HomeGraphObjectKind, HomeGraphPageAutomationOptions, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReindexResult, HomeGraphRelation, HomeGraphReviewInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult, } from './types.js';
|
|
3
|
+
export type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphGeneratedPagesSummary, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphKnowledgeTarget, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphMapEdge, HomeGraphMapHaFilterInput, HomeGraphMapInput, HomeGraphMapNode, HomeGraphMapResult, HomeGraphNodeKind, HomeGraphObjectInput, HomeGraphObjectKind, HomeGraphPageAutomationOptions, HomeGraphPageListResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReindexResult, HomeGraphRelation, HomeGraphReviewInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult, } from './types.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,eAAe,EACf,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,eAAe,EACf,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,EAC9B,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ArtifactStore } from '../../artifacts/index.js';
|
|
2
|
+
import type { KnowledgeSourceRecord } from '../types.js';
|
|
3
|
+
import type { HomeGraphPageListResult } from './types.js';
|
|
4
|
+
export declare function listHomeGraphPages(input: {
|
|
5
|
+
readonly artifactStore: ArtifactStore;
|
|
6
|
+
readonly spaceId: string;
|
|
7
|
+
readonly sources: readonly KnowledgeSourceRecord[];
|
|
8
|
+
readonly limit: number;
|
|
9
|
+
readonly includeMarkdown: boolean;
|
|
10
|
+
}): Promise<HomeGraphPageListResult>;
|
|
11
|
+
//# sourceMappingURL=pages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA0BnC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isGeneratedPageSource } from './helpers.js';
|
|
2
|
+
export async function listHomeGraphPages(input) {
|
|
3
|
+
const pages = [];
|
|
4
|
+
const sources = input.sources
|
|
5
|
+
.filter(isGeneratedPageSource)
|
|
6
|
+
.sort(compareGeneratedPages)
|
|
7
|
+
.slice(0, input.limit);
|
|
8
|
+
for (const source of sources) {
|
|
9
|
+
const artifact = typeof source.artifactId === 'string' ? input.artifactStore.get(source.artifactId) : undefined;
|
|
10
|
+
const markdown = input.includeMarkdown && artifact
|
|
11
|
+
? await readMarkdown(input.artifactStore, artifact.id)
|
|
12
|
+
: undefined;
|
|
13
|
+
pages.push({
|
|
14
|
+
source,
|
|
15
|
+
...(artifact ? {
|
|
16
|
+
artifact: {
|
|
17
|
+
id: artifact.id,
|
|
18
|
+
mimeType: artifact.mimeType,
|
|
19
|
+
filename: artifact.filename,
|
|
20
|
+
createdAt: artifact.createdAt,
|
|
21
|
+
metadata: artifact.metadata,
|
|
22
|
+
},
|
|
23
|
+
} : {}),
|
|
24
|
+
...(markdown ? { markdown } : {}),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return { ok: true, spaceId: input.spaceId, pages };
|
|
28
|
+
}
|
|
29
|
+
function compareGeneratedPages(left, right) {
|
|
30
|
+
const leftKind = typeof left.metadata.projectionKind === 'string' ? left.metadata.projectionKind : '';
|
|
31
|
+
const rightKind = typeof right.metadata.projectionKind === 'string' ? right.metadata.projectionKind : '';
|
|
32
|
+
return leftKind.localeCompare(rightKind)
|
|
33
|
+
|| (left.title ?? left.id).localeCompare(right.title ?? right.id)
|
|
34
|
+
|| left.id.localeCompare(right.id);
|
|
35
|
+
}
|
|
36
|
+
async function readMarkdown(artifactStore, artifactId) {
|
|
37
|
+
try {
|
|
38
|
+
const { buffer } = await artifactStore.readContent(artifactId);
|
|
39
|
+
return buffer.toString('utf-8');
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { KnowledgeEdgeRecord, KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
|
|
2
|
-
import type { HomeGraphMapResult } from './types.js';
|
|
1
|
+
import type { KnowledgeEdgeRecord, KnowledgeExtractionRecord, KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
|
|
2
|
+
import type { HomeGraphMapInput, HomeGraphMapResult } from './types.js';
|
|
3
3
|
export interface HomeGraphRenderState {
|
|
4
4
|
readonly spaceId: string;
|
|
5
5
|
readonly title: string;
|
|
@@ -7,6 +7,7 @@ export interface HomeGraphRenderState {
|
|
|
7
7
|
readonly nodes: readonly KnowledgeNodeRecord[];
|
|
8
8
|
readonly edges: readonly KnowledgeEdgeRecord[];
|
|
9
9
|
readonly issues: readonly KnowledgeIssueRecord[];
|
|
10
|
+
readonly extractions?: readonly KnowledgeExtractionRecord[];
|
|
10
11
|
}
|
|
11
12
|
export declare function renderRoomPage(state: HomeGraphRenderState, areaId?: string): string;
|
|
12
13
|
export declare function renderDevicePassportPage(input: {
|
|
@@ -14,6 +15,7 @@ export declare function renderDevicePassportPage(input: {
|
|
|
14
15
|
readonly device: KnowledgeNodeRecord;
|
|
15
16
|
readonly entities: readonly KnowledgeNodeRecord[];
|
|
16
17
|
readonly sources: readonly KnowledgeSourceRecord[];
|
|
18
|
+
readonly extractions?: readonly KnowledgeExtractionRecord[];
|
|
17
19
|
readonly issues: readonly KnowledgeIssueRecord[];
|
|
18
20
|
readonly missingFields: readonly string[];
|
|
19
21
|
}): string;
|
|
@@ -23,8 +25,5 @@ export declare function renderPacketPage(state: HomeGraphRenderState, options?:
|
|
|
23
25
|
readonly includeFields?: readonly string[];
|
|
24
26
|
readonly excludeFields?: readonly string[];
|
|
25
27
|
}): string;
|
|
26
|
-
export declare function renderHomeGraphMap(state: HomeGraphRenderState, options?:
|
|
27
|
-
readonly limit?: number;
|
|
28
|
-
readonly includeSources?: boolean;
|
|
29
|
-
}): HomeGraphMapResult;
|
|
28
|
+
export declare function renderHomeGraphMap(state: HomeGraphRenderState, options?: HomeGraphMapInput): HomeGraphMapResult;
|
|
30
29
|
//# sourceMappingURL=rendering.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EAEpB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAKrB,OAAO,KAAK,EAA6B,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAC7D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAiDnF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GAAG,MAAM,CA8BT;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GAAG,MAAM,CAkBd;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAcnH"}
|