@pellux/goodvibes-sdk 0.27.6 → 0.27.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "0.27.6"
6
+ "version": "0.27.8"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
@@ -1,6 +1,6 @@
1
1
  export declare const FOUNDATION_METADATA: {
2
2
  readonly productId: "goodvibes";
3
- readonly productVersion: "0.27.6";
3
+ readonly productVersion: "0.27.8";
4
4
  readonly operatorMethodCount: 256;
5
5
  readonly operatorEventCount: 30;
6
6
  readonly peerEndpointCount: 6;
@@ -1,7 +1,7 @@
1
1
  // Synced from packages/contracts/src/generated/foundation-metadata.ts
2
2
  export const FOUNDATION_METADATA = {
3
3
  "productId": "goodvibes",
4
- "productVersion": "0.27.6",
4
+ "productVersion": "0.27.8",
5
5
  "operatorMethodCount": 256,
6
6
  "operatorEventCount": 30,
7
7
  "peerEndpointCount": 6
@@ -3,7 +3,7 @@ export const OPERATOR_CONTRACT = {
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "0.27.6"
6
+ "version": "0.27.8"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
@@ -3,10 +3,10 @@ export async function answerHomeGraphQuery(input) {
3
3
  const sources = input.results.flatMap((result) => result.source ? [result.source] : []);
4
4
  const linkedObjects = collectLinkedObjects(input.results, input.state);
5
5
  if (input.semanticService) {
6
- await input.semanticService.enrichSources(uniqueSources(sources), {
6
+ void input.semanticService.enrichSources(uniqueSources(sources), {
7
7
  knowledgeSpaceId: input.spaceId,
8
8
  limit: Math.min(3, Math.max(1, sources.length)),
9
- });
9
+ }).catch(() => { });
10
10
  const answer = await input.semanticService.answer({
11
11
  query: input.query.query,
12
12
  knowledgeSpaceId: input.spaceId,
@@ -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,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,CA2EhF;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"}
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;AA+BlD,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,CA2EhF;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"}
@@ -2,6 +2,7 @@ import { materializeGeneratedKnowledgeProjection, } from '../generated-projectio
2
2
  import { HOME_GRAPH_CONNECTOR_ID, buildHomeGraphMetadata, edgeIsActive, homeGraphNodeId, homeGraphSourceId, isGeneratedPageSource, namespacedCanonicalUri, readRecord, uniqueStrings, } from './helpers.js';
3
3
  import { findHomeAssistantNode, missingDevicePassportFields, readHomeGraphState, renderHomeGraphState, safeHomeGraphFilename, sourcesLinkedToNode, } from './state.js';
4
4
  import { renderDevicePassportPage, renderPacketPage, renderRoomPage, } from './rendering.js';
5
+ import { isUsefulHomeGraphPageFact } from '../semantic/fact-quality.js';
5
6
  export async function generateAutomaticHomeGraphPages(context) {
6
7
  return generateHomeGraphPagesForCurrentState(context, context.input.pageAutomation ?? {});
7
8
  }
@@ -286,7 +287,7 @@ function semanticFactsLinkedToSources(sources, nodes, edges) {
286
287
  && sourceIds.has(edge.fromId)
287
288
  && edge.toKind === 'node'
288
289
  && edge.relation === 'supports_fact')).map((edge) => edge.toId));
289
- return nodes.filter((node) => factIds.has(node.id));
290
+ return nodes.filter((node) => factIds.has(node.id) && isUsefulHomeGraphPageFact(node));
290
291
  }
291
292
  function limitRecords(records, limit) {
292
293
  if (typeof limit !== 'number')
@@ -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,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,CAmDnF;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;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACzD,GAAG,MAAM,CA+BT;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"}
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;AAGnG,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,CAmDnF;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;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACzD,GAAG,MAAM,CA+BT;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"}
@@ -2,6 +2,7 @@ import { renderKnowledgeMap } from '../map.js';
2
2
  import { countFacet, normalizeStringArray, readString } from '../map-filters.js';
3
3
  import { isUnusableHomeGraphExtractionText } from './extraction-quality.js';
4
4
  import { edgeIsActive, isGeneratedPageSource, readRecord, uniqueStrings } from './helpers.js';
5
+ import { isLowValueFeatureOrSpecText, isUsefulHomeGraphPageFact } from '../semantic/fact-quality.js';
5
6
  export function renderRoomPage(state, areaId) {
6
7
  const area = areaId
7
8
  ? findNodeByHaId(state.nodes, 'ha_area', areaId) ?? findNodeByHaId(state.nodes, 'ha_room', areaId)
@@ -295,7 +296,7 @@ function sourceEvidenceSnippets(source, extraction, tokens, limit) {
295
296
  ...featureSentences(searchText),
296
297
  source.summary,
297
298
  source.description,
298
- ]).filter((entry) => !isUnusableHomeGraphExtractionText(entry)).slice(0, 80);
299
+ ]).filter((entry) => !isUnusableHomeGraphExtractionText(entry) && !isLowValueFeatureOrSpecText(entry)).slice(0, 80);
299
300
  const scored = candidates
300
301
  .map((text) => ({ text: clampEvidence(text), score: evidenceScore(text, tokens) }))
301
302
  .filter((entry) => entry.text.length > 0)
@@ -327,7 +328,7 @@ function featureSentences(value) {
327
328
  const keywords = /\b(feature|features|support|supports|capability|capabilities|specification|specifications|mode|modes|hdmi|hdr|dolby|battery|reset|warranty|firmware|voice|remote)\b/i;
328
329
  return (text.match(/[^.!?\n]+[.!?]?/g) ?? [])
329
330
  .map((entry) => entry.trim())
330
- .filter((entry) => keywords.test(entry))
331
+ .filter((entry) => keywords.test(entry) && !isLowValueFeatureOrSpecText(entry))
331
332
  .slice(0, 24);
332
333
  }
333
334
  function evidenceScore(value, tokens) {
@@ -390,7 +391,7 @@ function renderSourceList(title, sources) {
390
391
  }
391
392
  function renderSemanticFacts(title, facts) {
392
393
  const entries = facts
393
- .filter((node) => node.metadata.semanticKind === 'fact')
394
+ .filter(isUsefulHomeGraphPageFact)
394
395
  .sort((left, right) => semanticFactSortKey(left).localeCompare(semanticFactSortKey(right)) || left.title.localeCompare(right.title))
395
396
  .slice(0, 80);
396
397
  if (entries.length === 0)
@@ -423,7 +424,7 @@ function semanticFactsLinkedToSources(sources, nodes, edges) {
423
424
  && sourceIds.has(edge.fromId)
424
425
  && edge.toKind === 'node'
425
426
  && edge.relation === 'supports_fact')).map((edge) => edge.toId));
426
- return nodes.filter((node) => factIds.has(node.id));
427
+ return nodes.filter((node) => factIds.has(node.id) && isUsefulHomeGraphPageFact(node));
427
428
  }
428
429
  function semanticFactSortKey(node) {
429
430
  const order = {
@@ -1 +1 @@
1
- {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAE7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAepB,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAsCD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,6BAA6B,CAAC,CAiDxC"}
1
+ {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAE7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAqBpB,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAsCD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,6BAA6B,CAAC,CAmDxC"}
@@ -1,5 +1,6 @@
1
1
  import { getKnowledgeSpaceId, isInKnowledgeSpace, normalizeKnowledgeSpaceId } from '../spaces.js';
2
2
  import { MAX_ANSWER_EVIDENCE_CHARS, clampText, normalizeWhitespace, readRecord, readString, scoreSemanticText, semanticHash, semanticMetadata, sourceSemanticText, tokenizeSemanticQuery, uniqueStrings, } from './utils.js';
3
+ import { hasConcreteFeatureSignal, isLowValueFeatureOrSpecText, isSemanticAnswerLinkedObject, semanticFactText, } from './fact-quality.js';
3
4
  const GENERIC_ANSWER_INTENT_TOKENS = new Set([
4
5
  'capabilities',
5
6
  'capability',
@@ -53,7 +54,9 @@ export async function answerKnowledgeQuery(context, input) {
53
54
  const sources = uniqueSources(evidence.flatMap((item) => item.source ? [item.source] : [])).slice(0, limit);
54
55
  const linkedObjects = input.includeLinkedObjects === false
55
56
  ? []
56
- : uniqueNodes([...(input.linkedObjects ?? []), ...evidence.flatMap((item) => item.node ? [item.node] : [])]).slice(0, 24);
57
+ : uniqueNodes([...(input.linkedObjects ?? []), ...evidence.flatMap((item) => item.node ? [item.node] : [])])
58
+ .filter(isSemanticAnswerLinkedObject)
59
+ .slice(0, 24);
57
60
  const gaps = await persistAnswerGaps(context.store, spaceId, input.query, llmAnswer?.gaps ?? []);
58
61
  const text = llmAnswer?.answer?.trim() || renderFallbackAnswer(input.query, mode, evidence);
59
62
  return {
@@ -260,14 +263,8 @@ function isLowValueFactForQuery(tokens, intent, fact) {
260
263
  const kind = readString(fact.metadata.factKind) ?? 'note';
261
264
  if (!['feature', 'capability', 'specification', 'compatibility', 'configuration', 'identity'].includes(kind))
262
265
  return false;
263
- const text = [
264
- fact.title,
265
- fact.summary,
266
- readString(fact.metadata.value),
267
- readString(fact.metadata.evidence),
268
- Array.isArray(fact.metadata.labels) ? fact.metadata.labels.join(' ') : '',
269
- ].filter(Boolean).join(' ').toLowerCase();
270
- if (isManualBoilerplateFeatureText(text))
266
+ const text = semanticFactText(fact);
267
+ if (isLowValueFeatureOrSpecText(text))
271
268
  return true;
272
269
  const extractor = readString(fact.metadata.extractor);
273
270
  const confidence = typeof fact.confidence === 'number' ? fact.confidence : 0;
@@ -279,12 +276,6 @@ function isLowValueFactForQuery(tokens, intent, fact) {
279
276
  function hasFeatureIntent(intent) {
280
277
  return intent.has('feature') || intent.has('capability') || intent.has('specification') || intent.has('compatibility');
281
278
  }
282
- function isManualBoilerplateFeatureText(text) {
283
- return /\b(items? supplied|accessories supplied|contents? of (this )?manual|may vary|may be changed|without prior notice|depending (upon|on) (the )?model|available menus? and options?|product specifications?|power cord|electric shock|fire hazard|certified cable|do not|warning|caution|risk|hazard|clean only|near water|ventilation|antenna grounding)\b/.test(text);
284
- }
285
- function hasConcreteFeatureSignal(text) {
286
- return /\b(hdmi|usb|hdr|hdr10|dolby|vision|earc|arc|bluetooth|wi-?fi|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)\b/.test(text);
287
- }
288
279
  function compareFactQuality(left, right) {
289
280
  return factQuality(right) - factQuality(left) || left.title.localeCompare(right.title);
290
281
  }
@@ -361,12 +352,18 @@ function sourceIdsLinkedToNodes(store, nodeIds, spaceId) {
361
352
  }
362
353
  function selectEvidenceExcerpt(query, text, facts) {
363
354
  const tokens = expandQueryTokens(tokenizeSemanticQuery(query));
355
+ const intent = factIntent(tokenizeSemanticQuery(query));
356
+ const featureIntent = Boolean(intent && hasFeatureIntent(intent));
364
357
  const factLines = facts
365
358
  .map(renderFactForPrompt)
366
359
  .filter((line) => scoreSemanticText(line, tokens) > 0)
360
+ .filter((line) => !featureIntent || !isLowValueFeatureOrSpecText(line))
367
361
  .slice(0, 12);
368
- const windows = evidenceWindows(text, tokens).slice(0, 4);
369
- return uniqueStrings([...factLines, ...windows, clampText(text, 720)]).join('\n');
362
+ const windows = evidenceWindows(text, tokens)
363
+ .filter((line) => !featureIntent || !isLowValueFeatureOrSpecText(line))
364
+ .slice(0, 4);
365
+ const fallback = featureIntent && (factLines.length > 0 || windows.length > 0) ? [] : [clampText(text, 720)];
366
+ return uniqueStrings([...factLines, ...windows, ...fallback]).join('\n');
370
367
  }
371
368
  function evidenceWindows(text, tokens) {
372
369
  const normalized = normalizeWhitespace(text);
@@ -0,0 +1,7 @@
1
+ import type { KnowledgeNodeRecord } from '../types.js';
2
+ export declare function isSemanticAnswerLinkedObject(node: KnowledgeNodeRecord): boolean;
3
+ export declare function semanticFactText(fact: KnowledgeNodeRecord): string;
4
+ export declare function isLowValueFeatureOrSpecText(text: string): boolean;
5
+ export declare function hasConcreteFeatureSignal(text: string): boolean;
6
+ export declare function isUsefulHomeGraphPageFact(fact: KnowledgeNodeRecord): boolean;
7
+ //# sourceMappingURL=fact-quality.d.ts.map
@@ -0,0 +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,CAgCjE;AAWD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAa5E"}
@@ -0,0 +1,93 @@
1
+ import { readString } from './utils.js';
2
+ const USEFUL_PAGE_FACT_KINDS = new Set([
3
+ 'feature',
4
+ 'capability',
5
+ 'specification',
6
+ 'identity',
7
+ 'maintenance',
8
+ 'compatibility',
9
+ 'configuration',
10
+ 'troubleshooting',
11
+ ]);
12
+ export function isSemanticAnswerLinkedObject(node) {
13
+ if (node.status === 'stale')
14
+ return false;
15
+ const semanticKind = readString(node.metadata.semanticKind);
16
+ if (semanticKind)
17
+ return false;
18
+ return node.kind !== 'fact' && node.kind !== 'wiki_page' && node.kind !== 'knowledge_gap';
19
+ }
20
+ export function semanticFactText(fact) {
21
+ return [
22
+ fact.title,
23
+ fact.summary,
24
+ readString(fact.metadata.value),
25
+ readString(fact.metadata.evidence),
26
+ Array.isArray(fact.metadata.labels) ? fact.metadata.labels.join(' ') : '',
27
+ ].filter(Boolean).join(' ').toLowerCase();
28
+ }
29
+ export function isLowValueFeatureOrSpecText(text) {
30
+ const lower = text.toLowerCase();
31
+ const magicRemoteDetail = /\bmagic remote\b/.test(lower) || /\bbluetooth\b/.test(lower);
32
+ if (isTruncatedManualFragment(lower))
33
+ return true;
34
+ 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)) {
35
+ return true;
36
+ }
37
+ if (/\b(new features? may be added|features? may be added|specifications? may change|product upgrades?|due to product upgrades?)\b/.test(lower)) {
38
+ return true;
39
+ }
40
+ if (/\b(recommended hdmi cable types?|hdmi cable types?|ultra high speed hdmi cables?|usb extension cable|extension cable|physically fit)\b/.test(lower)) {
41
+ return true;
42
+ }
43
+ if (/\b(button|buttons|remote control)\b/.test(lower) && /[\u25b2\u25bc\u25c4\u25ba]|[▲▼◄►]|\\u25/.test(text)) {
44
+ return true;
45
+ }
46
+ if (!magicRemoteDetail && /\b(may vary|depending (upon|on) (the )?model|depending on country|depending on region)\b/.test(lower)) {
47
+ return true;
48
+ }
49
+ if (/\b(fasten|screws?|stand|tip over|overturn|fall over|transporting|move the tv|moving the tv|oils?|lubricants?|cleaning cloth|dry cloth|power cord|electric shock|fire hazard|near water|ventilation|antenna grounding|qualified personnel|customer service|servicing|repair is required|refer all servicing)\b/.test(lower)) {
50
+ return true;
51
+ }
52
+ if (/\bbatter(y|ies)\b/.test(lower) && /\b(remote|magic remote|button)\b/.test(lower)) {
53
+ return true;
54
+ }
55
+ if (/\b(bezel|less than \d+(?:\.\d+)?\s*(mm|cm|inches?)|does not fit|will not fit|fit your tv'?s usb port|usb port may not fit)\b/.test(lower)) {
56
+ return true;
57
+ }
58
+ if (/\b(warning|caution|risk|hazard|do not|never)\b/.test(lower) && !/\b(feature|supports?|hdmi|usb|hdr|remote|bluetooth)\b/.test(lower)) {
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ function isTruncatedManualFragment(value) {
64
+ const trimmed = value.trim();
65
+ if (trimmed.length === 0)
66
+ return false;
67
+ const openParens = trimmed.match(/\(/g)?.length ?? 0;
68
+ const closeParens = trimmed.match(/\)/g)?.length ?? 0;
69
+ if (openParens > closeParens && /[\w\d]$/.test(trimmed))
70
+ return true;
71
+ return false;
72
+ }
73
+ export function hasConcreteFeatureSignal(text) {
74
+ return /\b(hdmi|usb|hdr|hdr10|dolby|vision|earc|arc|bluetooth|wi-?fi|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)\b/.test(text.toLowerCase());
75
+ }
76
+ export function isUsefulHomeGraphPageFact(fact) {
77
+ if (fact.status === 'stale')
78
+ return false;
79
+ if (fact.metadata.semanticKind !== 'fact')
80
+ return false;
81
+ const kind = readString(fact.metadata.factKind) ?? 'note';
82
+ if (!USEFUL_PAGE_FACT_KINDS.has(kind))
83
+ return false;
84
+ const text = semanticFactText(fact);
85
+ if (isLowValueFeatureOrSpecText(text))
86
+ return false;
87
+ const extractor = readString(fact.metadata.extractor);
88
+ const confidence = typeof fact.confidence === 'number' ? fact.confidence : 0;
89
+ if (extractor === 'deterministic' && confidence <= 60 && ['feature', 'capability', 'specification', 'compatibility', 'configuration'].includes(kind)) {
90
+ return hasConcreteFeatureSignal(text);
91
+ }
92
+ return true;
93
+ }
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- let version = '0.27.6';
3
+ let version = '0.27.8';
4
4
  try {
5
5
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
6
6
  version = pkg.version ?? version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-sdk",
3
- "version": "0.27.6",
3
+ "version": "0.27.8",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/mgd34msu/goodvibes-sdk.git"