@pellux/goodvibes-sdk 0.27.3 → 0.27.5

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