@littlebigbrain/client 0.10.0 → 0.11.1

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/README.md CHANGED
@@ -36,11 +36,10 @@ await graph.facts.create(
36
36
  const published = await lbb.readSnapshot();
37
37
  console.log(published.snapshot.served_at_seq, published.query_lag_commits);
38
38
 
39
- // 3. Hybrid search over the snapshot.
40
- const results = await graph.search.hybrid(
41
- "How long are customer records retained?",
42
- { topK: 10, consistency: "eventual" },
43
- );
39
+ // 3. Query the snapshot with SPARQL.
40
+ const rows = await lbb.sparqlRows({
41
+ query: "SELECT ?s ?o WHERE { ?s <policy:retention> ?o } LIMIT 10",
42
+ });
44
43
  ```
45
44
 
46
45
  For hosted use, `baseUrl` is required and must be the exact `endpoint_url`
@@ -49,23 +48,6 @@ parameters; they are not encoded in the hostname.
49
48
 
50
49
  ## Examples
51
50
 
52
- **Search with filters.** Pass the request body to filter before ranking — here, only facts an ACL principal may see:
53
-
54
- ```ts
55
- const results = await graph.search.hybrid({
56
- query: "incident response runbook",
57
- targets: ["entities"],
58
- search: {
59
- filters: {
60
- op: "overlaps",
61
- field: "acl",
62
- values: ["user:rino@example.com", "group:engineering"],
63
- },
64
- },
65
- top_k: 20,
66
- });
67
- ```
68
-
69
51
  **Bulk import.** Load an array of records (or an NDJSON string) in one call:
70
52
 
71
53
  ```ts
@@ -86,17 +68,26 @@ const accepted = await lbb.submitImport(records(), {
86
68
  });
87
69
  const completed = await lbb.waitForImportJob(accepted.job_id);
88
70
  console.log(completed.state, completed.committed_commit_seq);
71
+ if (completed.committed_commit_seq != null) {
72
+ await lbb.waitForIndexLineage(completed.committed_commit_seq);
73
+ }
89
74
  ```
90
75
 
91
76
  `records()` may be an iterable or async iterable. Success means every grouped
92
77
  commit is durable and final publication was enqueued; it does not mean indexes
93
- have already reached `committed_commit_seq`. An empty iterable is rejected
94
- locally before an import POST is sent.
78
+ have already reached `committed_commit_seq`. Wait once after the final commit,
79
+ not after each source row or chunk. The lineage waiter polls normal
80
+ `index_caught_up=false` metadata until its own deadline, including on an
81
+ RDF-only deployment. An empty iterable is rejected locally before an import
82
+ POST is sent.
95
83
 
96
- **Time-travel read.** Pin any search to a past instant — results reflect the graph as it was then:
84
+ **Time-travel read.** Pin a SPARQL read to a past instant — results reflect the graph as it was then:
97
85
 
98
86
  ```ts
99
- const asOf = await graph.search.hybrid("retention policy", { asOf: "2026-01-01T00:00:00Z" });
87
+ const asOf = await lbb.sparqlRows({
88
+ query: "SELECT ?s ?o WHERE { ?s <policy:retention> ?o }",
89
+ as_of_valid_time: "2026-01-01T00:00:00Z",
90
+ });
100
91
  ```
101
92
 
102
93
  **SPARQL.** `sparqlRows` runs a SPARQL 1.1 SELECT/ASK and returns parsed rows:
@@ -110,12 +101,14 @@ const { rows } = await lbb.sparqlRows({
110
101
  ## Errors & retries
111
102
 
112
103
  Methods return parsed JSON and throw `LbbError` (with `status`, `code`, `message`, `param`, `requestId`, `docUrl`) on any non-2xx response. Safe reads and idempotency-keyed writes retry `429`/`5xx` and network failures with full-jitter backoff, bounded by a retry budget (`retryBudgetMs`, default 60s) rather than a fixed count, and honor `Retry-After` — a terminal error the server marks non-retryable surfaces immediately. Use `rawRequest()` for response headers, request id, and retry/timing metadata.
104
+ `waitForIndexLineage(...)` is a separate deadline-bounded poller, so the generic
105
+ request retry-count cap cannot end publication waiting early.
113
106
 
114
107
  ## More
115
108
 
116
- The `graph(...)` scope exposes `facts`, `search`, `entities`, `ontology`,
117
- `query`, `schema`, and `context` namespaces. `context` includes vocabulary
118
- suggestion, term resolution, relation decoding, and groundability inspection; `schema`
109
+ The `graph(...)` scope exposes `facts`, `entities`, `ontology`, `query`,
110
+ `search` (feedback surfaces), and `schema` namespaces. `query` runs SPARQL,
111
+ the one query language on the API; `schema`
119
112
  reads or atomically publishes the active ontology/shapes bundle. Writes enqueue
120
113
  published-generation maintenance automatically. Every generated shape is
121
114
  available as `Schemas["TypeName"]`.
package/dist/client.d.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import type { DurableImportSource, ImportLine, LbbClientOptions, ListResponse, RawLbbResponse, ReadConsistencyOptions, RdfImportOptions, Schemas, SearchConsistency, SparqlResults } from "./types.js";
2
2
  import { type CallOptions, type RequestOptions } from "./transport.js";
3
- import { ContextNamespace, EntityNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace } from "./namespaces.js";
3
+ import { EntityNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace } from "./namespaces.js";
4
4
  export { parseSparqlResults } from "./types.js";
5
- export type { AttributeFilter, AttributeFilterOp, AttributeFilterValue, EntityAttributeFilterOptions, EntityPropertiesLine, DurableImportLine, DurableImportSource, FetchLike, FlatProperties, ImportLine, LbbClientOptions, LbbRequestEvent, LbbResponseEvent, LbbRetryEvent, LbbErrorPayload, ListResponse, RawLbbResponse, ReadConsistencyOptions, RdfImportOptions, Schemas, SearchConsistency, SparqlResults, SparqlResultsJson, SparqlTerm, CommitRequest, CommitResponse, Entity, EntitySelector, GraphMetadata, GraphSummary, SchemaView, SearchRequest, SearchResponse, SearchResult, Snapshot, } from "./types.js";
5
+ export type { AttributeFilter, AttributeFilterOp, AttributeFilterValue, EntityAttributeFilterOptions, EntityPropertiesLine, DurableImportLine, DurableImportSource, FetchLike, FlatProperties, ImportLine, LbbClientOptions, LbbRequestEvent, LbbResponseEvent, LbbRetryEvent, LbbErrorPayload, ListResponse, RawLbbResponse, ReadConsistencyOptions, RdfImportOptions, Schemas, SearchConsistency, SparqlResults, SparqlResultsJson, SparqlTerm, CommitRequest, CommitResponse, Entity, EntitySelector, GraphMetadata, GraphSummary, SchemaView, Snapshot, } from "./types.js";
6
6
  export { LbbCapabilityError, LbbError } from "./transport.js";
7
7
  export type { CallOptions, Query, QueryValue, RequestOptions, } from "./transport.js";
8
- export type { HybridSearchOptions } from "./namespaces.js";
9
- export { ContextNamespace, EntityNamespace, FactsNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace, } from "./namespaces.js";
8
+ export { EntityNamespace, FactsNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace, } from "./namespaces.js";
10
9
  export interface IndexLineageObservation {
11
10
  metadata: Schemas["GraphMetadataResponse"];
12
11
  lineage: Schemas["IndexLineage"];
@@ -39,7 +38,6 @@ export declare class LbbClient {
39
38
  private capabilities?;
40
39
  /** A5 default read consistency applied when a read omits its own value. */
41
40
  readonly defaultConsistency?: SearchConsistency;
42
- readonly context: ContextNamespace;
43
41
  readonly search: SearchNamespace;
44
42
  readonly entities: EntityNamespace;
45
43
  readonly schema: SchemaNamespace;
@@ -148,7 +146,11 @@ export declare class LbbClient {
148
146
  retract(body: Schemas["GraphRetractRequest"], opts?: {
149
147
  idempotencyKey?: string;
150
148
  }): Promise<Schemas["GraphRetractResponse"]>;
151
- /** Create the scoped graph/branch. Construct the client with the desired graph/branch first. */
149
+ /**
150
+ * Create the scoped graph/branch with an empty ontology. Construct the client
151
+ * with the desired graph/branch first, then call `ontology.define` before
152
+ * writing typed data.
153
+ */
152
154
  createGraph(): Promise<Schemas["CreateGraphResponse"]>;
153
155
  /**
154
156
  * Fork a whole graph into a brand-new destination graph in the same tenant.
@@ -213,47 +215,6 @@ export declare class LbbClient {
213
215
  deleteBranch(opts: {
214
216
  confirm: string;
215
217
  }): Promise<Schemas["GraphBranchDeleteResponse"]>;
216
- embeddingConfig(): Promise<Schemas["ManagedEmbeddingConfigResponse"]>;
217
- /** List the embedding models available on this deployment. */
218
- embeddingModels(): Promise<Schemas["ManagedEmbeddingModelsResponse"]>;
219
- /**
220
- * Choose the model used automatically for writes and vector queries.
221
- * Provider credentials and native dimension discovery stay server-side.
222
- */
223
- setEmbeddingModel(modelId: string, opts?: {
224
- autoEmbedQuery?: boolean;
225
- }): Promise<Schemas["ManagedEmbeddingConfigResponse"]>;
226
- /** Advanced configuration escape hatch. Prefer `setEmbeddingModel`. */
227
- setEmbeddingConfig(body: Schemas["ManagedEmbeddingConfigRequest"]): Promise<Schemas["ManagedEmbeddingConfigResponse"]>;
228
- submitEmbeddingBackfill(opts?: {
229
- batchSize?: number;
230
- limit?: number;
231
- full?: boolean;
232
- idempotencyKey?: string;
233
- }): Promise<Schemas["ManagedEmbeddingBackfillJobStatusResponse"]>;
234
- embeddingBackfillJob(jobId: string): Promise<Schemas["ManagedEmbeddingBackfillJobStatusResponse"]>;
235
- cancelEmbeddingBackfill(jobId: string): Promise<Schemas["ManagedEmbeddingBackfillJobStatusResponse"]>;
236
- backfillEmbeddings(opts?: {
237
- batchSize?: number;
238
- limit?: number;
239
- full?: boolean;
240
- idempotencyKey?: string;
241
- timeoutMs?: number;
242
- pollIntervalMs?: number;
243
- }): Promise<Schemas["ManagedEmbeddingBackfillResponse"]>;
244
- promoteEmbedding(opts: {
245
- runId: string;
246
- allowRegression?: boolean;
247
- }): Promise<Schemas["ManagedEmbeddingPromoteResponse"]>;
248
- /**
249
- * The graph's grounding vocabulary as byte-sorted, deduped string sections —
250
- * the canonical input for a decoder-side automaton (FST/trie) and the
251
- * vocabulary half of an export bundle.
252
- */
253
- vocabExport(opts?: {
254
- sections?: string[];
255
- limit?: number;
256
- }): Promise<Schemas["VocabExportResponse"]>;
257
218
  /**
258
219
  * Captured signals by flush-seq range, oldest first — the model-training
259
220
  * feed. The `seq` on each signal is the temporal-split coordinate (train ≤ T,
@@ -366,29 +327,11 @@ export declare class LbbClient {
366
327
  runId: string;
367
328
  allowRegression?: boolean;
368
329
  }): Promise<unknown>;
369
- /** Full semantic hybrid search from a request body (`POST /v1/graph/search`). */
370
- graphSearch(body: Schemas["SemanticGraphSearchRequest"], opts?: ReadConsistencyOptions): Promise<Schemas["SemanticGraphSearchResponse"]>;
371
- /** Reciprocal-rank-fusion across sub-queries. */
372
- multiSearch(body: Schemas["HybridMultiSearchRequest"]): Promise<Schemas["HybridMultiSearchResponse"]>;
373
330
  /**
374
- * Grounded prefix completion from the index vocabulary + ontology. Optionally
375
- * narrow relation completions by a type-signature `context` — a type
376
- * pair that admits a single relation flags `signature_forced`.
377
- */
378
- suggest(body: Schemas["SearchSuggestRequest"]): Promise<Schemas["SearchSuggestResponse"]>;
379
- /** Snap free text to the nearest term in the pinned published vocabulary. */
380
- resolveTerm(body: Schemas["ResolveTermRequest"]): Promise<Schemas["ResolveTermResponse"]>;
381
- /** Decode a relation from the graph's admissible published vocabulary. */
382
- decode(body: Schemas["DecodeRequest"]): Promise<Schemas["DecodeResponse"]>;
383
- /** Report completion strategy fitness for the pinned published graph. */
384
- groundability(opts?: {
385
- sample?: number;
386
- }): Promise<Schemas["GroundabilityReport"]>;
387
- /**
388
- * Append relevance labels for a set of search results — how little big brain
331
+ * Append relevance labels for a set of ranked results — how little big brain
389
332
  * gathers customer-specific qrels. Grade results (3 ideal/good, 1 partial,
390
- * 0 bad), referencing the search response's `search_id` so labels tie back to
391
- * that ranking. Stored apart from customer facts and exported via
333
+ * 0 bad), referencing the ranking's `search_id` so labels tie back to it.
334
+ * Stored apart from customer facts and exported via
392
335
  * {@link searchFeedbackExport} as training/eval data for embedding fine-tuning.
393
336
  */
394
337
  searchFeedback(body: Schemas["SearchFeedbackRequest"], opts?: {
@@ -396,10 +339,6 @@ export declare class LbbClient {
396
339
  }): Promise<Schemas["SearchFeedbackResponse"]>;
397
340
  /** Export the stored relevance labels as qrels-style rows for training. */
398
341
  searchFeedbackExport(): Promise<Schemas["SearchFeedbackExportResponse"]>;
399
- /** BM25 search. */
400
- fullTextSearch(body: Schemas["FullTextSearchRequest"], opts?: ReadConsistencyOptions): Promise<Schemas["FullTextSearchResponse"]>;
401
- /** ANN/vector search. */
402
- embeddingSearch(body: Schemas["EmbeddingSearchRequest"], opts?: ReadConsistencyOptions): Promise<Schemas["EmbeddingSearchResponse"]>;
403
342
  /**
404
343
  * Ranked incoming/outgoing neighborhood for a graph entity.
405
344
  *
@@ -476,14 +415,6 @@ export declare class LbbClient {
476
415
  * is the ASK answer (or `null` for a SELECT).
477
416
  */
478
417
  sparqlRows(body: Schemas["SparqlTextRequest"], opts?: ReadConsistencyOptions): Promise<SparqlResults>;
479
- /**
480
- * Basic-graph-pattern query with group-graph-pattern combinators
481
- * (UNION / OPTIONAL / MINUS / EXISTS / NOT EXISTS) folded over the base
482
- * patterns. The complement to {@link sparql}: this route carries the
483
- * combinators (but not FILTER/aggregation), so use it when a query needs an
484
- * optional/union/negated leg rather than a grouped aggregate.
485
- */
486
- analytics(body: Schemas["AnalyticQueryRequest"]): Promise<Schemas["AnalyticQueryResponse"]>;
487
418
  /**
488
419
  * The active ontology (entity types and relations) for the scoped graph.
489
420
  * Pass `{ counts: true }` to include a per-relation current-edge count
@@ -505,7 +436,17 @@ export declare class LbbClient {
505
436
  ontologySearch(body: Schemas["OntologySearchRequest"]): Promise<Schemas["OntologySearchResponse"]>;
506
437
  /** Resolve mentions to concepts/entities. */
507
438
  ontologyResolve(body: Schemas["OntologyResolveRequest"]): Promise<Schemas["OntologyResolveResponse"]>;
508
- /** Define the active ontology before the scoped graph's first commit. */
439
+ /**
440
+ * Put the scoped graph on an imported ontology, creating the graph when it
441
+ * does not exist yet. Safe to repeat: an unchanged ontology answers
442
+ * `changed: false` without writing. An additive difference is applied,
443
+ * including a wider relation domain or range and a new property field, and
444
+ * `changed` reports what was written. A document that narrows or drops what
445
+ * the graph already defines, or states a change no additive operation
446
+ * expresses, is refused with `ontology_restrictive_change`,
447
+ * `ontology_identity_breaking_change`, or `ontology_unsupported_change`, and
448
+ * writes nothing.
449
+ */
509
450
  ontologyDefine(body: Schemas["OntologyDefineRequest"]): Promise<Schemas["OntologyDefineResponse"]>;
510
451
  /**
511
452
  * Additively evolve the active ontology of an existing graph: widen relation
@@ -527,6 +468,13 @@ export declare class LbbClient {
527
468
  metadata(opts?: {
528
469
  includeIndexes?: boolean;
529
470
  }): Promise<Schemas["GraphMetadataResponse"]>;
471
+ /**
472
+ * Wait until one published generation covers `targetSeq`.
473
+ *
474
+ * The returned lineage may name absent BM25/ANN families on an RDF-only
475
+ * deployment; `metadata.index_caught_up` is the generation-level readiness
476
+ * signal used by bulk loaders.
477
+ */
530
478
  waitForIndexLineage(targetSeq: number, opts?: {
531
479
  timeoutMs?: number;
532
480
  pollIntervalMs?: number;
package/dist/client.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { parseSparqlResults } from "./types.js";
2
2
  import { bodyMarksTerminal, errorCodeFromBody, fullJitterBackoffMs, parseLbbError, parseResponseJson, retryAllowed, retryableStatus, retryDelayMs, sleep, } from "./transport.js";
3
- import { LbbCapabilityError } from "./transport.js";
4
- import { ContextNamespace, EntityNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace, } from "./namespaces.js";
3
+ import { LbbCapabilityError, LbbError } from "./transport.js";
4
+ import { EntityNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace, } from "./namespaces.js";
5
5
  export { parseSparqlResults } from "./types.js";
6
6
  export { LbbCapabilityError, LbbError } from "./transport.js";
7
- export { ContextNamespace, EntityNamespace, FactsNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace, } from "./namespaces.js";
7
+ export { EntityNamespace, FactsNamespace, GraphNamespace, OntologyNamespace, QueryNamespace, SchemaNamespace, SearchNamespace, } from "./namespaces.js";
8
8
  function durableImportBytes(line) {
9
9
  if (line instanceof Uint8Array)
10
10
  return line;
@@ -105,7 +105,6 @@ export class LbbClient {
105
105
  capabilities;
106
106
  /** A5 default read consistency applied when a read omits its own value. */
107
107
  defaultConsistency;
108
- context;
109
108
  search;
110
109
  entities;
111
110
  schema;
@@ -148,7 +147,6 @@ export class LbbClient {
148
147
  throw new Error("no fetch implementation available; pass options.fetch");
149
148
  }
150
149
  this.fetchImpl = chosen;
151
- this.context = new ContextNamespace(this);
152
150
  this.search = new SearchNamespace(this);
153
151
  this.entities = new EntityNamespace(this);
154
152
  this.schema = new SchemaNamespace(this);
@@ -577,7 +575,11 @@ export class LbbClient {
577
575
  idempotencyKey: opts.idempotencyKey ?? this.idempotencyKey("retract"),
578
576
  });
579
577
  }
580
- /** Create the scoped graph/branch. Construct the client with the desired graph/branch first. */
578
+ /**
579
+ * Create the scoped graph/branch with an empty ontology. Construct the client
580
+ * with the desired graph/branch first, then call `ontology.define` before
581
+ * writing typed data.
582
+ */
581
583
  createGraph() {
582
584
  return this.request("POST", "/v1/graph/create");
583
585
  }
@@ -668,78 +670,7 @@ export class LbbClient {
668
670
  query: { confirm: opts.confirm },
669
671
  });
670
672
  }
671
- embeddingConfig() {
672
- return this.request("GET", "/v1/graph/embedding");
673
- }
674
- /** List the embedding models available on this deployment. */
675
- embeddingModels() {
676
- return this.request("GET", "/v1/graph/embedding/models");
677
- }
678
- /**
679
- * Choose the model used automatically for writes and vector queries.
680
- * Provider credentials and native dimension discovery stay server-side.
681
- */
682
- setEmbeddingModel(modelId, opts = {}) {
683
- return this.setEmbeddingConfig({
684
- model_id: modelId,
685
- service: "open_router",
686
- auto_embed_query: opts.autoEmbedQuery ?? true,
687
- });
688
- }
689
- /** Advanced configuration escape hatch. Prefer `setEmbeddingModel`. */
690
- setEmbeddingConfig(body) {
691
- return this.request("POST", "/v1/graph/embedding", { body });
692
- }
693
- submitEmbeddingBackfill(opts = {}) {
694
- return this.request("POST", "/v1/graph/embedding/backfill-jobs", {
695
- body: {
696
- batch_size: opts.batchSize,
697
- limit: opts.limit,
698
- full: opts.full ?? false,
699
- },
700
- idempotencyKey: opts.idempotencyKey ?? this.idempotencyKey("embedding-backfill"),
701
- });
702
- }
703
- embeddingBackfillJob(jobId) {
704
- return this.request("GET", "/v1/graph/embedding/backfill-jobs", {
705
- query: { job_id: jobId },
706
- });
707
- }
708
- cancelEmbeddingBackfill(jobId) {
709
- return this.request("DELETE", "/v1/graph/embedding/backfill-jobs", {
710
- query: { job_id: jobId },
711
- });
712
- }
713
- async backfillEmbeddings(opts = {}) {
714
- let status = await this.submitEmbeddingBackfill(opts);
715
- const deadline = Date.now() + (opts.timeoutMs ?? 30 * 60_000);
716
- while (status.status === "pending" || status.status === "running") {
717
- if (Date.now() >= deadline)
718
- throw new Error(`embedding backfill ${status.job_id} did not finish before timeout`);
719
- await sleep(opts.pollIntervalMs ?? 2_000);
720
- status = await this.embeddingBackfillJob(status.job_id);
721
- }
722
- if (status.status !== "succeeded" || status.result == null)
723
- throw new Error(status.terminal_error ??
724
- `embedding backfill ${status.job_id} ended ${status.status}`);
725
- return status.result;
726
- }
727
- promoteEmbedding(opts) {
728
- return this.request("POST", "/v1/graph/embedding/promote", {
729
- query: { run_id: opts.runId, allow_regression: opts.allowRegression },
730
- });
731
- }
732
673
  // --- models as runs (training-run registry + eval machinery) ---
733
- /**
734
- * The graph's grounding vocabulary as byte-sorted, deduped string sections —
735
- * the canonical input for a decoder-side automaton (FST/trie) and the
736
- * vocabulary half of an export bundle.
737
- */
738
- vocabExport(opts = {}) {
739
- return this.request("GET", "/v1/search/vocab", {
740
- query: { sections: opts.sections?.join(","), limit: opts.limit },
741
- });
742
- }
743
674
  /**
744
675
  * Captured signals by flush-seq range, oldest first — the model-training
745
676
  * feed. The `seq` on each signal is the temporal-split coordinate (train ≤ T,
@@ -887,49 +818,12 @@ export class LbbClient {
887
818
  query: { run_id: opts.runId, allow_regression: opts.allowRegression },
888
819
  });
889
820
  }
890
- // --- search ---
891
- /** Full semantic hybrid search from a request body (`POST /v1/graph/search`). */
892
- graphSearch(body, opts) {
893
- // Consistency for hybrid graph search lives on the nested `search` options.
894
- const consistency = this.resolveConsistency(opts);
895
- const search = consistency !== undefined || opts?.minIndexedSeq !== undefined
896
- ? this.mergeReadConsistency(body.search ?? {}, opts)
897
- : body.search;
898
- return this.request("POST", "/v1/graph/search", {
899
- body: { ...body, search },
900
- });
901
- }
902
- /** Reciprocal-rank-fusion across sub-queries. */
903
- multiSearch(body) {
904
- return this.request("POST", "/v1/search/multi", { body });
905
- }
821
+ // --- relevance feedback ---
906
822
  /**
907
- * Grounded prefix completion from the index vocabulary + ontology. Optionally
908
- * narrow relation completions by a type-signature `context` — a type
909
- * pair that admits a single relation flags `signature_forced`.
910
- */
911
- suggest(body) {
912
- return this.request("POST", "/v1/search/suggest", { body });
913
- }
914
- /** Snap free text to the nearest term in the pinned published vocabulary. */
915
- resolveTerm(body) {
916
- return this.request("POST", "/v1/search/resolve-term", { body });
917
- }
918
- /** Decode a relation from the graph's admissible published vocabulary. */
919
- decode(body) {
920
- return this.request("POST", "/v1/decode", { body });
921
- }
922
- /** Report completion strategy fitness for the pinned published graph. */
923
- groundability(opts = {}) {
924
- return this.request("GET", "/v1/graph/groundability", {
925
- query: opts.sample == null ? undefined : { sample: opts.sample },
926
- });
927
- }
928
- /**
929
- * Append relevance labels for a set of search results — how little big brain
823
+ * Append relevance labels for a set of ranked results — how little big brain
930
824
  * gathers customer-specific qrels. Grade results (3 ideal/good, 1 partial,
931
- * 0 bad), referencing the search response's `search_id` so labels tie back to
932
- * that ranking. Stored apart from customer facts and exported via
825
+ * 0 bad), referencing the ranking's `search_id` so labels tie back to it.
826
+ * Stored apart from customer facts and exported via
933
827
  * {@link searchFeedbackExport} as training/eval data for embedding fine-tuning.
934
828
  */
935
829
  searchFeedback(body, opts = {}) {
@@ -942,18 +836,6 @@ export class LbbClient {
942
836
  searchFeedbackExport() {
943
837
  return this.request("GET", "/v1/search/feedback/export");
944
838
  }
945
- /** BM25 search. */
946
- fullTextSearch(body, opts) {
947
- return this.request("POST", "/v1/search/full-text", {
948
- body: this.mergeReadConsistency(body, opts),
949
- });
950
- }
951
- /** ANN/vector search. */
952
- embeddingSearch(body, opts) {
953
- return this.request("POST", "/v1/search/embedding", {
954
- body: this.mergeReadConsistency(body, opts),
955
- });
956
- }
957
839
  /**
958
840
  * Ranked incoming/outgoing neighborhood for a graph entity.
959
841
  *
@@ -1075,16 +957,6 @@ export class LbbClient {
1075
957
  async sparqlRows(body, opts) {
1076
958
  return parseSparqlResults(await this.sparqlText(body, opts));
1077
959
  }
1078
- /**
1079
- * Basic-graph-pattern query with group-graph-pattern combinators
1080
- * (UNION / OPTIONAL / MINUS / EXISTS / NOT EXISTS) folded over the base
1081
- * patterns. The complement to {@link sparql}: this route carries the
1082
- * combinators (but not FILTER/aggregation), so use it when a query needs an
1083
- * optional/union/negated leg rather than a grouped aggregate.
1084
- */
1085
- analytics(body) {
1086
- return this.request("POST", "/v1/query/analytics", { body });
1087
- }
1088
960
  // --- ontology ---
1089
961
  /**
1090
962
  * The active ontology (entity types and relations) for the scoped graph.
@@ -1117,7 +989,17 @@ export class LbbClient {
1117
989
  ontologyResolve(body) {
1118
990
  return this.request("POST", "/v1/ontology/resolve", { body });
1119
991
  }
1120
- /** Define the active ontology before the scoped graph's first commit. */
992
+ /**
993
+ * Put the scoped graph on an imported ontology, creating the graph when it
994
+ * does not exist yet. Safe to repeat: an unchanged ontology answers
995
+ * `changed: false` without writing. An additive difference is applied,
996
+ * including a wider relation domain or range and a new property field, and
997
+ * `changed` reports what was written. A document that narrows or drops what
998
+ * the graph already defines, or states a change no additive operation
999
+ * expresses, is refused with `ontology_restrictive_change`,
1000
+ * `ontology_identity_breaking_change`, or `ontology_unsupported_change`, and
1001
+ * writes nothing.
1002
+ */
1121
1003
  ontologyDefine(body) {
1122
1004
  return this.request("POST", "/v1/ontology/define", { body });
1123
1005
  }
@@ -1156,17 +1038,52 @@ export class LbbClient {
1156
1038
  },
1157
1039
  });
1158
1040
  }
1041
+ /**
1042
+ * Wait until one published generation covers `targetSeq`.
1043
+ *
1044
+ * The returned lineage may name absent BM25/ANN families on an RDF-only
1045
+ * deployment; `metadata.index_caught_up` is the generation-level readiness
1046
+ * signal used by bulk loaders.
1047
+ */
1159
1048
  async waitForIndexLineage(targetSeq, opts = {}) {
1160
1049
  const deadline = Date.now() + (opts.timeoutMs ?? 30_000);
1161
1050
  let last;
1162
1051
  while (true) {
1163
- last = await this.rawRequest("GET", "/v1/graph/metadata");
1052
+ try {
1053
+ // This method is already an explicit, deadline-bounded poller. Avoid
1054
+ // nesting the generic request retry count inside it: publication may
1055
+ // legitimately take longer than that secondary cap.
1056
+ last = await this.rawRequest("GET", "/v1/graph/metadata", {
1057
+ maxRetries: 0,
1058
+ });
1059
+ }
1060
+ catch (error) {
1061
+ const retryableHttpError = error instanceof LbbError &&
1062
+ retryableStatus(error.status) &&
1063
+ error.retryable !== false;
1064
+ const retryableTransportError = error instanceof Error && !(error instanceof LbbError);
1065
+ if (!retryableHttpError && !retryableTransportError)
1066
+ throw error;
1067
+ const now = Date.now();
1068
+ if (now >= deadline) {
1069
+ throw new Error(`index lineage did not reach ${targetSeq} before timeout (last_error=${error.message})`, { cause: error });
1070
+ }
1071
+ const retryAfterMs = error instanceof LbbError
1072
+ ? (error.retryAfterSeconds ?? 0) * 1_000
1073
+ : 0;
1074
+ await sleep(Math.min(Math.max(opts.pollIntervalMs ?? 250, retryAfterMs), deadline - now));
1075
+ continue;
1076
+ }
1164
1077
  const lineage = last.data.index_lineage;
1078
+ const servedAt = last.data.snapshot.served_at_seq;
1165
1079
  if (lineage != null &&
1166
- lineage.bm25_indexed_commit_seq != null &&
1167
- lineage.bm25_indexed_commit_seq >= targetSeq &&
1168
- lineage.ann_indexed_commit_seq != null &&
1169
- lineage.ann_indexed_commit_seq >= targetSeq) {
1080
+ servedAt != null &&
1081
+ servedAt >= targetSeq &&
1082
+ (last.data.index_caught_up === true ||
1083
+ (lineage.bm25_indexed_commit_seq != null &&
1084
+ lineage.bm25_indexed_commit_seq >= targetSeq &&
1085
+ lineage.ann_indexed_commit_seq != null &&
1086
+ lineage.ann_indexed_commit_seq >= targetSeq))) {
1170
1087
  return {
1171
1088
  metadata: last.data,
1172
1089
  lineage,
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { LbbCapabilityError, LbbClient, LbbError, parseSparqlResults, } from "./client.js";
2
- export type { DurableImportLine, DurableImportSource, LbbClientOptions, CallOptions, RequestOptions, HybridSearchOptions, LbbRequestEvent, LbbResponseEvent, FetchLike, ReadConsistencyOptions, SearchConsistency, Schemas, SparqlResults, SparqlResultsJson, SparqlTerm, CommitRequest, CommitResponse, Entity, EntitySelector, GraphMetadata, GraphSummary, SchemaView, SearchRequest, SearchResponse, SearchResult, Snapshot, } from "./client.js";
2
+ export type { DurableImportLine, DurableImportSource, LbbClientOptions, CallOptions, RequestOptions, LbbRequestEvent, LbbResponseEvent, FetchLike, ReadConsistencyOptions, SearchConsistency, Schemas, SparqlResults, SparqlResultsJson, SparqlTerm, CommitRequest, CommitResponse, Entity, EntitySelector, GraphMetadata, GraphSummary, SchemaView, Snapshot, } from "./client.js";
3
3
  export type { components, paths, operations } from "./schema.js";