@equationalapplications/core-llm-wiki 7.4.0 → 7.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-Dmh1kfkd.mjs';
2
- export { C as ChunkFailure, D as DegradedRecord, d as DraftPage, E as EmbedFactResult, e as EmbedFailureKind, f as EmbeddingMarkerKind, g as EntityStatus, h as ExtractedFact, i as ExtractedFactEdge, j as ExtractedFactWithOntology, k as ExtractedTask, l as GraphTraversalOptions, H as HEAL_BATCH_SIZE, m as HEAL_RECHECK_MS, n as HOOK_TIMEOUT_MARKER, o as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, p as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, q as ONTOLOGY_BACKFILL_RECHECK_MS, r as OntologyBackfillResult, s as OntologyConfig, t as OntologyEdgeType, u as OntologyManifest, v as OntologyMode, w as OntologyNodeType, x as OntologyPromptContext, y as OntologyUpdates, P as PromptOverrides, z as PromptService, A as PrunePartialFailureError, B as ReembedResult, V as VectorRanker, J as VectorRankerFallback, K as VectorRankerRankArgs, N as VectorRankerSemanticResult, Q as WikiBusyError, T as WikiBusyOperation, U as WikiCheckpoint, X as WikiConfig, Y as WikiDiagnostic, Z as WikiDiagnosticCode, _ as WikiDiagnosticDetail, $ as WikiDiagnosticOperation, a0 as WikiDiagnosticSeverity, a1 as WikiDiagnosticTrigger, a2 as WikiDraftNotFound, a3 as WikiDuplicateHashError, a4 as WikiEdge, a5 as WikiEvent, a6 as WikiFact, a7 as WikiGraphNodeOwnershipConflict, a8 as WikiIngestEmptyError, a9 as WikiInvalidReadOptions, aa as WikiMemoryTestAccess, ab as WikiOutboxEvent, ac as WikiParseError, ad as WikiSourceRefHashCollision, ae as WikiStrictOntologyViolation, af as WikiTask, ag as WikiTransactionError, ah as validateManifest } from './testing-Dmh1kfkd.mjs';
1
+ import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-ClpOUiLI.mjs';
2
+ export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, H as HEAL_BATCH_SIZE, q as HEAL_RECHECK_MS, r as HOOK_TIMEOUT_MARKER, s as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, t as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, u as ONTOLOGY_BACKFILL_RECHECK_MS, v as OntologyBackfillResult, w as OntologyConfig, x as OntologyEdgeType, y as OntologyManifest, z as OntologyMode, A as OntologyNodeType, B as OntologyPromptContext, J as OntologyUpdates, P as PromptOverrides, K as PromptService, N as PrunePartialFailureError, Q as ReembedResult, V as VectorRanker, T as VectorRankerFallback, U as VectorRankerRankArgs, X as VectorRankerSemanticResult, Y as WikiBusyError, Z as WikiBusyOperation, _ as WikiCheckpoint, $ as WikiConfig, a0 as WikiDiagnostic, a1 as WikiDiagnosticCode, a2 as WikiDiagnosticDetail, a3 as WikiDiagnosticOperation, a4 as WikiDiagnosticSeverity, a5 as WikiDiagnosticTrigger, a6 as WikiDraftNotFound, a7 as WikiDuplicateHashError, a8 as WikiEdge, a9 as WikiEvent, aa as WikiFact, ab as WikiGraphNodeOwnershipConflict, ac as WikiIngestEmptyError, ad as WikiInvalidReadOptions, ae as WikiMemoryTestAccess, af as WikiOutboxEvent, ag as WikiParseError, ah as WikiSourceRefHashCollision, ai as WikiStrictOntologyViolation, aj as WikiTask, ak as WikiTransactionError, al as validateManifest } from './testing-ClpOUiLI.mjs';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-Dmh1kfkd.js';
2
- export { C as ChunkFailure, D as DegradedRecord, d as DraftPage, E as EmbedFactResult, e as EmbedFailureKind, f as EmbeddingMarkerKind, g as EntityStatus, h as ExtractedFact, i as ExtractedFactEdge, j as ExtractedFactWithOntology, k as ExtractedTask, l as GraphTraversalOptions, H as HEAL_BATCH_SIZE, m as HEAL_RECHECK_MS, n as HOOK_TIMEOUT_MARKER, o as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, p as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, q as ONTOLOGY_BACKFILL_RECHECK_MS, r as OntologyBackfillResult, s as OntologyConfig, t as OntologyEdgeType, u as OntologyManifest, v as OntologyMode, w as OntologyNodeType, x as OntologyPromptContext, y as OntologyUpdates, P as PromptOverrides, z as PromptService, A as PrunePartialFailureError, B as ReembedResult, V as VectorRanker, J as VectorRankerFallback, K as VectorRankerRankArgs, N as VectorRankerSemanticResult, Q as WikiBusyError, T as WikiBusyOperation, U as WikiCheckpoint, X as WikiConfig, Y as WikiDiagnostic, Z as WikiDiagnosticCode, _ as WikiDiagnosticDetail, $ as WikiDiagnosticOperation, a0 as WikiDiagnosticSeverity, a1 as WikiDiagnosticTrigger, a2 as WikiDraftNotFound, a3 as WikiDuplicateHashError, a4 as WikiEdge, a5 as WikiEvent, a6 as WikiFact, a7 as WikiGraphNodeOwnershipConflict, a8 as WikiIngestEmptyError, a9 as WikiInvalidReadOptions, aa as WikiMemoryTestAccess, ab as WikiOutboxEvent, ac as WikiParseError, ad as WikiSourceRefHashCollision, ae as WikiStrictOntologyViolation, af as WikiTask, ag as WikiTransactionError, ah as validateManifest } from './testing-Dmh1kfkd.js';
1
+ import { M as MemoryBundle, F as FormatContextOptions, G as GraphNeighborhood, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-ClpOUiLI.js';
2
+ export { C as ChunkFailure, d as ClassifierAnswer, e as ClassifierQuestion, f as ClassifyRequest, g as ClassifyResponse, D as DegradedRecord, h as DraftPage, E as EmbedFactResult, i as EmbedFailureKind, j as EmbeddingMarkerKind, k as EntityStatus, l as ExtractedFact, m as ExtractedFactEdge, n as ExtractedFactWithOntology, o as ExtractedTask, p as GraphTraversalOptions, H as HEAL_BATCH_SIZE, q as HEAL_RECHECK_MS, r as HOOK_TIMEOUT_MARKER, s as HealResult, I as IngestDocumentResult, L as LLMProvider, O as ONTOLOGY_BACKFILL_BATCH_SIZE, t as ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, u as ONTOLOGY_BACKFILL_RECHECK_MS, v as OntologyBackfillResult, w as OntologyConfig, x as OntologyEdgeType, y as OntologyManifest, z as OntologyMode, A as OntologyNodeType, B as OntologyPromptContext, J as OntologyUpdates, P as PromptOverrides, K as PromptService, N as PrunePartialFailureError, Q as ReembedResult, V as VectorRanker, T as VectorRankerFallback, U as VectorRankerRankArgs, X as VectorRankerSemanticResult, Y as WikiBusyError, Z as WikiBusyOperation, _ as WikiCheckpoint, $ as WikiConfig, a0 as WikiDiagnostic, a1 as WikiDiagnosticCode, a2 as WikiDiagnosticDetail, a3 as WikiDiagnosticOperation, a4 as WikiDiagnosticSeverity, a5 as WikiDiagnosticTrigger, a6 as WikiDraftNotFound, a7 as WikiDuplicateHashError, a8 as WikiEdge, a9 as WikiEvent, aa as WikiFact, ab as WikiGraphNodeOwnershipConflict, ac as WikiIngestEmptyError, ad as WikiInvalidReadOptions, ae as WikiMemoryTestAccess, af as WikiOutboxEvent, ag as WikiParseError, ah as WikiSourceRefHashCollision, ai as WikiStrictOntologyViolation, aj as WikiTask, ak as WikiTransactionError, al as validateManifest } from './testing-ClpOUiLI.js';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
package/dist/index.js CHANGED
@@ -5112,6 +5112,51 @@ var IngestionService = class {
5112
5112
  }
5113
5113
  };
5114
5114
 
5115
+ // src/utils/classifier.ts
5116
+ var isUnit = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 1;
5117
+ function validateClassifierAnswer(response, key, question) {
5118
+ try {
5119
+ if (response === null || typeof response !== "object") return { ok: false, reason: "malformed" };
5120
+ const answers = response.answers;
5121
+ if (answers === null || typeof answers !== "object") return { ok: false, reason: "missing_answer" };
5122
+ if (!Object.prototype.hasOwnProperty.call(answers, key)) return { ok: false, reason: "missing_answer" };
5123
+ const raw = answers[key];
5124
+ if (raw === null || typeof raw !== "object") return { ok: false, reason: "missing_answer" };
5125
+ const a = raw;
5126
+ if (a.kind !== question.kind) return { ok: false, reason: "kind_mismatch" };
5127
+ if (question.kind === "choice") {
5128
+ if (typeof a.choice !== "string" || !question.options.includes(a.choice)) return { ok: false, reason: "choice_not_offered" };
5129
+ if (!isUnit(a.confidence)) return { ok: false, reason: "invalid_probability" };
5130
+ const probs = a.probabilities;
5131
+ if (probs === null || typeof probs !== "object" || Array.isArray(probs)) return { ok: false, reason: "invalid_probability" };
5132
+ const probabilities = {};
5133
+ for (const [k, v] of Object.entries(probs)) {
5134
+ if (!isUnit(v)) return { ok: false, reason: "invalid_probability" };
5135
+ probabilities[k] = v;
5136
+ }
5137
+ return { ok: true, answer: { kind: "choice", choice: a.choice, confidence: a.confidence, probabilities } };
5138
+ }
5139
+ if (question.kind === "binary") {
5140
+ if (!isUnit(a.probability)) return { ok: false, reason: "invalid_probability" };
5141
+ return { ok: true, answer: { kind: "binary", probability: a.probability } };
5142
+ }
5143
+ const maxScore = question.levels.length - 1;
5144
+ if (typeof a.score !== "number" || !Number.isFinite(a.score) || a.score < 0 || a.score > maxScore) {
5145
+ return { ok: false, reason: "score_out_of_range" };
5146
+ }
5147
+ if (!isUnit(a.confidence)) return { ok: false, reason: "invalid_probability" };
5148
+ if (!Array.isArray(a.probabilities) || !a.probabilities.every(isUnit)) return { ok: false, reason: "invalid_probability" };
5149
+ return { ok: true, answer: { kind: "score", score: a.score, confidence: a.confidence, probabilities: a.probabilities.slice() } };
5150
+ } catch {
5151
+ return { ok: false, reason: "malformed" };
5152
+ }
5153
+ }
5154
+ function classifierStateForFact(fact) {
5155
+ const parts = [fact.title, fact.body];
5156
+ if (Array.isArray(fact.tags) && fact.tags.length > 0) parts.push(`Tags: ${fact.tags.join(", ")}`);
5157
+ return parts.join("\n\n");
5158
+ }
5159
+
5115
5160
  // src/services/BoundedLlmCall.ts
5116
5161
  var DEFAULT_BATCH_SIZE = 10;
5117
5162
  var ESTIMATED_OUTPUT_TOKENS_PER_ITEM = 150;
@@ -5994,7 +6039,7 @@ var MaintenanceService = class {
5994
6039
  if (!ontologyService) {
5995
6040
  return { ...zeroed, remaining: 0, deferred: 0 };
5996
6041
  }
5997
- const { mode } = await ontologyService.getEffectiveState(entityId);
6042
+ const { mode, manifest: effectiveManifest } = await ontologyService.getEffectiveState(entityId);
5998
6043
  if (mode === "off") {
5999
6044
  const counts2 = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
6000
6045
  return { ...zeroed, remaining: 0, deferred: counts2.deferred };
@@ -6004,6 +6049,14 @@ var MaintenanceService = class {
6004
6049
  const counts2 = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
6005
6050
  return { ...zeroed, remaining: counts2.eligible, deferred: counts2.deferred };
6006
6051
  }
6052
+ const classifierMode = options?.classifier ?? this.options.config?.ontology?.backfillClassifier ?? "llm";
6053
+ const classify = this.options.llmProvider.classify;
6054
+ if (classifierMode === "auto" && typeof classify === "function") {
6055
+ const slugs = effectiveManifest.node_types.map((n) => n.type);
6056
+ if (slugs.length > 0 && slugs.length <= 255) {
6057
+ return this._runClassifierBackfill(entityId, candidates, effectiveManifest, now, recheckCutoff);
6058
+ }
6059
+ }
6007
6060
  const ontologyContext = await ontologyService.buildPromptContext(entityId);
6008
6061
  const toPromptShape = (f) => ({ id: f.id, title: f.title, body: f.body, tags: f.tags });
6009
6062
  const buildPrompt = (facts) => this.promptService.buildOntologyBackfillPrompt(
@@ -6082,6 +6135,87 @@ var MaintenanceService = class {
6082
6135
  deferred: counts.deferred
6083
6136
  };
6084
6137
  }
6138
+ /**
6139
+ * Classifier-mode backfill (spec §7.3): one `choice` question per untyped
6140
+ * fact over the effective manifest's node-type slugs. Accepted answers go
6141
+ * through `_applyOntologyBackfillBatch` exactly like LLM classifications.
6142
+ * No edges are proposed.
6143
+ */
6144
+ async _runClassifierBackfill(entityId, candidates, manifest, now, recheckCutoff) {
6145
+ const provider = this.options.llmProvider;
6146
+ const rawMin = this.options.config?.ontology?.classifyMinConfidence;
6147
+ const minConfidence = typeof rawMin === "number" && Number.isFinite(rawMin) && rawMin >= 0 && rawMin <= 1 ? rawMin : 0.5;
6148
+ const rawConcurrency = this.options.config?.chunkConcurrency ?? 1;
6149
+ const concurrency = Number.isFinite(rawConcurrency) && rawConcurrency >= 1 ? Math.floor(rawConcurrency) : 1;
6150
+ const question = {
6151
+ kind: "choice",
6152
+ options: manifest.node_types.map((n) => n.type),
6153
+ instructions: "Choose the ontology node type that best describes this fact.\n" + manifest.node_types.map((n) => `- ${n.type}: ${n.description}`).join("\n")
6154
+ };
6155
+ const outcomes = await withConcurrency(
6156
+ candidates.map((fact) => async () => {
6157
+ let response;
6158
+ try {
6159
+ response = await provider.classify({ state: classifierStateForFact(fact), questions: { okf_type: question } });
6160
+ } catch {
6161
+ return { fact, kind: "threw" };
6162
+ }
6163
+ const checked = validateClassifierAnswer(response, "okf_type", question);
6164
+ if (!checked.ok) return { fact, kind: "invalid", reason: checked.reason };
6165
+ if (checked.answer.kind !== "choice") return { fact, kind: "invalid", reason: "kind_mismatch" };
6166
+ if (checked.answer.confidence < minConfidence) return { fact, kind: "low_confidence" };
6167
+ return { fact, kind: "accepted", okfType: checked.answer.choice };
6168
+ }),
6169
+ concurrency
6170
+ );
6171
+ const attempted = outcomes.filter((o) => o.kind !== "threw").map((o) => o.fact);
6172
+ const skipped = outcomes.length - attempted.length;
6173
+ const classifications = outcomes.filter((o) => o.kind === "accepted").map((o) => ({ id: o.fact.id, okf_type: o.okfType }));
6174
+ const invalidCount = outcomes.filter((o) => o.kind === "invalid").length;
6175
+ let typed = 0;
6176
+ let failedValidation = 0;
6177
+ let edgesAdded = 0;
6178
+ let aborted = false;
6179
+ if (attempted.length > 0) {
6180
+ const applied = await this._applyOntologyBackfillBatch(
6181
+ entityId,
6182
+ { batch: attempted, classifications, ontologyUpdates: void 0 },
6183
+ now
6184
+ );
6185
+ aborted = applied.abortedOntologyOff;
6186
+ if (!aborted) {
6187
+ typed = applied.typed;
6188
+ failedValidation = invalidCount + applied.failedValidation;
6189
+ edgesAdded = applied.edgesAdded;
6190
+ }
6191
+ }
6192
+ const diagBuffer = new DiagnosticBuffer();
6193
+ const diagBase = { entityId, operation: "ontologyBackfill", trigger: "call" };
6194
+ for (const o of outcomes) {
6195
+ if (o.kind === "low_confidence") {
6196
+ diagBuffer.push({ ...diagBase, code: "classification_low_confidence", detail: { factId: o.fact.id, reason: "below_threshold" } });
6197
+ } else if (o.kind === "invalid") {
6198
+ diagBuffer.push({ ...diagBase, code: "classification_invalid", detail: { factId: o.fact.id, reason: o.reason } });
6199
+ } else if (o.kind === "threw") {
6200
+ diagBuffer.push({ ...diagBase, code: "classification_invalid", detail: { factId: o.fact.id, reason: "classify_threw" } });
6201
+ }
6202
+ }
6203
+ diagBuffer.flush(this.options);
6204
+ const counts = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
6205
+ if (aborted) {
6206
+ return { scanned: 0, typed: 0, failedValidation: 0, edgesAdded: 0, skipped, remaining: 0, deferred: counts.deferred };
6207
+ }
6208
+ this.searchService.evictCache(entityId);
6209
+ return {
6210
+ scanned: candidates.length,
6211
+ typed,
6212
+ failedValidation,
6213
+ edgesAdded,
6214
+ skipped,
6215
+ remaining: counts.eligible,
6216
+ deferred: counts.deferred
6217
+ };
6218
+ }
6085
6219
  /**
6086
6220
  * Applies one parsed backfill batch in its own transaction. Per-batch rather
6087
6221
  * than one transaction for the pass, so mergeEmergentUpdates semantics and
@@ -6785,8 +6919,7 @@ var EmbeddingService = class {
6785
6919
  }
6786
6920
  }
6787
6921
  async tryEmbedFact(fact, ctx) {
6788
- const embedFn = this.options.llmProvider.embed;
6789
- if (typeof embedFn !== "function") return { ok: false, kind: "no_provider" };
6922
+ if (typeof this.options.llmProvider.embed !== "function") return { ok: false, kind: "no_provider" };
6790
6923
  let tagsStr;
6791
6924
  if (Array.isArray(fact.tags)) {
6792
6925
  tagsStr = fact.tags.join(" ");
@@ -6803,7 +6936,7 @@ var EmbeddingService = class {
6803
6936
  const text = clip(`${fact.title} ${fact.body} ${tagsStr}`.trim(), maxEmbedChars);
6804
6937
  let float32Vector;
6805
6938
  try {
6806
- const vector = await embedFn(text);
6939
+ const vector = await this.options.llmProvider.embed(text);
6807
6940
  if (vector.length === 0 || !vector.every((v) => typeof v === "number" && isFinite(v))) {
6808
6941
  console.warn(`[WikiMemory] embedFact: embed() returned an invalid vector for ${fact.id}; skipping.`);
6809
6942
  this.reportEmbed(ctx, fact, "embedding_failed", "invalid_vector");
@@ -7062,7 +7195,6 @@ var RetrievalService = class {
7062
7195
  const hybridWeight = options?.hybridWeight ?? config?.hybridWeight;
7063
7196
  const weight = hybridWeight !== void 0 && !Number.isNaN(hybridWeight) ? Math.max(0, Math.min(1, hybridWeight)) : void 0;
7064
7197
  const skipEmbed = weight === 0;
7065
- const embedFn = this.options.llmProvider.embed;
7066
7198
  let facts = [];
7067
7199
  let scoreByFactId;
7068
7200
  if (maxResults === 0) ; else if (trimmedQuery) {
@@ -7073,11 +7205,11 @@ var RetrievalService = class {
7073
7205
  const padLimit = (n) => n >= Number.MAX_SAFE_INTEGER ? n : n + draftPad;
7074
7206
  if (scoredEntityIds.length === 0) {
7075
7207
  usedEmbed = true;
7076
- } else if (!skipEmbed && embedFn) {
7208
+ } else if (!skipEmbed && typeof this.options.llmProvider.embed === "function") {
7077
7209
  let rankerShouldRethrow = false;
7078
7210
  let pendingRankerFallbackError;
7079
7211
  try {
7080
- const queryVec = await embedFn(trimmedQuery);
7212
+ const queryVec = await this.options.llmProvider.embed(trimmedQuery);
7081
7213
  if (queryVec.length === 0 || !queryVec.every((v) => typeof v === "number" && isFinite(v))) {
7082
7214
  throw new Error(
7083
7215
  "embed() returned an empty or non-finite vector. Falling back to keyword search."