@pellux/goodvibes-sdk 0.27.2 → 0.27.4

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 (100) hide show
  1. package/dist/_internal/contracts/artifacts/operator-contract.json +845 -4
  2. package/dist/_internal/contracts/generated/foundation-metadata.d.ts +2 -2
  3. package/dist/_internal/contracts/generated/foundation-metadata.js +2 -2
  4. package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
  5. package/dist/_internal/contracts/generated/operator-contract.js +845 -4
  6. package/dist/_internal/contracts/generated/operator-method-ids.d.ts +1 -1
  7. package/dist/_internal/contracts/generated/operator-method-ids.d.ts.map +1 -1
  8. package/dist/_internal/contracts/generated/operator-method-ids.js +2 -0
  9. package/dist/_internal/daemon/context.d.ts +1 -0
  10. package/dist/_internal/daemon/context.d.ts.map +1 -1
  11. package/dist/_internal/daemon/knowledge-route-types.d.ts +1 -0
  12. package/dist/_internal/daemon/knowledge-route-types.d.ts.map +1 -1
  13. package/dist/_internal/daemon/knowledge-routes.d.ts +1 -1
  14. package/dist/_internal/daemon/knowledge-routes.d.ts.map +1 -1
  15. package/dist/_internal/daemon/knowledge-routes.js +18 -0
  16. package/dist/_internal/daemon/operator.d.ts +1 -1
  17. package/dist/_internal/daemon/operator.d.ts.map +1 -1
  18. package/dist/_internal/daemon/operator.js +2 -0
  19. package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -1
  20. package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +10 -1
  21. package/dist/_internal/platform/control-plane/method-catalog-knowledge.d.ts.map +1 -1
  22. package/dist/_internal/platform/control-plane/method-catalog-knowledge.js +20 -1
  23. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts +2 -0
  24. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts.map +1 -1
  25. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.js +23 -0
  26. package/dist/_internal/platform/control-plane/routes/operator.d.ts +1 -1
  27. package/dist/_internal/platform/control-plane/routes/operator.d.ts.map +1 -1
  28. package/dist/_internal/platform/control-plane/routes/operator.js +2 -0
  29. package/dist/_internal/platform/daemon/http/home-graph-routes.d.ts.map +1 -1
  30. package/dist/_internal/platform/daemon/http/home-graph-routes.js +7 -0
  31. package/dist/_internal/platform/daemon/http/router-route-contexts.d.ts.map +1 -1
  32. package/dist/_internal/platform/daemon/http/router-route-contexts.js +1 -0
  33. package/dist/_internal/platform/knowledge/extractors.d.ts.map +1 -1
  34. package/dist/_internal/platform/knowledge/extractors.js +1 -116
  35. package/dist/_internal/platform/knowledge/home-graph/ask.d.ts +13 -0
  36. package/dist/_internal/platform/knowledge/home-graph/ask.d.ts.map +1 -0
  37. package/dist/_internal/platform/knowledge/home-graph/ask.js +65 -0
  38. package/dist/_internal/platform/knowledge/home-graph/auto-link.d.ts +27 -0
  39. package/dist/_internal/platform/knowledge/home-graph/auto-link.d.ts.map +1 -0
  40. package/dist/_internal/platform/knowledge/home-graph/auto-link.js +236 -0
  41. package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts +1 -0
  42. package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
  43. package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +24 -8
  44. package/dist/_internal/platform/knowledge/home-graph/index.d.ts +1 -1
  45. package/dist/_internal/platform/knowledge/home-graph/index.d.ts.map +1 -1
  46. package/dist/_internal/platform/knowledge/home-graph/pages.d.ts +11 -0
  47. package/dist/_internal/platform/knowledge/home-graph/pages.d.ts.map +1 -0
  48. package/dist/_internal/platform/knowledge/home-graph/pages.js +44 -0
  49. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts +4 -1
  50. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -1
  51. package/dist/_internal/platform/knowledge/home-graph/rendering.js +215 -4
  52. package/dist/_internal/platform/knowledge/home-graph/service.d.ts +12 -2
  53. package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
  54. package/dist/_internal/platform/knowledge/home-graph/service.js +78 -21
  55. package/dist/_internal/platform/knowledge/home-graph/types.d.ts +31 -1
  56. package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
  57. package/dist/_internal/platform/knowledge/home-graph/types.js +2 -0
  58. package/dist/_internal/platform/knowledge/index.d.ts +3 -1
  59. package/dist/_internal/platform/knowledge/index.d.ts.map +1 -1
  60. package/dist/_internal/platform/knowledge/index.js +1 -0
  61. package/dist/_internal/platform/knowledge/ingest-compile.d.ts.map +1 -1
  62. package/dist/_internal/platform/knowledge/ingest-compile.js +5 -0
  63. package/dist/_internal/platform/knowledge/ingest-context.d.ts +1 -0
  64. package/dist/_internal/platform/knowledge/ingest-context.d.ts.map +1 -1
  65. package/dist/_internal/platform/knowledge/map.js +15 -1
  66. package/dist/_internal/platform/knowledge/pdf-extractor.d.ts +3 -0
  67. package/dist/_internal/platform/knowledge/pdf-extractor.d.ts.map +1 -0
  68. package/dist/_internal/platform/knowledge/pdf-extractor.js +346 -0
  69. package/dist/_internal/platform/knowledge/scheduling.d.ts.map +1 -1
  70. package/dist/_internal/platform/knowledge/scheduling.js +1 -0
  71. package/dist/_internal/platform/knowledge/semantic/answer.d.ts +9 -0
  72. package/dist/_internal/platform/knowledge/semantic/answer.d.ts.map +1 -0
  73. package/dist/_internal/platform/knowledge/semantic/answer.js +407 -0
  74. package/dist/_internal/platform/knowledge/semantic/enrichment.d.ts +23 -0
  75. package/dist/_internal/platform/knowledge/semantic/enrichment.d.ts.map +1 -0
  76. package/dist/_internal/platform/knowledge/semantic/enrichment.js +491 -0
  77. package/dist/_internal/platform/knowledge/semantic/index.d.ts +5 -0
  78. package/dist/_internal/platform/knowledge/semantic/index.d.ts.map +1 -0
  79. package/dist/_internal/platform/knowledge/semantic/index.js +2 -0
  80. package/dist/_internal/platform/knowledge/semantic/llm.d.ts +4 -0
  81. package/dist/_internal/platform/knowledge/semantic/llm.d.ts.map +1 -0
  82. package/dist/_internal/platform/knowledge/semantic/llm.js +34 -0
  83. package/dist/_internal/platform/knowledge/semantic/service.d.ts +37 -0
  84. package/dist/_internal/platform/knowledge/semantic/service.d.ts.map +1 -0
  85. package/dist/_internal/platform/knowledge/semantic/service.js +55 -0
  86. package/dist/_internal/platform/knowledge/semantic/types.d.ts +107 -0
  87. package/dist/_internal/platform/knowledge/semantic/types.d.ts.map +1 -0
  88. package/dist/_internal/platform/knowledge/semantic/types.js +1 -0
  89. package/dist/_internal/platform/knowledge/semantic/utils.d.ts +39 -0
  90. package/dist/_internal/platform/knowledge/semantic/utils.d.ts.map +1 -0
  91. package/dist/_internal/platform/knowledge/semantic/utils.js +189 -0
  92. package/dist/_internal/platform/knowledge/service.d.ts +12 -0
  93. package/dist/_internal/platform/knowledge/service.d.ts.map +1 -1
  94. package/dist/_internal/platform/knowledge/service.js +14 -0
  95. package/dist/_internal/platform/knowledge/types.d.ts +2 -2
  96. package/dist/_internal/platform/knowledge/types.d.ts.map +1 -1
  97. package/dist/_internal/platform/runtime/services.d.ts.map +1 -1
  98. package/dist/_internal/platform/runtime/services.js +8 -2
  99. package/dist/_internal/platform/version.js +1 -1
  100. package/package.json +1 -1
@@ -0,0 +1,491 @@
1
+ import { MAX_SEMANTIC_SOURCE_CHARS, applySourceMetadata, clampText, normalizeWhitespace, readRecord, readString, semanticHash, semanticMetadata, semanticSlug, sourceKnowledgeSpace, sourceSemanticHash, sourceSemanticText, splitSentences, uniqueStrings, } from './utils.js';
2
+ export async function enrichKnowledgeSource(context, source, options = {}) {
3
+ const extraction = context.store.getExtractionBySourceId(source.id);
4
+ const text = sourceSemanticText(source, extraction);
5
+ const textHash = sourceSemanticHash(source, extraction);
6
+ const existingSemantic = readRecord(source.metadata.semanticEnrichment);
7
+ if (!options.force && existingSemantic.textHash === textHash) {
8
+ return emptyResult(source, true, 'semantic enrichment is current');
9
+ }
10
+ if (text.length < 40) {
11
+ await markSourceSemanticState(context.store, source, textHash, {
12
+ skippedReason: 'source has too little extracted text',
13
+ });
14
+ return emptyResult(source, true, 'source has too little extracted text');
15
+ }
16
+ const llmExtraction = await extractSemanticsWithLlm(context.llm ?? null, source, extraction, text);
17
+ const semantic = normalizeSemanticExtraction(llmExtraction)
18
+ ?? deterministicSemanticExtraction(source, extraction, text);
19
+ const persisted = await persistSemanticExtraction(context.store, source, extraction, semantic, {
20
+ knowledgeSpaceId: options.knowledgeSpaceId,
21
+ textHash,
22
+ });
23
+ await markSourceSemanticState(context.store, source, textHash, {
24
+ extractor: semantic.extractor,
25
+ factCount: persisted.facts.length,
26
+ entityCount: persisted.entities.length,
27
+ gapCount: persisted.gaps.length,
28
+ });
29
+ return persisted;
30
+ }
31
+ async function extractSemanticsWithLlm(llm, source, extraction, text) {
32
+ if (!llm)
33
+ return null;
34
+ return llm.completeJson({
35
+ purpose: 'knowledge-semantic-enrichment',
36
+ maxTokens: 2600,
37
+ systemPrompt: [
38
+ 'You extract a durable semantic knowledge graph from source material.',
39
+ 'Return only JSON. Do not invent facts. Every fact must be grounded in the supplied source text.',
40
+ 'Capture capabilities, features, specifications, procedures, warnings, maintenance items, compatibility, configuration, and troubleshooting facts when present.',
41
+ 'Prefer precise facts over broad summaries. Preserve numbers, model names, ports, version names, constraints, and useful procedures.',
42
+ ].join(' '),
43
+ prompt: JSON.stringify({
44
+ source: {
45
+ id: source.id,
46
+ title: source.title,
47
+ sourceType: source.sourceType,
48
+ tags: source.tags,
49
+ uri: source.canonicalUri ?? source.sourceUri,
50
+ metadata: source.metadata,
51
+ },
52
+ extraction: {
53
+ format: extraction?.format,
54
+ title: extraction?.title,
55
+ summary: extraction?.summary,
56
+ sections: extraction?.sections.slice(0, 80),
57
+ },
58
+ instructions: {
59
+ outputShape: {
60
+ summary: 'short source summary',
61
+ entities: [{ title: 'entity name', kind: 'entity type', aliases: ['alternate names'], summary: 'one sentence', confidence: 0 }],
62
+ facts: [{
63
+ kind: 'feature|capability|specification|identity|procedure|warning|maintenance|compatibility|configuration|troubleshooting|relationship|note',
64
+ title: 'short fact title',
65
+ value: 'precise value when applicable',
66
+ summary: 'source-grounded explanation',
67
+ evidence: 'short quote or close paraphrase from source',
68
+ confidence: 0,
69
+ labels: ['optional labels'],
70
+ targetHints: ['entities this fact describes'],
71
+ }],
72
+ relations: [{ from: 'entity/fact title', relation: 'relation label', to: 'entity/fact title', evidence: 'source-grounded evidence', confidence: 0 }],
73
+ gaps: [{ question: 'missing useful question', reason: 'why source does not answer it', subject: 'optional subject', severity: 'info|warning|error' }],
74
+ wikiPage: { title: 'living page title', markdown: 'concise markdown page synthesized only from extracted facts' },
75
+ },
76
+ },
77
+ text: clampText(text, MAX_SEMANTIC_SOURCE_CHARS),
78
+ }),
79
+ });
80
+ }
81
+ function normalizeSemanticExtraction(value) {
82
+ const record = readRecord(value);
83
+ const facts = readArray(record.facts).map(normalizeFact).filter(isFact);
84
+ const entities = readArray(record.entities).map(normalizeEntity).filter(isEntity);
85
+ const relations = readArray(record.relations).map(normalizeRelation).filter(isRelation);
86
+ const gaps = readArray(record.gaps).map(normalizeGap).filter(isGap);
87
+ const wikiRecord = readRecord(record.wikiPage);
88
+ const markdown = readString(wikiRecord.markdown);
89
+ const title = readString(wikiRecord.title);
90
+ if (facts.length === 0 && entities.length === 0 && !markdown)
91
+ return null;
92
+ return {
93
+ summary: readString(record.summary),
94
+ entities,
95
+ facts,
96
+ relations,
97
+ gaps,
98
+ ...(markdown || title ? { wikiPage: { ...(title ? { title } : {}), ...(markdown ? { markdown } : {}) } } : {}),
99
+ extractor: 'llm',
100
+ };
101
+ }
102
+ function deterministicSemanticExtraction(source, extraction, text) {
103
+ const sentences = splitSentences(text);
104
+ const facts = sentences
105
+ .map((sentence) => classifySentenceFact(sentence))
106
+ .filter((fact) => Boolean(fact))
107
+ .slice(0, 80);
108
+ const entities = uniqueStrings([
109
+ source.title,
110
+ extraction?.title,
111
+ ...source.tags,
112
+ ]).slice(0, 12).map((title) => ({
113
+ title,
114
+ kind: title === source.title ? source.sourceType : 'topic',
115
+ summary: `Entity inferred from ${source.title ?? source.id}.`,
116
+ confidence: 45,
117
+ }));
118
+ return {
119
+ summary: extraction?.summary ?? source.summary ?? clampText(text, 360),
120
+ entities,
121
+ facts,
122
+ relations: [],
123
+ gaps: facts.length === 0
124
+ ? [{ question: `What useful facts should be extracted from ${source.title ?? source.id}?`, reason: 'No high-confidence semantic facts were detected.', severity: 'info' }]
125
+ : [],
126
+ wikiPage: {
127
+ title: source.title ? `${source.title} knowledge page` : 'Knowledge page',
128
+ markdown: renderDeterministicWikiPage(source, facts),
129
+ },
130
+ extractor: 'deterministic',
131
+ };
132
+ }
133
+ function classifySentenceFact(sentence) {
134
+ const text = normalizeWhitespace(sentence);
135
+ if (text.length < 28)
136
+ return null;
137
+ const lower = text.toLowerCase();
138
+ const kind = (() => {
139
+ if (/\b(warning|caution|do not|never|risk|hazard|important)\b/.test(lower))
140
+ return 'warning';
141
+ if (/\b(reset|press|hold|select|open|install|pair|configure|enable|disable|connect|setup|set up)\b/.test(lower))
142
+ return 'procedure';
143
+ if (/\b(clean|replace|battery|filter|firmware|update|service|maintenance|warranty)\b/.test(lower))
144
+ return 'maintenance';
145
+ if (/\b(compatible|works with|requires|supports?|supported|connects? to|integrates? with)\b/.test(lower))
146
+ return 'compatibility';
147
+ if (/\b(feature|features|capabilit|function|mode|built-in|includes?|provides?|allows?|can )\b/.test(lower))
148
+ return 'feature';
149
+ if (/\b(specification|specifications|model|serial|version|hdmi|usb|port|ports|resolution|volt|watt|hz|inch|mm|gb|mb|ip[0-9])\b/.test(lower))
150
+ return 'specification';
151
+ return null;
152
+ })();
153
+ if (!kind)
154
+ return null;
155
+ return {
156
+ kind,
157
+ title: titleFromSentence(text),
158
+ summary: text,
159
+ evidence: text,
160
+ confidence: 55,
161
+ labels: inferLabels(text),
162
+ };
163
+ }
164
+ async function persistSemanticExtraction(store, source, extraction, semantic, options) {
165
+ const spaceId = options.knowledgeSpaceId ?? sourceKnowledgeSpace(source);
166
+ const entities = [];
167
+ const facts = [];
168
+ const gaps = [];
169
+ for (const entity of semantic.entities.slice(0, 60)) {
170
+ const node = await store.upsertNode({
171
+ id: `sem-entity-${semanticHash(spaceId, source.id, entity.title)}`,
172
+ kind: 'knowledge_entity',
173
+ slug: semanticSlug(`${spaceId}-${entity.title}`),
174
+ title: entity.title,
175
+ summary: entity.summary,
176
+ aliases: entity.aliases,
177
+ confidence: entity.confidence ?? 65,
178
+ sourceId: source.id,
179
+ metadata: semanticMetadata(spaceId, {
180
+ ...(entity.metadata ?? {}),
181
+ semanticKind: 'entity',
182
+ entityKind: entity.kind ?? 'entity',
183
+ sourceId: source.id,
184
+ extractionId: extraction?.id,
185
+ extractor: semantic.extractor,
186
+ textHash: options.textHash,
187
+ }),
188
+ });
189
+ entities.push(node);
190
+ await linkSourceToNode(store, source.id, node.id, 'mentions_entity', spaceId, semantic.extractor);
191
+ }
192
+ for (const fact of semantic.facts.slice(0, 160)) {
193
+ const node = await store.upsertNode({
194
+ id: `sem-fact-${semanticHash(spaceId, source.id, fact.kind, fact.title, fact.value ?? fact.summary)}`,
195
+ kind: 'fact',
196
+ slug: semanticSlug(`${spaceId}-${fact.kind}-${fact.title}-${fact.value ?? ''}`),
197
+ title: fact.title,
198
+ summary: fact.summary ?? fact.value ?? fact.evidence,
199
+ aliases: fact.labels,
200
+ confidence: fact.confidence ?? 70,
201
+ sourceId: source.id,
202
+ metadata: semanticMetadata(spaceId, {
203
+ semanticKind: 'fact',
204
+ factKind: fact.kind,
205
+ value: fact.value,
206
+ evidence: fact.evidence,
207
+ labels: fact.labels ?? [],
208
+ targetHints: fact.targetHints ?? [],
209
+ sourceId: source.id,
210
+ extractionId: extraction?.id,
211
+ extractor: semantic.extractor,
212
+ textHash: options.textHash,
213
+ }),
214
+ });
215
+ facts.push(node);
216
+ await linkSourceToNode(store, source.id, node.id, 'supports_fact', spaceId, semantic.extractor);
217
+ await linkFactToEntities(store, node, entities, fact, spaceId, semantic.extractor);
218
+ }
219
+ for (const relation of semantic.relations.slice(0, 80)) {
220
+ await linkRelation(store, entities, facts, relation, spaceId, semantic.extractor);
221
+ }
222
+ for (const gap of semantic.gaps.slice(0, 32)) {
223
+ const node = await store.upsertNode({
224
+ id: `sem-gap-${semanticHash(spaceId, source.id, gap.question)}`,
225
+ kind: 'knowledge_gap',
226
+ slug: semanticSlug(`${spaceId}-gap-${gap.question}`),
227
+ title: gap.question,
228
+ summary: gap.reason,
229
+ confidence: gap.severity === 'error' ? 85 : gap.severity === 'warning' ? 70 : 50,
230
+ sourceId: source.id,
231
+ metadata: semanticMetadata(spaceId, {
232
+ semanticKind: 'gap',
233
+ subject: gap.subject,
234
+ severity: gap.severity ?? 'info',
235
+ sourceId: source.id,
236
+ extractionId: extraction?.id,
237
+ extractor: semantic.extractor,
238
+ textHash: options.textHash,
239
+ }),
240
+ });
241
+ gaps.push(node);
242
+ await linkSourceToNode(store, source.id, node.id, 'has_gap', spaceId, semantic.extractor);
243
+ await store.upsertIssue({
244
+ id: `sem-issue-${semanticHash(spaceId, source.id, gap.question)}`,
245
+ severity: gap.severity ?? 'info',
246
+ code: 'knowledge.semantic_gap',
247
+ message: gap.question,
248
+ status: 'open',
249
+ sourceId: source.id,
250
+ nodeId: node.id,
251
+ metadata: semanticMetadata(spaceId, {
252
+ reason: gap.reason,
253
+ subject: gap.subject,
254
+ namespace: `knowledge:${spaceId}:semantic`,
255
+ }),
256
+ });
257
+ }
258
+ const wikiPage = await persistWikiPage(store, source, semantic, spaceId, options.textHash);
259
+ return { source, skipped: false, extractor: semantic.extractor, facts, entities, gaps, ...(wikiPage ? { wikiPage } : {}) };
260
+ }
261
+ async function persistWikiPage(store, source, semantic, spaceId, textHash) {
262
+ const markdown = semantic.wikiPage?.markdown ?? renderDeterministicWikiPage(source, semantic.facts);
263
+ if (!markdown.trim())
264
+ return undefined;
265
+ const page = await store.upsertNode({
266
+ id: `sem-page-${semanticHash(spaceId, source.id)}`,
267
+ kind: 'wiki_page',
268
+ slug: semanticSlug(`${spaceId}-${source.title ?? source.id}-page`),
269
+ title: semantic.wikiPage?.title ?? `${source.title ?? source.id} knowledge page`,
270
+ summary: semantic.summary ?? source.summary,
271
+ aliases: source.title ? [source.title] : [],
272
+ confidence: semantic.extractor === 'llm' ? 80 : 55,
273
+ sourceId: source.id,
274
+ metadata: semanticMetadata(spaceId, {
275
+ semanticKind: 'wiki_page',
276
+ markdown,
277
+ sourceId: source.id,
278
+ extractor: semantic.extractor,
279
+ textHash,
280
+ }),
281
+ });
282
+ await linkSourceToNode(store, source.id, page.id, 'compiled_into_page', spaceId, semantic.extractor);
283
+ return page;
284
+ }
285
+ async function markSourceSemanticState(store, source, textHash, details) {
286
+ await store.upsertSource(applySourceMetadata(source, {
287
+ semanticEnrichment: {
288
+ textHash,
289
+ enrichedAt: Date.now(),
290
+ ...details,
291
+ },
292
+ }));
293
+ }
294
+ async function linkSourceToNode(store, sourceId, nodeId, relation, spaceId, extractor) {
295
+ await store.upsertEdge({
296
+ fromKind: 'source',
297
+ fromId: sourceId,
298
+ toKind: 'node',
299
+ toId: nodeId,
300
+ relation,
301
+ weight: extractor === 'llm' ? 1 : 0.6,
302
+ metadata: semanticMetadata(spaceId, { extractor }),
303
+ });
304
+ }
305
+ async function linkFactToEntities(store, fact, entities, input, spaceId, extractor) {
306
+ const hints = uniqueStrings(input.targetHints ?? []);
307
+ const candidates = hints.length === 0
308
+ ? entities.slice(0, 1)
309
+ : entities.filter((entity) => hints.some((hint) => entityMatchesHint(entity, hint)));
310
+ for (const entity of candidates.slice(0, 6)) {
311
+ await store.upsertEdge({
312
+ fromKind: 'node',
313
+ fromId: fact.id,
314
+ toKind: 'node',
315
+ toId: entity.id,
316
+ relation: 'describes',
317
+ metadata: semanticMetadata(spaceId, { extractor }),
318
+ });
319
+ }
320
+ }
321
+ async function linkRelation(store, entities, facts, relation, spaceId, extractor) {
322
+ const from = findSemanticNode([...entities, ...facts], relation.from);
323
+ const to = findSemanticNode([...entities, ...facts], relation.to);
324
+ if (!from || !to || from.id === to.id)
325
+ return;
326
+ await store.upsertEdge({
327
+ fromKind: 'node',
328
+ fromId: from.id,
329
+ toKind: 'node',
330
+ toId: to.id,
331
+ relation: semanticSlug(relation.relation).replace(/-/g, '_') || 'related_to',
332
+ weight: Math.max(0.1, Math.min(1, (relation.confidence ?? 70) / 100)),
333
+ metadata: semanticMetadata(spaceId, {
334
+ evidence: relation.evidence,
335
+ extractor,
336
+ }),
337
+ });
338
+ }
339
+ function renderDeterministicWikiPage(source, facts) {
340
+ const grouped = new Map();
341
+ for (const fact of facts) {
342
+ grouped.set(fact.kind, [...(grouped.get(fact.kind) ?? []), fact]);
343
+ }
344
+ const sections = [...grouped.entries()].flatMap(([kind, entries]) => [
345
+ `## ${titleCase(kind)}`,
346
+ '',
347
+ ...entries.slice(0, 24).map((fact) => `- ${fact.title}${fact.value ? `: ${fact.value}` : ''}${fact.summary ? ` - ${fact.summary}` : ''}`),
348
+ '',
349
+ ]);
350
+ return [
351
+ `# ${source.title ?? source.id}`,
352
+ '',
353
+ source.summary ?? '',
354
+ '',
355
+ ...sections,
356
+ ].filter((line) => line !== undefined).join('\n').trim();
357
+ }
358
+ function normalizeFact(value) {
359
+ const record = readRecord(value);
360
+ const kind = normalizeFactKind(readString(record.kind));
361
+ const title = readString(record.title);
362
+ if (!kind || !title)
363
+ return null;
364
+ return {
365
+ kind,
366
+ title,
367
+ ...(readString(record.value) ? { value: readString(record.value) } : {}),
368
+ ...(readString(record.summary) ? { summary: readString(record.summary) } : {}),
369
+ ...(readString(record.evidence) ? { evidence: readString(record.evidence) } : {}),
370
+ ...(typeof record.confidence === 'number' ? { confidence: clampConfidence(record.confidence) } : {}),
371
+ labels: readStringArray(record.labels),
372
+ targetHints: readStringArray(record.targetHints),
373
+ };
374
+ }
375
+ function normalizeEntity(value) {
376
+ const record = readRecord(value);
377
+ const title = readString(record.title);
378
+ if (!title)
379
+ return null;
380
+ return {
381
+ title,
382
+ ...(readString(record.kind) ? { kind: readString(record.kind) } : {}),
383
+ aliases: readStringArray(record.aliases),
384
+ ...(readString(record.summary) ? { summary: readString(record.summary) } : {}),
385
+ ...(typeof record.confidence === 'number' ? { confidence: clampConfidence(record.confidence) } : {}),
386
+ metadata: readRecord(record.metadata),
387
+ };
388
+ }
389
+ function normalizeRelation(value) {
390
+ const record = readRecord(value);
391
+ const from = readString(record.from);
392
+ const relation = readString(record.relation);
393
+ const to = readString(record.to);
394
+ if (!from || !relation || !to)
395
+ return null;
396
+ return {
397
+ from,
398
+ relation,
399
+ to,
400
+ ...(readString(record.evidence) ? { evidence: readString(record.evidence) } : {}),
401
+ ...(typeof record.confidence === 'number' ? { confidence: clampConfidence(record.confidence) } : {}),
402
+ };
403
+ }
404
+ function normalizeGap(value) {
405
+ const record = readRecord(value);
406
+ const question = readString(record.question);
407
+ if (!question)
408
+ return null;
409
+ const severity = readString(record.severity);
410
+ return {
411
+ question,
412
+ ...(readString(record.reason) ? { reason: readString(record.reason) } : {}),
413
+ ...(readString(record.subject) ? { subject: readString(record.subject) } : {}),
414
+ severity: severity === 'warning' || severity === 'error' ? severity : 'info',
415
+ };
416
+ }
417
+ function normalizeFactKind(value) {
418
+ switch (value) {
419
+ case 'feature':
420
+ case 'capability':
421
+ case 'specification':
422
+ case 'identity':
423
+ case 'procedure':
424
+ case 'warning':
425
+ case 'maintenance':
426
+ case 'compatibility':
427
+ case 'configuration':
428
+ case 'troubleshooting':
429
+ case 'relationship':
430
+ case 'note':
431
+ return value;
432
+ default:
433
+ return value ? 'note' : null;
434
+ }
435
+ }
436
+ function readArray(value) {
437
+ return Array.isArray(value) ? value : [];
438
+ }
439
+ function readStringArray(value) {
440
+ return Array.isArray(value)
441
+ ? uniqueStrings(value.map((entry) => typeof entry === 'string' ? entry : undefined))
442
+ : [];
443
+ }
444
+ function isFact(value) {
445
+ return Boolean(value);
446
+ }
447
+ function isEntity(value) {
448
+ return Boolean(value);
449
+ }
450
+ function isRelation(value) {
451
+ return Boolean(value);
452
+ }
453
+ function isGap(value) {
454
+ return Boolean(value);
455
+ }
456
+ function clampConfidence(value) {
457
+ return Math.max(0, Math.min(100, Math.round(value)));
458
+ }
459
+ function titleFromSentence(sentence) {
460
+ const withoutLead = sentence.replace(/^(the|this|these|it)\s+/i, '');
461
+ return clampText(withoutLead, 96).replace(/[.:;,\s]+$/g, '');
462
+ }
463
+ function inferLabels(text) {
464
+ const lower = text.toLowerCase();
465
+ return uniqueStrings([
466
+ /\bhdmi\b/.test(lower) ? 'hdmi' : undefined,
467
+ /\busb\b/.test(lower) ? 'usb' : undefined,
468
+ /\bbattery\b/.test(lower) ? 'battery' : undefined,
469
+ /\bfirmware\b/.test(lower) ? 'firmware' : undefined,
470
+ /\bwarranty\b/.test(lower) ? 'warranty' : undefined,
471
+ /\bvoice\b/.test(lower) ? 'voice' : undefined,
472
+ /\bnetwork|wi-?fi|ethernet|bluetooth\b/.test(lower) ? 'network' : undefined,
473
+ ]);
474
+ }
475
+ function entityMatchesHint(entity, hint) {
476
+ const lower = hint.toLowerCase();
477
+ return entity.title.toLowerCase().includes(lower)
478
+ || entity.aliases.some((alias) => alias.toLowerCase().includes(lower))
479
+ || lower.includes(entity.title.toLowerCase());
480
+ }
481
+ function findSemanticNode(nodes, label) {
482
+ const lower = label.toLowerCase();
483
+ return nodes.find((node) => node.title.toLowerCase() === lower)
484
+ ?? nodes.find((node) => node.title.toLowerCase().includes(lower) || lower.includes(node.title.toLowerCase()));
485
+ }
486
+ function titleCase(value) {
487
+ return value.replace(/[_-]+/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
488
+ }
489
+ function emptyResult(source, skipped, reason) {
490
+ return { source, skipped, reason, facts: [], entities: [], gaps: [] };
491
+ }
@@ -0,0 +1,5 @@
1
+ export { createProviderBackedKnowledgeSemanticLlm } from './llm.js';
2
+ export { KnowledgeSemanticService } from './service.js';
3
+ export type { KnowledgeSemanticServiceOptions } from './service.js';
4
+ export type { KnowledgeSemanticAnswer, KnowledgeSemanticAnswerInput, KnowledgeSemanticAnswerResult, KnowledgeSemanticEnrichmentResult, KnowledgeSemanticExtraction, KnowledgeSemanticFactInput, KnowledgeSemanticFactKind, KnowledgeSemanticGapInput, KnowledgeSemanticLlm, KnowledgeSemanticLlmAnswer, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wCAAwC,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EACV,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createProviderBackedKnowledgeSemanticLlm } from './llm.js';
2
+ export { KnowledgeSemanticService } from './service.js';
@@ -0,0 +1,4 @@
1
+ import type { ProviderRegistry } from '../../providers/registry.js';
2
+ import type { KnowledgeSemanticLlm } from './types.js';
3
+ export declare function createProviderBackedKnowledgeSemanticLlm(providerRegistry: ProviderRegistry): KnowledgeSemanticLlm;
4
+ //# sourceMappingURL=llm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/llm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,wBAAgB,wCAAwC,CACtD,gBAAgB,EAAE,gBAAgB,GACjC,oBAAoB,CAQtB"}
@@ -0,0 +1,34 @@
1
+ import { logger } from '../../utils/logger.js';
2
+ import { summarizeError } from '../../utils/error-display.js';
3
+ import { extractJsonObject } from './utils.js';
4
+ export function createProviderBackedKnowledgeSemanticLlm(providerRegistry) {
5
+ return {
6
+ completeJson: async (input) => {
7
+ const text = await completeWithCurrentModel(providerRegistry, input);
8
+ return text ? extractJsonObject(text) : null;
9
+ },
10
+ completeText: async (input) => completeWithCurrentModel(providerRegistry, input),
11
+ };
12
+ }
13
+ async function completeWithCurrentModel(providerRegistry, input) {
14
+ try {
15
+ const current = providerRegistry.getCurrentModel();
16
+ const provider = providerRegistry.getForModel(current.id, current.provider);
17
+ const response = await provider.chat({
18
+ model: current.id,
19
+ messages: [{ role: 'user', content: input.prompt }],
20
+ systemPrompt: input.systemPrompt,
21
+ maxTokens: input.maxTokens ?? 1800,
22
+ reasoningEffort: 'low',
23
+ });
24
+ const content = response.content?.trim() ?? '';
25
+ return content.length > 0 ? content : null;
26
+ }
27
+ catch (error) {
28
+ logger.debug('Knowledge semantic LLM request failed', {
29
+ purpose: input.purpose,
30
+ error: summarizeError(error),
31
+ });
32
+ return null;
33
+ }
34
+ }
@@ -0,0 +1,37 @@
1
+ import type { KnowledgeStore } from '../store.js';
2
+ import type { KnowledgeSourceRecord } from '../types.js';
3
+ import type { KnowledgeSemanticAnswerInput, KnowledgeSemanticAnswerResult, KnowledgeSemanticEnrichmentResult, KnowledgeSemanticLlm } from './types.js';
4
+ export interface KnowledgeSemanticServiceOptions {
5
+ readonly llm?: KnowledgeSemanticLlm | null;
6
+ }
7
+ export declare class KnowledgeSemanticService {
8
+ private readonly store;
9
+ private readonly options;
10
+ constructor(store: KnowledgeStore, options?: KnowledgeSemanticServiceOptions);
11
+ enrichSource(sourceId: string, input?: {
12
+ readonly force?: boolean;
13
+ readonly knowledgeSpaceId?: string;
14
+ }): Promise<KnowledgeSemanticEnrichmentResult | null>;
15
+ enrichSources(sources: readonly KnowledgeSourceRecord[], input?: {
16
+ readonly force?: boolean;
17
+ readonly knowledgeSpaceId?: string;
18
+ readonly limit?: number;
19
+ }): Promise<readonly KnowledgeSemanticEnrichmentResult[]>;
20
+ reindex(input?: {
21
+ readonly sourceIds?: readonly string[];
22
+ readonly limit?: number;
23
+ readonly force?: boolean;
24
+ readonly knowledgeSpaceId?: string;
25
+ }): Promise<{
26
+ readonly scanned: number;
27
+ readonly enriched: number;
28
+ readonly skipped: number;
29
+ readonly failed: number;
30
+ readonly errors: readonly {
31
+ readonly sourceId: string;
32
+ readonly error: string;
33
+ }[];
34
+ }>;
35
+ answer(input: KnowledgeSemanticAnswerInput): Promise<KnowledgeSemanticAnswerResult>;
36
+ }
37
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/semantic/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,iCAAiC,EACjC,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC5C;AAED,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,+BAAoC;IAG1D,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAO,GAC3E,OAAO,CAAC,iCAAiC,GAAG,IAAI,CAAC;IAO9C,aAAa,CACjB,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,KAAK,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GACpG,OAAO,CAAC,SAAS,iCAAiC,EAAE,CAAC;IASlD,OAAO,CAAC,KAAK,GAAE;QACnB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC/B,GAAG,OAAO,CAAC;QACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,SAAS;YAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACnF,CAAC;IAuBI,MAAM,CAAC,KAAK,EAAE,4BAA4B,GAAG,OAAO,CAAC,6BAA6B,CAAC;CAI1F"}
@@ -0,0 +1,55 @@
1
+ import { answerKnowledgeQuery } from './answer.js';
2
+ import { enrichKnowledgeSource } from './enrichment.js';
3
+ export class KnowledgeSemanticService {
4
+ store;
5
+ options;
6
+ constructor(store, options = {}) {
7
+ this.store = store;
8
+ this.options = options;
9
+ }
10
+ async enrichSource(sourceId, input = {}) {
11
+ await this.store.init();
12
+ const source = this.store.getSource(sourceId);
13
+ if (!source)
14
+ return null;
15
+ return enrichKnowledgeSource({ store: this.store, llm: this.options.llm }, source, input);
16
+ }
17
+ async enrichSources(sources, input = {}) {
18
+ const results = [];
19
+ for (const source of sources.slice(0, Math.max(1, input.limit ?? sources.length))) {
20
+ const result = await this.enrichSource(source.id, input);
21
+ if (result)
22
+ results.push(result);
23
+ }
24
+ return results;
25
+ }
26
+ async reindex(input = {}) {
27
+ await this.store.init();
28
+ const allowed = input.sourceIds?.length ? new Set(input.sourceIds) : null;
29
+ const sources = this.store.listSources(10_000)
30
+ .filter((source) => !allowed || allowed.has(source.id))
31
+ .slice(0, Math.max(1, input.limit ?? 10_000));
32
+ let enriched = 0;
33
+ let skipped = 0;
34
+ let failed = 0;
35
+ const errors = [];
36
+ for (const source of sources) {
37
+ try {
38
+ const result = await this.enrichSource(source.id, input);
39
+ if (result?.skipped)
40
+ skipped += 1;
41
+ else if (result)
42
+ enriched += 1;
43
+ }
44
+ catch (error) {
45
+ failed += 1;
46
+ errors.push({ sourceId: source.id, error: error instanceof Error ? error.message : String(error) });
47
+ }
48
+ }
49
+ return { scanned: sources.length, enriched, skipped, failed, errors };
50
+ }
51
+ async answer(input) {
52
+ await this.store.init();
53
+ return answerKnowledgeQuery({ store: this.store, llm: this.options.llm }, input);
54
+ }
55
+ }