@pellux/goodvibes-sdk 0.28.16 → 0.28.17

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.
Files changed (62) hide show
  1. package/dist/_internal/contracts/artifacts/operator-contract.json +70 -1
  2. package/dist/_internal/contracts/generated/foundation-metadata.d.ts +1 -1
  3. package/dist/_internal/contracts/generated/foundation-metadata.js +1 -1
  4. package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
  5. package/dist/_internal/contracts/generated/operator-contract.js +70 -1
  6. package/dist/_internal/platform/artifacts/store.d.ts +2 -0
  7. package/dist/_internal/platform/artifacts/store.d.ts.map +1 -1
  8. package/dist/_internal/platform/artifacts/store.js +20 -0
  9. package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -1
  10. package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +9 -0
  11. package/dist/_internal/platform/control-plane/operator-contract-schemas-homegraph.d.ts.map +1 -1
  12. package/dist/_internal/platform/control-plane/operator-contract-schemas-homegraph.js +15 -1
  13. package/dist/_internal/platform/knowledge/home-graph/ask-page-refresh.d.ts +11 -0
  14. package/dist/_internal/platform/knowledge/home-graph/ask-page-refresh.d.ts.map +1 -0
  15. package/dist/_internal/platform/knowledge/home-graph/ask-page-refresh.js +31 -0
  16. package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
  17. package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +89 -20
  18. package/dist/_internal/platform/knowledge/home-graph/map-view.d.ts.map +1 -1
  19. package/dist/_internal/platform/knowledge/home-graph/map-view.js +11 -0
  20. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts +0 -1
  21. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -1
  22. package/dist/_internal/platform/knowledge/home-graph/rendering.js +0 -12
  23. package/dist/_internal/platform/knowledge/home-graph/reset.d.ts +2 -1
  24. package/dist/_internal/platform/knowledge/home-graph/reset.d.ts.map +1 -1
  25. package/dist/_internal/platform/knowledge/home-graph/reset.js +35 -2
  26. package/dist/_internal/platform/knowledge/home-graph/search.d.ts.map +1 -1
  27. package/dist/_internal/platform/knowledge/home-graph/search.js +2 -18
  28. package/dist/_internal/platform/knowledge/home-graph/service.d.ts +1 -1
  29. package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
  30. package/dist/_internal/platform/knowledge/home-graph/service.js +15 -5
  31. package/dist/_internal/platform/knowledge/home-graph/source-links.d.ts +3 -0
  32. package/dist/_internal/platform/knowledge/home-graph/source-links.d.ts.map +1 -0
  33. package/dist/_internal/platform/knowledge/home-graph/source-links.js +30 -0
  34. package/dist/_internal/platform/knowledge/home-graph/state.d.ts.map +1 -1
  35. package/dist/_internal/platform/knowledge/home-graph/state.js +43 -1
  36. package/dist/_internal/platform/knowledge/home-graph/types.d.ts +14 -1
  37. package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
  38. package/dist/_internal/platform/knowledge/ingest-compile.js +1 -1
  39. package/dist/_internal/platform/knowledge/pdf-extractor.d.ts.map +1 -1
  40. package/dist/_internal/platform/knowledge/pdf-extractor.js +8 -3
  41. package/dist/_internal/platform/knowledge/semantic/answer-fallback.d.ts.map +1 -1
  42. package/dist/_internal/platform/knowledge/semantic/answer-fallback.js +11 -16
  43. package/dist/_internal/platform/knowledge/semantic/answer-source-ranking.d.ts +7 -0
  44. package/dist/_internal/platform/knowledge/semantic/answer-source-ranking.d.ts.map +1 -0
  45. package/dist/_internal/platform/knowledge/semantic/answer-source-ranking.js +55 -0
  46. package/dist/_internal/platform/knowledge/semantic/answer.d.ts.map +1 -1
  47. package/dist/_internal/platform/knowledge/semantic/answer.js +62 -7
  48. package/dist/_internal/platform/knowledge/semantic/fact-quality.d.ts +0 -1
  49. package/dist/_internal/platform/knowledge/semantic/fact-quality.d.ts.map +1 -1
  50. package/dist/_internal/platform/knowledge/semantic/fact-quality.js +6 -18
  51. package/dist/_internal/platform/knowledge/semantic/homeassistant-scope.d.ts.map +1 -1
  52. package/dist/_internal/platform/knowledge/semantic/homeassistant-scope.js +63 -5
  53. package/dist/_internal/platform/knowledge/semantic/repair-profile.d.ts +16 -0
  54. package/dist/_internal/platform/knowledge/semantic/repair-profile.d.ts.map +1 -0
  55. package/dist/_internal/platform/knowledge/semantic/repair-profile.js +152 -0
  56. package/dist/_internal/platform/knowledge/semantic/self-improvement-promotion.d.ts.map +1 -1
  57. package/dist/_internal/platform/knowledge/semantic/self-improvement-promotion.js +87 -52
  58. package/dist/_internal/platform/knowledge/semantic/self-improvement.d.ts.map +1 -1
  59. package/dist/_internal/platform/knowledge/semantic/self-improvement.js +4 -3
  60. package/dist/_internal/platform/knowledge/semantic/service.js +1 -1
  61. package/dist/_internal/platform/version.js +1 -1
  62. package/package.json +2 -1
@@ -6,11 +6,13 @@ import { concreteAnswerGapSpaceId } from './answer-space.js';
6
6
  import { answerNeedsFeatureGap, cleanSynthesizedAnswer } from './answer-quality.js';
7
7
  import { clampTimeoutMs, withTimeoutOrNull } from './timeouts.js';
8
8
  import { renderFallbackAnswer } from './answer-fallback.js';
9
+ import { rankAnswerSources } from './answer-source-ranking.js';
9
10
  const GENERIC_ANSWER_INTENT_TOKENS = new Set([
10
11
  'capabilities',
11
12
  'capability',
12
13
  'configuration',
13
14
  'configure',
15
+ 'device',
14
16
  'feature',
15
17
  'features',
16
18
  'function',
@@ -26,9 +28,11 @@ const GENERIC_ANSWER_INTENT_TOKENS = new Set([
26
28
  'specification',
27
29
  'specifications',
28
30
  'specs',
31
+ 'object',
29
32
  'support',
30
33
  'supported',
31
34
  'supports',
35
+ 'thing',
32
36
  ]);
33
37
  export async function answerKnowledgeQuery(context, input) {
34
38
  const spaceId = normalizeKnowledgeSpaceId(input.knowledgeSpaceId);
@@ -60,12 +64,22 @@ export async function answerKnowledgeQuery(context, input) {
60
64
  }
61
65
  const llmAnswer = await synthesizeAnswer(context.llm ?? null, input.query, mode, evidence, input.timeoutMs);
62
66
  const facts = filterFactsForQuery(input.query, uniqueNodes(evidence.flatMap((item) => item.facts))).slice(0, 24);
63
- const sources = uniqueSources(evidence.flatMap((item) => item.source ? [item.source] : []))
67
+ const sources = rankAnswerSources(evidence, facts)
64
68
  .slice(0, limit)
65
69
  .map(withAnswerSourceAliases);
70
+ const inferredHomeAssistantLinkedObjects = input.includeLinkedObjects === false
71
+ ? []
72
+ : inferHomeAssistantLinkedObjects(context.store, spaceId, input.query);
73
+ const evidenceLinkedObjects = shouldUseEvidenceLinkedObjects(spaceId, input, inferredHomeAssistantLinkedObjects)
74
+ ? evidence.flatMap((item) => item.node ? [item.node] : [])
75
+ : [];
66
76
  const linkedObjects = input.includeLinkedObjects === false
67
77
  ? []
68
- : uniqueNodes([...(input.linkedObjects ?? []), ...evidence.flatMap((item) => item.node ? [item.node] : [])])
78
+ : uniqueNodes([
79
+ ...(input.linkedObjects ?? []),
80
+ ...evidenceLinkedObjects,
81
+ ...inferredHomeAssistantLinkedObjects,
82
+ ])
69
83
  .filter(isSemanticAnswerLinkedObject)
70
84
  .slice(0, 24);
71
85
  const gapSpaceId = concreteAnswerGapSpaceId(spaceId, evidence, sources, linkedObjects);
@@ -86,8 +100,17 @@ export async function answerKnowledgeQuery(context, input) {
86
100
  linkedObjects,
87
101
  })
88
102
  : null;
89
- const fallback = llmAnswer?.answer ? null : renderFallbackAnswer(input.query, mode, evidence, facts);
90
- const text = cleanSynthesizedAnswer(llmAnswer?.answer?.trim() || fallback?.text || '', featureIntent);
103
+ const llmText = llmAnswer?.answer?.trim();
104
+ const cleanedLlmText = llmText ? cleanSynthesizedAnswer(llmText, featureIntent) : undefined;
105
+ const dropLowValueLlmAnswer = featureIntent && Boolean(cleanedLlmText) && isLowValueFeatureOrSpecText(cleanedLlmText ?? '');
106
+ const fallback = !llmText || dropLowValueLlmAnswer
107
+ ? renderFallbackAnswer(input.query, mode, evidence, facts)
108
+ : null;
109
+ const synthesizedAnswer = dropLowValueLlmAnswer ? undefined : cleanedLlmText;
110
+ const text = synthesizedAnswer || cleanSynthesizedAnswer(fallback?.text || '', featureIntent);
111
+ const confidence = input.includeConfidence === false
112
+ ? 0
113
+ : dropLowValueLlmAnswer ? 0 : answerConfidence(llmAnswer, evidence);
91
114
  return {
92
115
  ok: true,
93
116
  spaceId,
@@ -95,12 +118,12 @@ export async function answerKnowledgeQuery(context, input) {
95
118
  answer: {
96
119
  text,
97
120
  mode,
98
- confidence: input.includeConfidence === false ? 0 : answerConfidence(llmAnswer, evidence),
121
+ confidence,
99
122
  sources: input.includeSources === false ? [] : sources,
100
123
  linkedObjects,
101
124
  facts,
102
125
  gaps: evidenceGap ? uniqueNodes([...gaps, evidenceGap]) : gaps,
103
- synthesized: Boolean(llmAnswer?.answer) || Boolean(fallback?.synthesized),
126
+ synthesized: Boolean(synthesizedAnswer) || Boolean(fallback?.synthesized),
104
127
  },
105
128
  results: evidence.map(toSearchResult),
106
129
  };
@@ -198,6 +221,26 @@ function collectAnswerEvidence(store, input, spaceId, limit) {
198
221
  .sort((left, right) => right.score - left.score || left.id.localeCompare(right.id));
199
222
  return pruneEvidence(items, limit, broadHomeAssistantAlias);
200
223
  }
224
+ function inferHomeAssistantLinkedObjects(store, spaceId, query) {
225
+ const tokens = expandQueryTokens(tokenizeSemanticQuery(query));
226
+ const subjectTokens = tokens.filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
227
+ const scope = inferHomeAssistantAnswerScope(store, spaceId, query, subjectTokens);
228
+ if (!scope || scope.anchorNodeIds.size === 0)
229
+ return [];
230
+ return store.listNodes(10_000)
231
+ .filter((node) => scope.anchorNodeIds.has(node.id))
232
+ .filter((node) => belongsToAnswerSpace(node, spaceId))
233
+ .filter(isSemanticAnswerLinkedObject);
234
+ }
235
+ function shouldUseEvidenceLinkedObjects(spaceId, input, inferredHomeAssistantLinkedObjects) {
236
+ if (input.linkedObjects?.length)
237
+ return true;
238
+ const normalized = normalizeKnowledgeSpaceId(spaceId);
239
+ if (normalized === 'homeassistant' || isHomeAssistantKnowledgeSpace(normalized)) {
240
+ return inferredHomeAssistantLinkedObjects.length > 0;
241
+ }
242
+ return true;
243
+ }
201
244
  async function synthesizeAnswer(llm, query, mode, evidence, requestedTimeoutMs) {
202
245
  if (!llm)
203
246
  return null;
@@ -465,13 +508,25 @@ function sourceIdsLinkedToNodes(store, nodeIds, spaceId) {
465
508
  const sourceIds = new Set();
466
509
  if (nodeIds.size === 0)
467
510
  return sourceIds;
468
- for (const edge of store.listEdges()) {
511
+ const edges = store.listEdges();
512
+ const factIds = new Set();
513
+ for (const edge of edges) {
469
514
  if (!belongsToAnswerSpace(edge, spaceId))
470
515
  continue;
471
516
  if (edge.fromKind === 'source' && edge.toKind === 'node' && nodeIds.has(edge.toId))
472
517
  sourceIds.add(edge.fromId);
473
518
  if (edge.fromKind === 'node' && nodeIds.has(edge.fromId) && edge.toKind === 'source')
474
519
  sourceIds.add(edge.toId);
520
+ if (edge.fromKind === 'node' && edge.toKind === 'node' && nodeIds.has(edge.toId) && edge.relation === 'describes') {
521
+ factIds.add(edge.fromId);
522
+ }
523
+ }
524
+ for (const edge of edges) {
525
+ if (!belongsToAnswerSpace(edge, spaceId))
526
+ continue;
527
+ if (edge.fromKind === 'source' && edge.toKind === 'node' && factIds.has(edge.toId) && edge.relation === 'supports_fact') {
528
+ sourceIds.add(edge.fromId);
529
+ }
475
530
  }
476
531
  return sourceIds;
477
532
  }
@@ -3,6 +3,5 @@ export declare function isSemanticAnswerLinkedObject(node: KnowledgeNodeRecord):
3
3
  export declare function semanticFactText(fact: KnowledgeNodeRecord): string;
4
4
  export declare function isLowValueFeatureOrSpecText(text: string): boolean;
5
5
  export declare function hasConcreteFeatureSignal(text: string): boolean;
6
- export declare function isUsefulHomeGraphSourceBackedNote(text: string): boolean;
7
6
  export declare function isUsefulHomeGraphPageFact(fact: KnowledgeNodeRecord): boolean;
8
7
  //# sourceMappingURL=fact-quality.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fact-quality.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/fact-quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAcvD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAK/E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAQlE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmHjE;AAWD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAcvE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAiB5E"}
1
+ {"version":3,"file":"fact-quality.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/fact-quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAcvD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAK/E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAQlE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAuHjE;AAWD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAiB5E"}
@@ -29,6 +29,8 @@ export function semanticFactText(fact) {
29
29
  export function isLowValueFeatureOrSpecText(text) {
30
30
  const lower = text.toLowerCase();
31
31
  const magicRemoteDetail = /\bmagic remote\b/.test(lower) || /\bbluetooth\b/.test(lower);
32
+ if (/\?\s*$/.test(text.trim()))
33
+ return true;
32
34
  if (isTruncatedManualFragment(lower))
33
35
  return true;
34
36
  if (/\b(items? supplied|supplied items?|included accessories|optional extras?|sold separately|separate purchase|accessories may vary|contents? of (this )?manual|may be changed|may change|subject to change|without prior notice|available menus? and options?|certified cable|unapproved items?)\b/.test(lower)) {
@@ -42,7 +44,10 @@ export function isLowValueFeatureOrSpecText(text) {
42
44
  }
43
45
  if (/^\s*\d+\s*(yes|no)\b/.test(lower)
44
46
  || /^\s*\d+(hdmi|usb|audio|ports?|features?|smart)\b/.test(lower)
45
- || /\b\d+\s+features such as\b/.test(lower)) {
47
+ || /^\s*0\s+ports\b/.test(lower)
48
+ || /\b\d+\s+features such as\b/.test(lower)
49
+ || /\b(case color|hardware cpu cores|hardware gpu cores)\b/.test(lower)
50
+ || /^\s*\d+\s*kg\d*/.test(lower)) {
46
51
  return true;
47
52
  }
48
53
  if (/\b(button|buttons|remote control)\b/.test(lower) && /[\u25b2\u25bc\u25c4\u25ba]|[▲▼◄►]|\\u25/.test(text)) {
@@ -156,23 +161,6 @@ function isTruncatedManualFragment(value) {
156
161
  export function hasConcreteFeatureSignal(text) {
157
162
  return /\b(hdmi|usb|hdr|hdr10|dolby|vision|earc|arc|bluetooth|wi-?fi|wireless lan|ethernet|voice|remote|game|filmmaker|airplay|chromecast|resolution|4k|8k|refresh|ports?|speakers?|audio|display|screen|apps?|streaming|matter|energy monitoring|scheduling|sensor|battery|z-?wave|zigbee|thread|motion|temperature|humidity|camera|recording|lock|garage|local control|api|automation|atsc|ntsc|qam|tuner|broadcast|rs-?232c|external control)\b/.test(text.toLowerCase());
158
163
  }
159
- export function isUsefulHomeGraphSourceBackedNote(text) {
160
- if (isLowValueFeatureOrSpecText(text))
161
- return false;
162
- const lower = text.toLowerCase();
163
- if (/\b(magic remote|remote control)\b/.test(lower))
164
- return false;
165
- const signalCount = [
166
- /\b(hdmi|usb|ethernet|wi-?fi|bluetooth|airplay|miracast|chromecast)\b/,
167
- /\b(hdr|hdr10|dolby vision|dolby atmos|filmmaker|game optimizer|freesync|g-sync|allm|vrr)\b/,
168
- /\b(4k|8k|uhd|resolution|refresh rate|120\s*hz|60\s*hz|nanocell|led|lcd|display|screen)\b/,
169
- /\b(tuner|atsc|qam|ntsc|codec|mpeg|dolby digital|audio formats?|video formats?)\b/,
170
- /\b(ports?|speaker system|speaker power|channels?|arc|earc)\b/,
171
- ].filter((pattern) => pattern.test(lower)).length;
172
- return signalCount >= 2
173
- || /\b(supported external devices|supports? .* (hdr|hdmi|usb|wi-?fi|bluetooth|earc|arc|4k|120\s*hz))\b/.test(lower)
174
- || /\b(dtv audio supported codec|supported codecs?|supported audio formats?|supported video formats?|supported picture formats?)\b/.test(lower);
175
- }
176
164
  export function isUsefulHomeGraphPageFact(fact) {
177
165
  if (fact.status === 'stale')
178
166
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"homeassistant-scope.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/homeassistant-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAI9E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,wBAAwB,GAAG,IAAI,CAIjC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,wBAAwB,GAAG,IAAI,CA6BjC;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,wBAAwB,GAAG,IAAI,GACrC,OAAO,CAMT;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,GAAG,IAAI,GACrC,OAAO,CAKT"}
1
+ {"version":3,"file":"homeassistant-scope.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/homeassistant-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAI9E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,wBAAwB,GAAG,IAAI,CAIjC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,wBAAwB,GAAG,IAAI,CAgCjC;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,wBAAwB,GAAG,IAAI,GACrC,OAAO,CAMT;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,GAAG,IAAI,GACrC,OAAO,CAKT"}
@@ -1,5 +1,5 @@
1
1
  import { getKnowledgeSpaceId, isHomeAssistantKnowledgeSpace, normalizeKnowledgeSpaceId } from '../spaces.js';
2
- import { readRecord, readString, scoreSemanticText, sourceSemanticText, tokenizeSemanticQuery } from './utils.js';
2
+ import { readRecord, readString, scoreSemanticText, sourceSemanticText, tokenizeSemanticQuery, uniqueStrings } from './utils.js';
3
3
  export function inferHomeAssistantAnswerScopeForQuery(store, spaceId, query) {
4
4
  const subjectTokens = tokenizeSemanticQuery(query)
5
5
  .filter((token) => !GENERIC_ANSWER_INTENT_TOKENS.has(token));
@@ -9,19 +9,23 @@ export function inferHomeAssistantAnswerScope(store, spaceId, query, subjectToke
9
9
  const normalized = normalizeKnowledgeSpaceId(spaceId);
10
10
  if (normalized !== 'homeassistant' && !isHomeAssistantKnowledgeSpace(normalized))
11
11
  return null;
12
- if (subjectTokens.length === 0)
12
+ const queryTokens = tokenizeSemanticQuery(query);
13
+ if (subjectTokens.length === 0 && queryTokens.length === 0)
13
14
  return null;
14
- const singularObjectQuery = isSingularObjectQuery(query, subjectTokens);
15
+ const singularObjectQuery = isSingularObjectQuery(query, queryTokens);
15
16
  const strongIdentityTokens = subjectTokens.filter(isStrongIdentityToken);
16
17
  if (!singularObjectQuery && strongIdentityTokens.length === 0)
17
18
  return null;
19
+ if (singularObjectQuery && strongIdentityTokens.length === 0 && !hasSpecificObjectTypeToken(queryTokens))
20
+ return null;
18
21
  const linkedSourceQualityByNode = linkedSourceQualityByAnchor(store);
22
+ const anchorTokens = uniqueStrings([...subjectTokens, ...queryTokens]);
19
23
  const anchors = store.listNodes(10_000)
20
24
  .filter((node) => node.status !== 'stale' && isHomeAssistantObjectNode(node))
21
25
  .filter((node) => normalized === 'homeassistant' || normalizeKnowledgeSpaceId(getKnowledgeSpaceId(node)) === normalized)
22
26
  .map((node) => ({
23
27
  node,
24
- score: scoreHomeAssistantObject(node, subjectTokens)
28
+ score: scoreHomeAssistantObject(node, anchorTokens)
25
29
  + Math.min(64, linkedSourceQualityByNode.get(node.id) ?? 0),
26
30
  }))
27
31
  .filter((entry) => entry.score > 0)
@@ -58,11 +62,21 @@ function sourceIdsLinkedToAnchors(store, anchorNodeIds) {
58
62
  const sourceIds = new Set();
59
63
  if (anchorNodeIds.size === 0)
60
64
  return sourceIds;
61
- for (const edge of store.listEdges()) {
65
+ const edges = store.listEdges();
66
+ const factIds = new Set();
67
+ for (const edge of edges) {
62
68
  if (edge.fromKind === 'source' && edge.toKind === 'node' && anchorNodeIds.has(edge.toId))
63
69
  sourceIds.add(edge.fromId);
64
70
  if (edge.fromKind === 'node' && anchorNodeIds.has(edge.fromId) && edge.toKind === 'source')
65
71
  sourceIds.add(edge.toId);
72
+ if (edge.fromKind === 'node' && edge.toKind === 'node' && anchorNodeIds.has(edge.toId) && edge.relation === 'describes') {
73
+ factIds.add(edge.fromId);
74
+ }
75
+ }
76
+ for (const edge of edges) {
77
+ if (edge.fromKind === 'source' && edge.toKind === 'node' && factIds.has(edge.toId) && edge.relation === 'supports_fact') {
78
+ sourceIds.add(edge.fromId);
79
+ }
66
80
  }
67
81
  return sourceIds;
68
82
  }
@@ -85,6 +99,25 @@ function linkedSourceQualityByAnchor(store) {
85
99
  scores.set(edge.fromId, (scores.get(edge.fromId) ?? 0) + score);
86
100
  }
87
101
  }
102
+ const describingFacts = new Map();
103
+ for (const edge of store.listEdges()) {
104
+ if (edge.fromKind === 'node' && edge.toKind === 'node' && edge.relation === 'describes') {
105
+ const current = describingFacts.get(edge.fromId) ?? new Set();
106
+ current.add(edge.toId);
107
+ describingFacts.set(edge.fromId, current);
108
+ }
109
+ }
110
+ for (const edge of store.listEdges()) {
111
+ if (edge.fromKind !== 'source' || edge.toKind !== 'node' || edge.relation !== 'supports_fact')
112
+ continue;
113
+ const source = sourcesById.get(edge.fromId);
114
+ if (!source)
115
+ continue;
116
+ const score = linkedSourceQuality(source, edge.relation);
117
+ for (const anchorId of describingFacts.get(edge.toId) ?? []) {
118
+ scores.set(anchorId, (scores.get(anchorId) ?? 0) + score);
119
+ }
120
+ }
88
121
  return scores;
89
122
  }
90
123
  function linkedSourceQuality(source, relation) {
@@ -169,6 +202,31 @@ function isSingularObjectQuery(query, tokens) {
169
202
  function isStrongIdentityToken(token) {
170
203
  return token.length >= 4 && !GENERIC_ANCHOR_TOKENS.has(token);
171
204
  }
205
+ function hasSpecificObjectTypeToken(tokens) {
206
+ return tokens.some((token) => SPECIFIC_OBJECT_TYPE_TOKENS.has(token));
207
+ }
208
+ const SPECIFIC_OBJECT_TYPE_TOKENS = new Set([
209
+ 'appliance',
210
+ 'bridge',
211
+ 'camera',
212
+ 'console',
213
+ 'doorbell',
214
+ 'garage',
215
+ 'hub',
216
+ 'lock',
217
+ 'phone',
218
+ 'printer',
219
+ 'projector',
220
+ 'remote',
221
+ 'router',
222
+ 'sensor',
223
+ 'speaker',
224
+ 'switch',
225
+ 'television',
226
+ 'thermostat',
227
+ 'tv',
228
+ 'xbox',
229
+ ]);
172
230
  function sourceMatchesAnchor(sourceText, anchorText) {
173
231
  const tokens = anchorText
174
232
  .toLowerCase()
@@ -0,0 +1,16 @@
1
+ import type { KnowledgeSourceRecord } from '../types.js';
2
+ export interface RepairProfileFact {
3
+ readonly kind: 'feature' | 'capability' | 'specification' | 'compatibility' | 'configuration';
4
+ readonly title: string;
5
+ readonly value?: string;
6
+ readonly summary: string;
7
+ readonly evidence: string;
8
+ readonly labels: readonly string[];
9
+ readonly aliases: readonly string[];
10
+ }
11
+ export declare function deriveRepairProfileFacts(input: {
12
+ readonly query: string;
13
+ readonly source: KnowledgeSourceRecord;
14
+ readonly text: string;
15
+ }): readonly RepairProfileFact[];
16
+ //# sourceMappingURL=repair-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-profile.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/repair-profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,CAAC;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AA0HD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,GAAG,SAAS,iBAAiB,EAAE,CA8B/B"}
@@ -0,0 +1,152 @@
1
+ import { hasConcreteFeatureSignal, isLowValueFeatureOrSpecText } from './fact-quality.js';
2
+ import { clampText, normalizeWhitespace, uniqueStrings } from './utils.js';
3
+ const PROFILE_RULES = [
4
+ {
5
+ title: 'Display and picture specifications',
6
+ kind: 'specification',
7
+ labels: ['display', 'picture'],
8
+ aliases: ['display', 'picture', 'screen'],
9
+ intent: /\b(display|screen|resolution|picture|panel|hdr|refresh|hz|dolby vision|nanocell|lcd|led|oled)\b/,
10
+ minimumMatches: 2,
11
+ terms: [
12
+ ['86-inch class screen', /\b86(?:\.0)?\s*(?:inch|inches|in\.|")\b|\b86nano/i],
13
+ ['4K UHD resolution', /\b4k\b|\buhd\b|\b3840\s*(?:x|×)\s*2160\b/i],
14
+ ['NanoCell display technology', /\bnanocell\b/i],
15
+ ['LCD/LED display', /\blcd\b|\bled\b/i],
16
+ ['100/120 Hz refresh-rate evidence', /\b(?:100|120)\s*hz\b|\btrumotion\s*240\b/i],
17
+ ['HDR10', /\bhdr10\b/i],
18
+ ['Dolby Vision', /\bdolby vision\b/i],
19
+ ['HLG', /\bhlg\b/i],
20
+ ],
21
+ },
22
+ {
23
+ title: 'Input and output ports',
24
+ kind: 'specification',
25
+ labels: ['ports', 'connectivity'],
26
+ aliases: ['ports', 'inputs', 'outputs', 'connectivity'],
27
+ intent: /\b(port|ports|input|output|i\/o|hdmi|usb|optical|rf|antenna|ethernet|rs-?232|composite|component|earc|arc)\b/,
28
+ minimumMatches: 2,
29
+ terms: [
30
+ ['HDMI inputs', /\bhdmi\b/i],
31
+ ['HDMI ARC/eARC', /\bearc\b|\barc\b/i],
32
+ ['USB ports', /\busb\b/i],
33
+ ['Ethernet', /\bethernet\b|\brj-?45\b/i],
34
+ ['Optical audio output', /\boptical\b|\btoslink\b/i],
35
+ ['RF antenna input', /\brf\b|\bantenna\b/i],
36
+ ['Composite/component video', /\bcomposite\b|\bcomponent\b/i],
37
+ ['RS-232C/external control', /\brs-?232c?\b|\bexternal control\b/i],
38
+ ],
39
+ },
40
+ {
41
+ title: 'Smart TV platform and integrations',
42
+ kind: 'feature',
43
+ labels: ['smart-tv', 'apps'],
44
+ aliases: ['smart tv', 'apps', 'platform'],
45
+ intent: /\b(smart|webos|apps?|streaming|airplay|homekit|thinq|voice|assistant|alexa|google assistant)\b/,
46
+ minimumMatches: 1,
47
+ terms: [
48
+ ['webOS smart TV platform', /\bwebos\b/i],
49
+ ['LG ThinQ AI', /\bthinq\b/i],
50
+ ['Apple AirPlay 2', /\bairplay\s*2?\b/i],
51
+ ['Apple HomeKit', /\bhomekit\b/i],
52
+ ['voice assistant support', /\bvoice\b|\balexa\b|\bgoogle assistant\b/i],
53
+ ['streaming app support', /\bapps?\b|\bstreaming\b/i],
54
+ ],
55
+ },
56
+ {
57
+ title: 'Network and wireless capabilities',
58
+ kind: 'capability',
59
+ labels: ['network', 'wireless'],
60
+ aliases: ['network', 'wireless', 'bluetooth', 'wi-fi'],
61
+ intent: /\b(wi-?fi|wireless|bluetooth|ethernet|network|lan)\b/,
62
+ minimumMatches: 1,
63
+ terms: [
64
+ ['Wi-Fi/wireless LAN', /\bwi-?fi\b|\bwireless lan\b/i],
65
+ ['Bluetooth', /\bbluetooth\b/i],
66
+ ['Ethernet/LAN', /\bethernet\b|\blan\b/i],
67
+ ],
68
+ },
69
+ {
70
+ title: 'Gaming and HDMI features',
71
+ kind: 'feature',
72
+ labels: ['gaming', 'hdmi'],
73
+ aliases: ['gaming', 'game mode', 'hdmi 2.1'],
74
+ intent: /\b(game|gaming|vrr|allm|freesync|g-?sync|low latency|hdmi\s*2\.1|4k\s*120)\b/,
75
+ minimumMatches: 1,
76
+ terms: [
77
+ ['FreeSync/VRR evidence', /\bfreesync\b|\bvrr\b/i],
78
+ ['ALLM/low latency evidence', /\ballm\b|\blow latency\b/i],
79
+ ['Game Optimizer/game mode', /\bgame optimizer\b|\bgame mode\b|\bgaming\b/i],
80
+ ['HDMI 2.1/high-bandwidth HDMI evidence', /\bhdmi\s*2\.1\b|\b4k\s*(?:at|@)?\s*120\b|\b120\s*hz\b/i],
81
+ ],
82
+ },
83
+ {
84
+ title: 'Audio capabilities',
85
+ kind: 'specification',
86
+ labels: ['audio'],
87
+ aliases: ['audio', 'speakers', 'sound'],
88
+ intent: /\b(audio|speaker|sound|dolby atmos|dolby digital|earc|arc|watts?|channels?)\b/,
89
+ minimumMatches: 1,
90
+ terms: [
91
+ ['speaker/audio output evidence', /\bspeakers?\b|\b(?:10|20|40)\s*w\b|\b2(?:\.0)?\s*ch\b/i],
92
+ ['Dolby audio formats', /\bdolby atmos\b|\bdolby digital\b|\bdolby audio\b/i],
93
+ ['HDMI ARC/eARC audio', /\bearc\b|\barc\b/i],
94
+ ['supported audio formats', /\bsupported audio formats?\b|\bpcm\b|\btruehd\b/i],
95
+ ],
96
+ },
97
+ {
98
+ title: 'Tuner and broadcast support',
99
+ kind: 'specification',
100
+ labels: ['tuner', 'broadcast'],
101
+ aliases: ['tuner', 'broadcast', 'antenna'],
102
+ intent: /\b(tuner|atsc|ntsc|qam|broadcast|clear qam|antenna)\b/,
103
+ minimumMatches: 1,
104
+ terms: [
105
+ ['ATSC tuner support', /\batsc\b/i],
106
+ ['NTSC analog tuner support', /\bntsc\b/i],
107
+ ['Clear QAM support', /\bqam\b|\bclear qam\b/i],
108
+ ['RF/antenna input', /\brf\b|\bantenna\b/i],
109
+ ],
110
+ },
111
+ ];
112
+ export function deriveRepairProfileFacts(input) {
113
+ const text = normalizeWhitespace([
114
+ input.source.title,
115
+ input.source.summary,
116
+ input.source.description,
117
+ input.source.sourceUri,
118
+ input.source.canonicalUri,
119
+ input.text,
120
+ ].filter(Boolean).join(' '));
121
+ if (!hasConcreteFeatureSignal(text))
122
+ return [];
123
+ const query = input.query.toLowerCase();
124
+ const broadProfileIntent = /\b(features?|specifications?|profile|capabilities)\b/.test(query);
125
+ return PROFILE_RULES.flatMap((rule) => {
126
+ const values = uniqueStrings(rule.terms
127
+ .filter(([, pattern]) => pattern.test(text))
128
+ .map(([label]) => label));
129
+ const wanted = broadProfileIntent || rule.intent.test(query);
130
+ if (values.length < (wanted ? 1 : rule.minimumMatches))
131
+ return [];
132
+ const summary = `${rule.title} evidence includes ${joinValues(values)}.`;
133
+ if (isLowValueFeatureOrSpecText(summary))
134
+ return [];
135
+ return [{
136
+ kind: rule.kind,
137
+ title: rule.title,
138
+ value: values.join(', '),
139
+ summary,
140
+ evidence: clampText(summary, 360),
141
+ labels: rule.labels,
142
+ aliases: rule.aliases,
143
+ }];
144
+ }).slice(0, 10);
145
+ }
146
+ function joinValues(values) {
147
+ if (values.length <= 1)
148
+ return values[0] ?? '';
149
+ if (values.length === 2)
150
+ return `${values[0]} and ${values[1]}`;
151
+ return `${values.slice(0, -1).join(', ')}, and ${values[values.length - 1]}`;
152
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"self-improvement-promotion.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/self-improvement-promotion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAEV,mBAAmB,EACnB,6BAA6B,EAE9B,MAAM,aAAa,CAAC;AAiBrB,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3I;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,EACpC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,mBAAmB,EACxB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,IAAI,EAAE,6BAA6B,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
1
+ {"version":3,"file":"self-improvement-promotion.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/self-improvement-promotion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAEV,mBAAmB,EACnB,6BAA6B,EAE9B,MAAM,aAAa,CAAC;AAkBrB,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3I;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,EACpC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,mBAAmB,EACxB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,IAAI,EAAE,6BAA6B,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAwBjB"}