@pellux/goodvibes-sdk 0.26.8 → 0.26.10
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 +209 -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 +209 -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/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -1
- package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +11 -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 +10 -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 +3 -0
- package/dist/_internal/platform/knowledge/extractors.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/extractors.js +73 -1
- 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/reindex.d.ts +12 -0
- package/dist/_internal/platform/knowledge/home-graph/reindex.d.ts.map +1 -0
- package/dist/_internal/platform/knowledge/home-graph/reindex.js +35 -0
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts +3 -1
- package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/search.js +438 -25
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts +3 -1
- package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/home-graph/service.js +43 -6
- 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 +14 -0
- package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/ingest-compile.d.ts.map +1 -1
- package/dist/_internal/platform/knowledge/ingest-compile.js +26 -1
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +2 -1
|
@@ -2,6 +2,75 @@ 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
|
+
};
|
|
57
|
+
const SOURCE_EVIDENCE_TOKENS = new Set([
|
|
58
|
+
'battery',
|
|
59
|
+
'capabilities',
|
|
60
|
+
'capability',
|
|
61
|
+
'feature',
|
|
62
|
+
'features',
|
|
63
|
+
'manual',
|
|
64
|
+
'model',
|
|
65
|
+
'reset',
|
|
66
|
+
'serial',
|
|
67
|
+
'spec',
|
|
68
|
+
'specs',
|
|
69
|
+
'support',
|
|
70
|
+
'supports',
|
|
71
|
+
'warranty',
|
|
72
|
+
]);
|
|
73
|
+
const INTEGRATION_QUERY_TOKENS = new Set(['automation', 'automations', 'homeassistant', 'integration', 'integrations', 'webostv']);
|
|
5
74
|
export function readHomeGraphSearchState(store, spaceId) {
|
|
6
75
|
const sources = store.listSources(10_000).filter((source) => belongsToSpace(source, spaceId));
|
|
7
76
|
const nodes = store.listNodes(10_000).filter((node) => belongsToSpace(node, spaceId));
|
|
@@ -23,54 +92,122 @@ export function readHomeGraphSearchState(store, spaceId) {
|
|
|
23
92
|
}
|
|
24
93
|
return { spaceId, sources, nodes, edges, extractionBySourceId };
|
|
25
94
|
}
|
|
26
|
-
export function scoreHomeGraphResults(query, sources, nodes, extractionBySourceId, limit) {
|
|
27
|
-
const tokens =
|
|
95
|
+
export function scoreHomeGraphResults(query, sources, nodes, edges, extractionBySourceId, limit) {
|
|
96
|
+
const tokens = tokenizeQuery(query);
|
|
28
97
|
if (tokens.length === 0)
|
|
29
98
|
return [];
|
|
99
|
+
const expandedTokens = expandTokens(tokens);
|
|
100
|
+
const anchors = selectAnchorNodes(tokens, nodes);
|
|
101
|
+
const anchorIds = new Set(anchors.map((anchor) => anchor.node.id));
|
|
102
|
+
const sourceLinks = buildSourceLinkIndex(edges);
|
|
103
|
+
const useAnchorScope = anchors.length > 0 && anchors.length <= ANCHOR_SCOPE_LIMIT;
|
|
104
|
+
const sourceEvidenceQuery = queryNeedsSourceEvidence(expandedTokens);
|
|
105
|
+
const integrationQuery = queryMentionsIntegration(tokens);
|
|
30
106
|
const sourceResults = sources.map((source) => {
|
|
31
107
|
const extraction = extractionBySourceId(source.id);
|
|
32
|
-
|
|
108
|
+
if (isPendingDocumentationCandidate(source, extraction)) {
|
|
109
|
+
return sourceResult(source, extraction, 0);
|
|
110
|
+
}
|
|
111
|
+
if (sourceEvidenceQuery && !integrationQuery && isHomeAssistantIntegrationSource(source)) {
|
|
112
|
+
return sourceResult(source, extraction, 0);
|
|
113
|
+
}
|
|
114
|
+
const linkedNodeIds = sourceLinks.get(source.id) ?? new Set();
|
|
115
|
+
const linkedToAnchor = useAnchorScope && intersects(linkedNodeIds, anchorIds);
|
|
116
|
+
const identityScore = scoreFields(tokens, [
|
|
33
117
|
source.title,
|
|
34
118
|
source.summary,
|
|
35
119
|
source.description,
|
|
36
120
|
source.sourceUri,
|
|
37
121
|
source.canonicalUri,
|
|
38
122
|
source.tags.join(' '),
|
|
123
|
+
]);
|
|
124
|
+
const contentScore = scoreFields(expandedTokens, [
|
|
39
125
|
extraction?.title,
|
|
40
126
|
extraction?.summary,
|
|
41
127
|
extraction?.excerpt,
|
|
42
128
|
readSearchText(extraction),
|
|
43
129
|
...limitedSections(extraction),
|
|
44
130
|
]);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
131
|
+
const baseScore = identityScore + contentScore;
|
|
132
|
+
const linkBoost = linkedToAnchor ? 120 + relationBoost(source.id, anchorIds, edges) : 0;
|
|
133
|
+
const manualBoost = isManualLikeSource(source) ? 24 : 0;
|
|
134
|
+
const indexedBoost = source.status === 'indexed' ? 18 : source.status === 'stale' ? 6 : 0;
|
|
135
|
+
const extractionBoost = extraction ? 20 : 0;
|
|
136
|
+
const score = baseScore > 0 || linkBoost > 0
|
|
137
|
+
? baseScore + linkBoost + manualBoost + indexedBoost + extractionBoost
|
|
138
|
+
: 0;
|
|
139
|
+
return sourceResult(source, extraction, score, selectRelevantExcerpt(expandedTokens, source, extraction));
|
|
53
140
|
});
|
|
54
141
|
const nodeResults = nodes.map((node) => {
|
|
55
|
-
const baseScore = scoreFields(tokens,
|
|
56
|
-
|
|
57
|
-
node.summary,
|
|
58
|
-
node.aliases.join(' '),
|
|
59
|
-
readNodeMetadataText(node),
|
|
60
|
-
]);
|
|
142
|
+
const baseScore = scoreFields(tokens, nodeIdentityFields(node));
|
|
143
|
+
const anchorBoost = anchorIds.has(node.id) ? 40 + nodeKindBoost(node.kind) : 0;
|
|
61
144
|
return {
|
|
62
145
|
kind: 'node',
|
|
63
146
|
id: node.id,
|
|
64
|
-
score: baseScore > 0 ? baseScore + Math.round(node.confidence / 20) : 0,
|
|
147
|
+
score: baseScore > 0 ? baseScore + anchorBoost + Math.round(node.confidence / 20) : 0,
|
|
65
148
|
title: node.title,
|
|
66
149
|
summary: node.summary,
|
|
150
|
+
excerpt: node.summary,
|
|
67
151
|
node,
|
|
68
152
|
};
|
|
69
153
|
});
|
|
70
|
-
|
|
154
|
+
let results = [...sourceResults, ...nodeResults]
|
|
155
|
+
.filter((entry) => entry.score > 0)
|
|
156
|
+
.sort(compareHomeGraphResults);
|
|
157
|
+
const anchoredSourceResults = useAnchorScope
|
|
158
|
+
? results.filter((result) => result.source && intersects(sourceLinks.get(result.source.id) ?? new Set(), anchorIds))
|
|
159
|
+
: [];
|
|
160
|
+
if (anchoredSourceResults.length > 0) {
|
|
161
|
+
results = anchoredSourceResults.sort(compareHomeGraphResults);
|
|
162
|
+
}
|
|
163
|
+
if (sourceEvidenceQuery) {
|
|
164
|
+
results = pruneWeakSourceEvidence(results, tokens, sourceLinks, anchorIds);
|
|
165
|
+
}
|
|
166
|
+
const strongResults = pruneWeakTokenCoverage(results, tokens);
|
|
167
|
+
return strongResults.slice(0, Math.max(1, limit));
|
|
168
|
+
}
|
|
169
|
+
export function selectHomeGraphExtractionRepairCandidates(query, sources, nodes, edges, extractionBySourceId, limit) {
|
|
170
|
+
const tokens = tokenizeQuery(query);
|
|
171
|
+
if (tokens.length === 0)
|
|
172
|
+
return [];
|
|
173
|
+
const anchors = selectAnchorNodes(tokens, nodes);
|
|
174
|
+
const anchorIds = new Set(anchors.map((anchor) => anchor.node.id));
|
|
175
|
+
const sourceLinks = buildSourceLinkIndex(edges);
|
|
176
|
+
return sources
|
|
177
|
+
.map((source) => {
|
|
178
|
+
if (!source.artifactId || !homeGraphExtractionNeedsRepair(extractionBySourceId(source.id)))
|
|
179
|
+
return { source, score: 0 };
|
|
180
|
+
const linkedNodeIds = sourceLinks.get(source.id) ?? new Set();
|
|
181
|
+
const linkedToAnchor = anchors.length > 0 && intersects(linkedNodeIds, anchorIds);
|
|
182
|
+
const identityScore = scoreFields(tokens, [
|
|
183
|
+
source.title,
|
|
184
|
+
source.summary,
|
|
185
|
+
source.description,
|
|
186
|
+
source.sourceUri,
|
|
187
|
+
source.canonicalUri,
|
|
188
|
+
source.tags.join(' '),
|
|
189
|
+
]);
|
|
190
|
+
const sourceKindBoost = isManualLikeSource(source) ? 30 : 0;
|
|
191
|
+
const score = linkedToAnchor
|
|
192
|
+
? 140 + relationBoost(source.id, anchorIds, edges) + sourceKindBoost + identityScore
|
|
193
|
+
: identityScore > 0 ? identityScore + sourceKindBoost : 0;
|
|
194
|
+
return { source, score };
|
|
195
|
+
})
|
|
71
196
|
.filter((entry) => entry.score > 0)
|
|
72
|
-
.sort((
|
|
73
|
-
.slice(0, Math.max(1, limit))
|
|
197
|
+
.sort((left, right) => right.score - left.score || left.source.id.localeCompare(right.source.id))
|
|
198
|
+
.slice(0, Math.max(1, limit))
|
|
199
|
+
.map((entry) => entry.source);
|
|
200
|
+
}
|
|
201
|
+
function sourceResult(source, extraction, score, excerpt) {
|
|
202
|
+
return {
|
|
203
|
+
kind: 'source',
|
|
204
|
+
id: source.id,
|
|
205
|
+
score,
|
|
206
|
+
title: source.title ?? source.sourceUri ?? source.id,
|
|
207
|
+
summary: usefulExtractionSummary(extraction) ?? source.summary,
|
|
208
|
+
...(excerpt ? { excerpt } : {}),
|
|
209
|
+
source,
|
|
210
|
+
};
|
|
74
211
|
}
|
|
75
212
|
function limitedSections(extraction) {
|
|
76
213
|
if (!extraction)
|
|
@@ -103,6 +240,238 @@ function readNodeMetadataText(node) {
|
|
|
103
240
|
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
104
241
|
return values.length > 0 ? values.join(' ') : undefined;
|
|
105
242
|
}
|
|
243
|
+
function nodeIdentityFields(node) {
|
|
244
|
+
return [
|
|
245
|
+
node.title,
|
|
246
|
+
node.summary,
|
|
247
|
+
node.aliases.join(' '),
|
|
248
|
+
readNodeMetadataText(node),
|
|
249
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
250
|
+
}
|
|
251
|
+
function selectAnchorNodes(tokens, nodes) {
|
|
252
|
+
return nodes.map((node) => {
|
|
253
|
+
const baseScore = scoreFields(tokens, nodeIdentityFields(node));
|
|
254
|
+
return {
|
|
255
|
+
node,
|
|
256
|
+
score: baseScore > 0 ? baseScore + nodeKindBoost(node.kind) : 0,
|
|
257
|
+
};
|
|
258
|
+
})
|
|
259
|
+
.filter((entry) => entry.score >= 10)
|
|
260
|
+
.sort((a, b) => b.score - a.score || a.node.id.localeCompare(b.node.id))
|
|
261
|
+
.slice(0, 12);
|
|
262
|
+
}
|
|
263
|
+
function buildSourceLinkIndex(edges) {
|
|
264
|
+
const links = new Map();
|
|
265
|
+
for (const edge of edges) {
|
|
266
|
+
if (edge.fromKind === 'source' && edge.toKind === 'node') {
|
|
267
|
+
addSourceLink(links, edge.fromId, edge.toId);
|
|
268
|
+
}
|
|
269
|
+
else if (edge.fromKind === 'node' && edge.toKind === 'source') {
|
|
270
|
+
addSourceLink(links, edge.toId, edge.fromId);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return links;
|
|
274
|
+
}
|
|
275
|
+
function addSourceLink(links, sourceId, nodeId) {
|
|
276
|
+
const current = links.get(sourceId) ?? new Set();
|
|
277
|
+
current.add(nodeId);
|
|
278
|
+
links.set(sourceId, current);
|
|
279
|
+
}
|
|
280
|
+
function relationBoost(sourceId, anchorIds, edges) {
|
|
281
|
+
let boost = 0;
|
|
282
|
+
for (const edge of edges) {
|
|
283
|
+
const connectsAnchor = (edge.fromKind === 'source' && edge.fromId === sourceId && edge.toKind === 'node' && anchorIds.has(edge.toId))
|
|
284
|
+
|| (edge.fromKind === 'node' && anchorIds.has(edge.fromId) && edge.toKind === 'source' && edge.toId === sourceId);
|
|
285
|
+
if (!connectsAnchor)
|
|
286
|
+
continue;
|
|
287
|
+
if (edge.relation === 'has_manual')
|
|
288
|
+
boost = Math.max(boost, 45);
|
|
289
|
+
else if (edge.relation === 'source_for')
|
|
290
|
+
boost = Math.max(boost, 25);
|
|
291
|
+
else
|
|
292
|
+
boost = Math.max(boost, 15);
|
|
293
|
+
}
|
|
294
|
+
return boost;
|
|
295
|
+
}
|
|
296
|
+
function nodeKindBoost(kind) {
|
|
297
|
+
switch (kind) {
|
|
298
|
+
case 'ha_device':
|
|
299
|
+
case 'ha_entity':
|
|
300
|
+
return 20;
|
|
301
|
+
case 'ha_area':
|
|
302
|
+
case 'ha_room':
|
|
303
|
+
case 'ha_automation':
|
|
304
|
+
case 'ha_script':
|
|
305
|
+
case 'ha_scene':
|
|
306
|
+
return 12;
|
|
307
|
+
case 'ha_integration':
|
|
308
|
+
return 6;
|
|
309
|
+
default:
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function intersects(left, right) {
|
|
314
|
+
for (const value of left) {
|
|
315
|
+
if (right.has(value))
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
function isPendingDocumentationCandidate(source, extraction) {
|
|
321
|
+
return !extraction
|
|
322
|
+
&& source.status !== 'indexed'
|
|
323
|
+
&& source.metadata.homeGraphSourceKind === 'documentation-candidate';
|
|
324
|
+
}
|
|
325
|
+
export function homeGraphExtractionNeedsRepair(extraction) {
|
|
326
|
+
if (!extraction)
|
|
327
|
+
return true;
|
|
328
|
+
if (extraction.format === 'pdf' && extraction.extractorId !== 'pdfjs')
|
|
329
|
+
return true;
|
|
330
|
+
const searchText = readSearchText(extraction);
|
|
331
|
+
if (searchText && searchText.trim().length > 0)
|
|
332
|
+
return false;
|
|
333
|
+
if ((extraction.excerpt?.trim() && !isLowInformationExtractionText(extraction.excerpt))
|
|
334
|
+
|| (extraction.summary?.trim() && !isLowInformationExtractionText(extraction.summary))
|
|
335
|
+
|| extraction.sections.some((section) => section.trim() && !isLowInformationExtractionText(section))) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
function queryNeedsSourceEvidence(tokens) {
|
|
341
|
+
return tokens.some((token) => SOURCE_EVIDENCE_TOKENS.has(token));
|
|
342
|
+
}
|
|
343
|
+
function queryMentionsIntegration(tokens) {
|
|
344
|
+
return tokens.some((token) => INTEGRATION_QUERY_TOKENS.has(token));
|
|
345
|
+
}
|
|
346
|
+
function isManualLikeSource(source) {
|
|
347
|
+
const tags = source.tags.map((tag) => tag.toLowerCase());
|
|
348
|
+
return source.sourceType === 'manual'
|
|
349
|
+
|| source.sourceType === 'document'
|
|
350
|
+
|| source.sourceType === 'url'
|
|
351
|
+
|| tags.includes('manual')
|
|
352
|
+
|| tags.includes('artifact')
|
|
353
|
+
|| tags.includes('document');
|
|
354
|
+
}
|
|
355
|
+
function isHomeAssistantIntegrationSource(source) {
|
|
356
|
+
const tags = source.tags.map((tag) => tag.toLowerCase());
|
|
357
|
+
const sourceKind = typeof source.metadata.homeGraphSourceKind === 'string'
|
|
358
|
+
? source.metadata.homeGraphSourceKind.toLowerCase()
|
|
359
|
+
: '';
|
|
360
|
+
return tags.includes('integration')
|
|
361
|
+
|| tags.includes('documentation')
|
|
362
|
+
|| sourceKind === 'documentation-candidate';
|
|
363
|
+
}
|
|
364
|
+
function pruneWeakSourceEvidence(results, tokens, sourceLinks, anchorIds) {
|
|
365
|
+
const sourceResults = results.filter((result) => result.source);
|
|
366
|
+
if (sourceResults.length === 0)
|
|
367
|
+
return [...results];
|
|
368
|
+
const strongSourceResults = sourceResults.filter((result) => {
|
|
369
|
+
const source = result.source;
|
|
370
|
+
if (!source)
|
|
371
|
+
return false;
|
|
372
|
+
if (!hasUsefulSourceAnswerText(result))
|
|
373
|
+
return false;
|
|
374
|
+
const linkedToAnchor = intersects(sourceLinks.get(source.id) ?? new Set(), anchorIds);
|
|
375
|
+
return linkedToAnchor || tokenCoverage(tokens, resultText(result)) >= Math.min(2, tokens.length);
|
|
376
|
+
});
|
|
377
|
+
return strongSourceResults;
|
|
378
|
+
}
|
|
379
|
+
function hasUsefulSourceAnswerText(result) {
|
|
380
|
+
const detail = result.excerpt ?? result.summary ?? result.source?.description;
|
|
381
|
+
return typeof detail === 'string' && detail.trim().length > 0 && !isLowInformationExtractionText(detail);
|
|
382
|
+
}
|
|
383
|
+
function selectRelevantExcerpt(tokens, source, extraction) {
|
|
384
|
+
const chunks = candidateExcerptChunks(source, extraction);
|
|
385
|
+
let best;
|
|
386
|
+
for (const chunk of chunks) {
|
|
387
|
+
const text = cleanWhitespace(chunk);
|
|
388
|
+
if (!text || isLowInformationExtractionText(text))
|
|
389
|
+
continue;
|
|
390
|
+
const score = scoreFields(tokens, [text]);
|
|
391
|
+
if (!best || score > best.score || (score === best.score && text.length < best.text.length)) {
|
|
392
|
+
best = { score, text };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (!best || best.score <= 0) {
|
|
396
|
+
return firstBoundedText(chunks.filter((chunk) => !isLowInformationExtractionText(chunk)), MAX_ANSWER_EXCERPT_CHARS);
|
|
397
|
+
}
|
|
398
|
+
return clampAroundBestToken(best.text, tokens, MAX_ANSWER_EXCERPT_CHARS);
|
|
399
|
+
}
|
|
400
|
+
function candidateExcerptChunks(source, extraction) {
|
|
401
|
+
const searchText = readSearchText(extraction);
|
|
402
|
+
return [
|
|
403
|
+
extraction?.excerpt,
|
|
404
|
+
extraction?.summary,
|
|
405
|
+
...limitedSections(extraction),
|
|
406
|
+
...sentenceChunks(searchText),
|
|
407
|
+
source.description,
|
|
408
|
+
source.summary,
|
|
409
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
410
|
+
}
|
|
411
|
+
function usefulExtractionSummary(extraction) {
|
|
412
|
+
const summary = extraction?.summary?.trim();
|
|
413
|
+
return summary && !isLowInformationExtractionText(summary) ? summary : undefined;
|
|
414
|
+
}
|
|
415
|
+
function isLowInformationExtractionText(value) {
|
|
416
|
+
const normalized = value.toLowerCase();
|
|
417
|
+
return normalized.includes('pdf extraction produced limited text')
|
|
418
|
+
|| normalized.includes('no readable text streams')
|
|
419
|
+
|| normalized.includes('no specialized extractor matched')
|
|
420
|
+
|| normalized.includes('has no specialized in-core extractor');
|
|
421
|
+
}
|
|
422
|
+
function sentenceChunks(value) {
|
|
423
|
+
const text = cleanWhitespace(value ?? '');
|
|
424
|
+
if (!text)
|
|
425
|
+
return [];
|
|
426
|
+
const matches = text.match(/[^.!?\n]+[.!?]?/g) ?? [text];
|
|
427
|
+
return matches.map((entry) => entry.trim()).filter(Boolean).slice(0, 80);
|
|
428
|
+
}
|
|
429
|
+
function clampAroundBestToken(value, tokens, maxLength) {
|
|
430
|
+
const text = cleanWhitespace(value);
|
|
431
|
+
if (text.length <= maxLength)
|
|
432
|
+
return text;
|
|
433
|
+
const lower = text.toLowerCase();
|
|
434
|
+
const index = tokens
|
|
435
|
+
.map((token) => lower.indexOf(token.toLowerCase()))
|
|
436
|
+
.filter((entry) => entry >= 0)
|
|
437
|
+
.sort((a, b) => a - b)[0] ?? 0;
|
|
438
|
+
const start = Math.max(0, index - Math.floor(maxLength / 3));
|
|
439
|
+
const end = Math.min(text.length, start + maxLength);
|
|
440
|
+
const prefix = start > 0 ? '...' : '';
|
|
441
|
+
const suffix = end < text.length ? '...' : '';
|
|
442
|
+
return `${prefix}${text.slice(start, end).trim()}${suffix}`;
|
|
443
|
+
}
|
|
444
|
+
function cleanWhitespace(value) {
|
|
445
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
446
|
+
}
|
|
447
|
+
function pruneWeakTokenCoverage(results, tokens) {
|
|
448
|
+
if (results.length <= 1 || tokens.length <= 1)
|
|
449
|
+
return [...results];
|
|
450
|
+
const topCoverage = tokenCoverage(tokens, resultText(results[0]));
|
|
451
|
+
if (topCoverage < 2)
|
|
452
|
+
return [...results];
|
|
453
|
+
return results.filter((result) => tokenCoverage(tokens, resultText(result)) >= Math.max(1, topCoverage - 1));
|
|
454
|
+
}
|
|
455
|
+
function tokenCoverage(tokens, text) {
|
|
456
|
+
const haystack = text.toLowerCase();
|
|
457
|
+
let count = 0;
|
|
458
|
+
for (const token of tokens) {
|
|
459
|
+
if (fieldIncludesToken(haystack, token))
|
|
460
|
+
count += 1;
|
|
461
|
+
}
|
|
462
|
+
return count;
|
|
463
|
+
}
|
|
464
|
+
function resultText(result) {
|
|
465
|
+
return [
|
|
466
|
+
result.title,
|
|
467
|
+
result.summary,
|
|
468
|
+
result.excerpt,
|
|
469
|
+
result.source?.description,
|
|
470
|
+
result.source?.sourceUri,
|
|
471
|
+
result.source?.canonicalUri,
|
|
472
|
+
result.source?.tags.join(' '),
|
|
473
|
+
].filter((value) => typeof value === 'string').join(' ');
|
|
474
|
+
}
|
|
106
475
|
function firstBoundedText(values, maxLength) {
|
|
107
476
|
for (const value of values) {
|
|
108
477
|
if (typeof value !== 'string')
|
|
@@ -117,8 +486,31 @@ function firstBoundedText(values, maxLength) {
|
|
|
117
486
|
function clampText(value, maxLength) {
|
|
118
487
|
return value.length <= maxLength ? value : value.slice(0, maxLength);
|
|
119
488
|
}
|
|
120
|
-
function
|
|
121
|
-
|
|
489
|
+
function tokenizeQuery(value) {
|
|
490
|
+
const tokens = value.toLowerCase()
|
|
491
|
+
.split(/[^a-z0-9_.:-]+/)
|
|
492
|
+
.map((entry) => entry.trim())
|
|
493
|
+
.filter((entry) => isMeaningfulToken(entry));
|
|
494
|
+
return [...new Set(tokens)];
|
|
495
|
+
}
|
|
496
|
+
function expandTokens(tokens) {
|
|
497
|
+
const expanded = new Set();
|
|
498
|
+
for (const token of tokens) {
|
|
499
|
+
expanded.add(token);
|
|
500
|
+
for (const synonym of QUERY_EXPANSIONS[token] ?? []) {
|
|
501
|
+
expanded.add(synonym);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return [...expanded];
|
|
505
|
+
}
|
|
506
|
+
function isMeaningfulToken(token) {
|
|
507
|
+
if (!token || STOPWORDS.has(token))
|
|
508
|
+
return false;
|
|
509
|
+
if (token.length === 1)
|
|
510
|
+
return false;
|
|
511
|
+
if (token.length <= 2 && !SHORT_MEANINGFUL_TOKENS.has(token))
|
|
512
|
+
return false;
|
|
513
|
+
return true;
|
|
122
514
|
}
|
|
123
515
|
function scoreFields(tokens, fields) {
|
|
124
516
|
let score = 0;
|
|
@@ -128,9 +520,30 @@ function scoreFields(tokens, fields) {
|
|
|
128
520
|
if (!haystack)
|
|
129
521
|
continue;
|
|
130
522
|
for (const token of tokens) {
|
|
131
|
-
if (haystack
|
|
132
|
-
score += 10;
|
|
523
|
+
if (fieldIncludesToken(haystack, token))
|
|
524
|
+
score += token.length <= 3 ? 14 : 10;
|
|
133
525
|
}
|
|
134
526
|
}
|
|
135
527
|
return score;
|
|
136
528
|
}
|
|
529
|
+
function fieldIncludesToken(haystack, token) {
|
|
530
|
+
if (token.length <= 3 || token.includes('_') || token.includes('-')) {
|
|
531
|
+
return new RegExp(`(?:^|[^a-z0-9])${escapeRegExp(token)}(?:$|[^a-z0-9])`).test(haystack);
|
|
532
|
+
}
|
|
533
|
+
return haystack.includes(token);
|
|
534
|
+
}
|
|
535
|
+
function escapeRegExp(value) {
|
|
536
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
537
|
+
}
|
|
538
|
+
function compareHomeGraphResults(left, right) {
|
|
539
|
+
return right.score - left.score
|
|
540
|
+
|| resultKindPriority(right) - resultKindPriority(left)
|
|
541
|
+
|| left.id.localeCompare(right.id);
|
|
542
|
+
}
|
|
543
|
+
function resultKindPriority(result) {
|
|
544
|
+
if (result.source)
|
|
545
|
+
return 2;
|
|
546
|
+
if (result.node)
|
|
547
|
+
return 1;
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
@@ -2,7 +2,7 @@ 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
4
|
import { type HomeGraphReviewResult } from './review.js';
|
|
5
|
-
import type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReviewInput, HomeGraphSpaceInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult } from './types.js';
|
|
5
|
+
import type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReindexResult, HomeGraphReviewInput, HomeGraphSpaceInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult } from './types.js';
|
|
6
6
|
export declare class HomeGraphService {
|
|
7
7
|
private readonly store;
|
|
8
8
|
private readonly artifactStore;
|
|
@@ -18,6 +18,8 @@ export declare class HomeGraphService {
|
|
|
18
18
|
linkKnowledge(input: HomeGraphLinkInput): Promise<HomeGraphLinkResult>;
|
|
19
19
|
unlinkKnowledge(input: HomeGraphLinkInput): Promise<HomeGraphLinkResult>;
|
|
20
20
|
ask(input: HomeGraphAskInput): Promise<HomeGraphAskResult>;
|
|
21
|
+
reindex(input?: HomeGraphSpaceInput): Promise<HomeGraphReindexResult>;
|
|
22
|
+
private repairWeakExtractionsForAsk;
|
|
21
23
|
refreshDevicePassport(input: HomeGraphProjectionInput): Promise<HomeGraphDevicePassportResult>;
|
|
22
24
|
generateRoomPage(input: HomeGraphProjectionInput): Promise<HomeGraphProjectionResult>;
|
|
23
25
|
generatePacket(input: HomeGraphProjectionInput): Promise<HomeGraphProjectionResult>;
|
|
@@ -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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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { extractKnowledgeArtifact } from '../extractors.js';
|
|
2
|
-
import { HOME_GRAPH_CONNECTOR_ID, belongsToSpace, buildHomeGraphMetadata, buildHomeGraphNodeInput, edgeIsActive, homeGraphNodeId, homeGraphSourceId, namespacedCanonicalUri, nodeKindForHomeGraphObject, normalizeHomeGraphObjectInput, resolveHomeGraphSpace, targetToReference, uniqueStrings, } from './helpers.js';
|
|
2
|
+
import { HOME_GRAPH_CONNECTOR_ID, belongsToSpace, buildHomeGraphMetadata, buildHomeGraphNodeInput, edgeIsActive, homeGraphNodeId, homeGraphSourceId, namespacedCanonicalUri, nodeKindForHomeGraphObject, normalizeHomeGraphObjectInput, readRecord, resolveHomeGraphSpace, targetToReference, uniqueStrings, } from './helpers.js';
|
|
3
3
|
import { upsertIntegrationDocumentationCandidates } from './documentation.js';
|
|
4
4
|
import { refreshHomeGraphQualityIssues } from './quality.js';
|
|
5
5
|
import { reviewHomeGraphFact } from './review.js';
|
|
6
6
|
import { collectLinkedObjects, findHomeAssistantNode, inferHomeGraphSourceType, missingDevicePassportFields, readHomeGraphState, renderAskAnswer, renderHomeGraphState, safeHomeGraphFilename, sourcesLinkedToNode, } from './state.js';
|
|
7
7
|
import { renderDevicePassportPage, renderPacketPage, renderRoomPage, } from './rendering.js';
|
|
8
|
-
import {
|
|
8
|
+
import { reindexHomeGraphSources } from './reindex.js';
|
|
9
|
+
import { readHomeGraphSearchState, scoreHomeGraphResults, selectHomeGraphExtractionRepairCandidates, } from './search.js';
|
|
9
10
|
import { HOME_GRAPH_CAPABILITIES } from './types.js';
|
|
10
11
|
export class HomeGraphService {
|
|
11
12
|
store;
|
|
@@ -189,9 +190,12 @@ export class HomeGraphService {
|
|
|
189
190
|
}
|
|
190
191
|
async ask(input) {
|
|
191
192
|
await this.store.init();
|
|
192
|
-
const { spaceId } = resolveHomeGraphSpace(input);
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
const { spaceId, installationId } = resolveHomeGraphSpace(input);
|
|
194
|
+
let state = readHomeGraphSearchState(this.store, spaceId);
|
|
195
|
+
if (await this.repairWeakExtractionsForAsk(spaceId, installationId, input.query, state) > 0) {
|
|
196
|
+
state = readHomeGraphSearchState(this.store, spaceId);
|
|
197
|
+
}
|
|
198
|
+
const results = scoreHomeGraphResults(input.query, state.sources, state.nodes, state.edges, (sourceId) => state.extractionBySourceId.get(sourceId), input.limit ?? 8);
|
|
195
199
|
const sources = results.flatMap((result) => result.source ? [result.source] : []);
|
|
196
200
|
const linkedObjects = collectLinkedObjects(results, state);
|
|
197
201
|
const confidence = Math.min(100, Math.max(10, results[0]?.score ?? 10));
|
|
@@ -209,6 +213,34 @@ export class HomeGraphService {
|
|
|
209
213
|
results,
|
|
210
214
|
};
|
|
211
215
|
}
|
|
216
|
+
async reindex(input = {}) {
|
|
217
|
+
await this.store.init();
|
|
218
|
+
const { spaceId, installationId } = resolveHomeGraphSpace(input);
|
|
219
|
+
const state = readHomeGraphSearchState(this.store, spaceId);
|
|
220
|
+
return reindexHomeGraphSources({
|
|
221
|
+
spaceId,
|
|
222
|
+
sources: state.sources,
|
|
223
|
+
extractionBySourceId: state.extractionBySourceId,
|
|
224
|
+
artifactStore: this.artifactStore,
|
|
225
|
+
extract: (source, artifact) => this.extractArtifact(source, artifact, spaceId, installationId),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async repairWeakExtractionsForAsk(spaceId, installationId, query, state) {
|
|
229
|
+
const candidates = selectHomeGraphExtractionRepairCandidates(query, state.sources, state.nodes, state.edges, (sourceId) => state.extractionBySourceId.get(sourceId), 8);
|
|
230
|
+
let repaired = 0;
|
|
231
|
+
for (const source of candidates) {
|
|
232
|
+
const artifactId = typeof source.artifactId === 'string' ? source.artifactId : undefined;
|
|
233
|
+
if (!artifactId)
|
|
234
|
+
continue;
|
|
235
|
+
const artifact = this.artifactStore.get(artifactId);
|
|
236
|
+
if (!artifact)
|
|
237
|
+
continue;
|
|
238
|
+
const extraction = await this.extractArtifact(source, artifact, spaceId, installationId);
|
|
239
|
+
if (extraction && extractionHasSearchableText(extraction))
|
|
240
|
+
repaired += 1;
|
|
241
|
+
}
|
|
242
|
+
return repaired;
|
|
243
|
+
}
|
|
212
244
|
async refreshDevicePassport(input) {
|
|
213
245
|
await this.store.init();
|
|
214
246
|
const { spaceId, installationId } = resolveHomeGraphSpace(input);
|
|
@@ -410,8 +442,9 @@ export class HomeGraphService {
|
|
|
410
442
|
return undefined;
|
|
411
443
|
const { buffer } = await this.artifactStore.readContent(artifact.id);
|
|
412
444
|
const extracted = await extractKnowledgeArtifact(record, buffer);
|
|
445
|
+
const existing = this.store.getExtractionBySourceId(source.id);
|
|
413
446
|
return this.store.upsertExtraction({
|
|
414
|
-
id: `hg-extract-${source.id.replace(/^hg-src-/, '')}`,
|
|
447
|
+
id: existing?.id ?? `hg-extract-${source.id.replace(/^hg-src-/, '')}`,
|
|
415
448
|
sourceId: source.id,
|
|
416
449
|
artifactId: artifact.id,
|
|
417
450
|
extractorId: extracted.extractorId,
|
|
@@ -577,3 +610,7 @@ export class HomeGraphService {
|
|
|
577
610
|
};
|
|
578
611
|
}
|
|
579
612
|
}
|
|
613
|
+
function extractionHasSearchableText(extraction) {
|
|
614
|
+
const structure = readRecord(extraction.structure);
|
|
615
|
+
return typeof structure.searchText === 'string' && structure.searchText.trim().length > 0;
|
|
616
|
+
}
|
|
@@ -21,7 +21,9 @@ export declare function safeHomeGraphFilename(value: string): string;
|
|
|
21
21
|
export declare function renderAskAnswer(query: string, results: readonly {
|
|
22
22
|
readonly title: string;
|
|
23
23
|
readonly summary?: string;
|
|
24
|
+
readonly excerpt?: string;
|
|
24
25
|
readonly source?: KnowledgeSourceRecord;
|
|
26
|
+
readonly node?: KnowledgeNodeRecord;
|
|
25
27
|
}[], mode: 'concise' | 'standard' | 'detailed'): string;
|
|
26
28
|
export declare function edgeConnectsNode(edge: KnowledgeEdgeRecord, nodeId: string, relation: string, toId: string): boolean;
|
|
27
29
|
//# sourceMappingURL=state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAEzB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACzE,QAAQ,CAAC,WAAW,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAC5D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAkBzF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAUhH;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,qBAAqB,EAAE,CAKlG;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAAE,EAAE,EACpG,KAAK,EAAE;IACL,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAChD,GACA,mBAAmB,EAAE,CAavB;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACxC,MAAM,EAAE,CAOV;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,mBAAmB,GAAG,SAAS,CAOjC;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB,CAMrB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAA;CAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAEzB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACzE,QAAQ,CAAC,WAAW,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAC5D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAkBzF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAUhH;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,qBAAqB,EAAE,CAKlG;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAAE,EAAE,EACpG,KAAK,EAAE;IACL,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAChD,GACA,mBAAmB,EAAE,CAavB;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACxC,MAAM,EAAE,CAOV;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,mBAAmB,GAAG,SAAS,CAOjC;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB,CAMrB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAA;CAAE,EAAE,EAClL,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GACxC,MAAM,CASR;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAOnH"}
|