@adonis-agora/agent 0.35.0 → 0.37.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/NOTICE +31 -0
  3. package/README.md +5 -5
  4. package/dist/configure.d.ts.map +1 -1
  5. package/dist/configure.js +1 -0
  6. package/dist/configure.js.map +1 -1
  7. package/dist/src/index.d.ts +1 -1
  8. package/dist/src/index.js +1 -1
  9. package/dist/src/rag/index.d.ts +3 -1
  10. package/dist/src/rag/index.d.ts.map +1 -1
  11. package/dist/src/rag/index.js +3 -1
  12. package/dist/src/rag/index.js.map +1 -1
  13. package/dist/src/rag/lexical-query.d.ts +28 -0
  14. package/dist/src/rag/lexical-query.d.ts.map +1 -0
  15. package/dist/src/rag/lexical-query.js +90 -0
  16. package/dist/src/rag/lexical-query.js.map +1 -0
  17. package/dist/src/rag/openai-embeddings.d.ts +20 -2
  18. package/dist/src/rag/openai-embeddings.d.ts.map +1 -1
  19. package/dist/src/rag/openai-embeddings.js +107 -42
  20. package/dist/src/rag/openai-embeddings.js.map +1 -1
  21. package/dist/src/rag/pg-vector-store.d.ts +56 -7
  22. package/dist/src/rag/pg-vector-store.d.ts.map +1 -1
  23. package/dist/src/rag/pg-vector-store.js +65 -17
  24. package/dist/src/rag/pg-vector-store.js.map +1 -1
  25. package/dist/src/rag/tree/build.d.ts +96 -0
  26. package/dist/src/rag/tree/build.d.ts.map +1 -0
  27. package/dist/src/rag/tree/build.js +659 -0
  28. package/dist/src/rag/tree/build.js.map +1 -0
  29. package/dist/src/rag/tree/headings.d.ts +29 -0
  30. package/dist/src/rag/tree/headings.d.ts.map +1 -0
  31. package/dist/src/rag/tree/headings.js +227 -0
  32. package/dist/src/rag/tree/headings.js.map +1 -0
  33. package/dist/src/rag/tree/index-document.d.ts +33 -0
  34. package/dist/src/rag/tree/index-document.d.ts.map +1 -0
  35. package/dist/src/rag/tree/index-document.js +36 -0
  36. package/dist/src/rag/tree/index-document.js.map +1 -0
  37. package/dist/src/rag/tree/index.d.ts +13 -0
  38. package/dist/src/rag/tree/index.d.ts.map +1 -0
  39. package/dist/src/rag/tree/index.js +12 -0
  40. package/dist/src/rag/tree/index.js.map +1 -0
  41. package/dist/src/rag/tree/keyword-tree-llm.d.ts +15 -0
  42. package/dist/src/rag/tree/keyword-tree-llm.d.ts.map +1 -0
  43. package/dist/src/rag/tree/keyword-tree-llm.js +105 -0
  44. package/dist/src/rag/tree/keyword-tree-llm.js.map +1 -0
  45. package/dist/src/rag/tree/llm.d.ts +179 -0
  46. package/dist/src/rag/tree/llm.d.ts.map +1 -0
  47. package/dist/src/rag/tree/llm.js +264 -0
  48. package/dist/src/rag/tree/llm.js.map +1 -0
  49. package/dist/src/rag/tree/navigate-tool.d.ts +58 -0
  50. package/dist/src/rag/tree/navigate-tool.d.ts.map +1 -0
  51. package/dist/src/rag/tree/navigate-tool.js +95 -0
  52. package/dist/src/rag/tree/navigate-tool.js.map +1 -0
  53. package/dist/src/rag/tree/navigate.d.ts +137 -0
  54. package/dist/src/rag/tree/navigate.d.ts.map +1 -0
  55. package/dist/src/rag/tree/navigate.js +404 -0
  56. package/dist/src/rag/tree/navigate.js.map +1 -0
  57. package/dist/src/rag/tree/pg-tree-store.d.ts +45 -0
  58. package/dist/src/rag/tree/pg-tree-store.d.ts.map +1 -0
  59. package/dist/src/rag/tree/pg-tree-store.js +205 -0
  60. package/dist/src/rag/tree/pg-tree-store.js.map +1 -0
  61. package/dist/src/rag/tree/store.d.ts +57 -0
  62. package/dist/src/rag/tree/store.d.ts.map +1 -0
  63. package/dist/src/rag/tree/store.js +66 -0
  64. package/dist/src/rag/tree/store.js.map +1 -0
  65. package/dist/src/rag/tree/two-stage.d.ts +54 -0
  66. package/dist/src/rag/tree/two-stage.d.ts.map +1 -0
  67. package/dist/src/rag/tree/two-stage.js +92 -0
  68. package/dist/src/rag/tree/two-stage.js.map +1 -0
  69. package/dist/src/rag/tree/types.d.ts +135 -0
  70. package/dist/src/rag/tree/types.d.ts.map +1 -0
  71. package/dist/src/rag/tree/types.js +21 -0
  72. package/dist/src/rag/tree/types.js.map +1 -0
  73. package/dist/stubs/database/migrations/create_agent_rag_trees.stub +40 -0
  74. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @adonis-agora/agent
2
2
 
3
+ ## 0.37.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#194](https://github.com/DavideCarvalho/adonis-agora-agent/pull/194) [`b0e7284`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/b0e7284efa7a6d48f42038bd4a11f18fcc167ba9) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - PageIndex-style tree navigation retrieval for long structured documents ("vectorless", reasoning-based: a table-of-contents tree per document, navigated by an LLM) — the port of the nestjs-agent feature. In a 149-question benchmark it beat hybrid dense+lexical search by +24 points on long GovCon regulations and +30 on FinanceBench 10-Ks, with no gain on short notes, so it is built to sit behind the existing search.
8
+
9
+ - `buildDocumentTree` / `indexDocumentTree`: structure from the PDF outline, section titles or detected headings (markdown, `PART 52`, `Item 7.`, `52.236–5 …`; running headers and contents listings dropped) before any LLM; LLM structuring only for documents with no usable outline; page groups as the never-failing fallback. Hard per-document budget (calls, input/output tokens, timeout) checked before every call, no retries. Deterministic and fingerprinted: unchanged documents cost no LLM call, changed ones re-summarize only changed sections. `minUnits`/`minChars` threshold.
10
+ - `DocumentTreeStore` SPI with `MemoryDocumentTreeStore` and `PgDocumentTreeStore` over the Lucid raw runner (filters with the vector stores' semantics), plus a published `create_agent_rag_trees` migration (`node ace configure` now publishes it; delete it if you don't use tree navigation).
11
+ - `TreeNavigationRetriever` (single pass or bounded beam, auditable trail, `Retriever`), `TwoStageRetriever` (first stage picks documents, top long ones navigated), and `createNavigateDocumentTool` — the `navigate_document` tool as a `defineTool` functional tool.
12
+ - `TreeLlm` adapters: `openAiChatTreeLlm`, `treeLlmFromModelProvider`, `cachedTreeLlm`, and the deterministic `keywordTreeLlm` for tests.
13
+
14
+ Credits: PageIndex (Vectify AI, MIT) — see the package NOTICE.
15
+
16
+ ### Patch Changes
17
+
18
+ - [#193](https://github.com/DavideCarvalho/adonis-agora-agent/pull/193) [`2893186`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/2893186bb289e94fb9c710b63b4c1c8bfb7173a1) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `openAiEmbeddings`: a batch the server refuses as too large — HTTP 413, or TEI's `batch size 64 > maximum allowed batch size 32` (its `--max-client-batch-size` defaults to 32 while `batchSize` defaults to 64) — is split in halves and retried instead of failing the whole ingestion, and later requests to the same server and model start at the size that worked (remembered for the process). `embedWithUsage` sums the tokens of the accepted requests. Each split is reported through the new `onWarn` option. New export: `isBatchTooLarge(error)`.
19
+
20
+ ## 0.36.0
21
+
22
+ ### Minor Changes
23
+
24
+ - [#191](https://github.com/DavideCarvalho/adonis-agora-agent/pull/191) [`c70587d`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/c70587d52cb00235203539d5b4868d5055789788) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - `PgLexicalVectorStore.searchText` (and `retrievers.pgvector({ fullText })`): natural-language questions are searched by their meaningful terms. The question's stop words are dropped (new `fullText.stopWords`, default `DEFAULT_STOP_WORDS`: English, Portuguese and Spanish, only the language(s) the question is written in; `false` keeps every word) and rows holding any remaining term are ranked by the sum of the matched terms' IDF among the matching rows (BM25 without term frequencies), `ts_rank_cd` breaking ties. Before, a question had to match every word and then fell back to any word including stop words, ranked by `ts_rank_cd`, so rows dense in "the/of/which" won: in a 149-question benchmark over 12k chunks this took the keyword-only evidence hit rate from 18% to 75% (hybrid 69% to 80%), and it is faster. Quoted phrases and `-word` still go through `websearch_to_tsquery` as written first; `anyTermFallback: false` keeps the old every-word-only behavior. New exports: `keywordTerms`, `hasSearchSyntax`, `anyTermTsquery`, `DEFAULT_STOP_WORDS`.
25
+
3
26
  ## 0.35.0
4
27
 
5
28
  ### Minor Changes
package/NOTICE ADDED
@@ -0,0 +1,31 @@
1
+ @adonis-agora/agent
2
+ Copyright (c) Davi Carvalho. Licensed under the MIT License.
3
+
4
+ The tree navigation retrieval in src/rag/tree/ (buildDocumentTree, TreeNavigationRetriever,
5
+ TwoStageRetriever, navigate_document) is an independent TypeScript implementation of the method of
6
+ PageIndex ("vectorless", reasoning-based RAG: an LLM-navigated table-of-contents tree per document).
7
+ Its tree-search and document-selection prompts are adapted from PageIndex's.
8
+
9
+ PageIndex — https://github.com/VectifyAI/PageIndex
10
+
11
+ MIT License
12
+
13
+ Copyright (c) 2025 Vectify AI
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
package/README.md CHANGED
@@ -11,8 +11,8 @@ The package is two things layered on top of each other:
11
11
  in-process or as a replay-safe durable workflow (via `@adonis-agora/durable`, optional peer).
12
12
  - An **AdonisJS integration shell** — a service provider, `defineConfig`, HTTP routes (`/agent/*`,
13
13
  `/agent/governance/*`, `/agent/attachments`), Lucid-backed stores, tool discovery from
14
- `app/agent_tools`, RAG (pgvector or Qdrant retrievers), a governed read-only SQL tool, and a
15
- React chat hook.
14
+ `app/agent_tools`, RAG (pgvector or Qdrant retrievers, plus PageIndex-style tree navigation for
15
+ long documents), a governed read-only SQL tool, and a React chat hook.
16
16
 
17
17
  `read` tools auto-execute, `action` tools gate on human approval (HITL), and `agent` tools delegate
18
18
  to another named agent.
@@ -26,9 +26,9 @@ node ace configure @adonis-agora/agent
26
26
 
27
27
  `configure` registers the provider, wires an Assembler `init` hook that generates the typed
28
28
  `app/agent_tools` barrel (falls back to a runtime scan if absent), publishes `config/agent.ts`, and
29
- publishes three migrations — the base agent tables, additive run-tracking, and a pgvector RAG-chunk
30
- table (delete whichever you don't use, e.g. the pgvector migration if you retrieve via Qdrant instead;
31
- run `node ace migration:run` for the rest).
29
+ publishes the migrations — the base agent tables, a pgvector RAG-chunk table, and the tree-navigation
30
+ tables (delete whichever you don't use, e.g. the pgvector migration if you retrieve via Qdrant instead,
31
+ or the trees migration if you don't navigate long documents; run `node ace migration:run` for the rest).
32
32
 
33
33
  Only `ai` (or a hand-rolled `ModelProvider`) and a model factory are strictly required. Everything
34
34
  else is an **optional peer**, imported lazily only when configured:
@@ -1 +1 @@
1
- {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAcjD"}
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAejD"}
package/dist/configure.js CHANGED
@@ -31,5 +31,6 @@ export async function configure(command) {
31
31
  await codemods.makeUsingStub(stubsRoot, 'config/mcp.stub', {});
32
32
  await codemods.makeUsingStub(stubsRoot, 'database/migrations/create_agent_tables.stub', {});
33
33
  await codemods.makeUsingStub(stubsRoot, 'database/migrations/create_agent_rag_chunks.stub', {});
34
+ await codemods.makeUsingStub(stubsRoot, 'database/migrations/create_agent_rag_trees.stub', {});
34
35
  }
35
36
  //# sourceMappingURL=configure.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;QACzD,MAAM,CAAC,WAAW,CAAC,wCAAwC,CAAC,CAAC;QAC7D,mGAAmG;QACnG,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC;IACjE,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,8CAA8C,EAAE,EAAE,CAAC,CAAC;IAC5F,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,kDAAkD,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC"}
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;QACzD,MAAM,CAAC,WAAW,CAAC,wCAAwC,CAAC,CAAC;QAC7D,mGAAmG;QACnG,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC;IACjE,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,8CAA8C,EAAE,EAAE,CAAC,CAAC;IAC5F,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,kDAAkD,EAAE,EAAE,CAAC,CAAC;IAChG,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,iDAAiD,EAAE,EAAE,CAAC,CAAC;AACjG,CAAC"}
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.35.0";
1
+ export declare const VERSION = "0.37.0";
2
2
  export { configure } from '../configure.js';
3
3
  export * from './actor-labels.js';
4
4
  export type { AuthActorResolverOptions } from './actor-resolver.js';
package/dist/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = '0.35.0';
1
+ export const VERSION = '0.37.0';
2
2
  // Re-export the configure hook from the package root so `node ace configure` finds it.
3
3
  // AdonisJS imports the package MAIN and reads `configure` off the module namespace —
4
4
  // the `./configure` subpath alone is never consulted.
@@ -5,10 +5,12 @@ export { HttpReranker, type HttpRerankerOptions } from './http-reranker.js';
5
5
  export { HybridRetriever, type HybridRetrieverOptions } from './hybrid-retriever.js';
6
6
  export { type ChunkRecord, chunkDocuments, type IngestChunksOptions, type IngestDocument, type IngestOptions, ingestChunks, ingestDocuments, } from './ingest.js';
7
7
  export { KeywordRetriever, type KeywordRetrieverOptions } from './keyword-retriever.js';
8
+ export { anyTermTsquery, DEFAULT_STOP_WORDS, hasSearchSyntax, keywordTerms, } from './lexical-query.js';
8
9
  export { LexicalRetriever } from './lexical-retriever.js';
9
10
  export { cosineSimilarity, MemoryVectorStore } from './memory-vector-store.js';
10
- export { HttpModelError, type OpenAiEmbeddingsOptions, openAiEmbeddings, } from './openai-embeddings.js';
11
+ export { HttpModelError, isBatchTooLarge, type OpenAiEmbeddingsOptions, openAiEmbeddings, } from './openai-embeddings.js';
11
12
  export { type PgFullTextOptions, PgLexicalVectorStore, type PgVectorColumns, type PgVectorMetric, PgVectorRetriever, PgVectorStore, type PgVectorStoreOptions, stripNulBytes, toVectorLiteral, } from './pg-vector-store.js';
12
13
  export { RerankingRetriever, type RerankingRetrieverOptions, } from './reranking-retriever.js';
14
+ export * from './tree/index.js';
13
15
  export { applyMetadataPatch, assertRemovalFilter, documentIdOf, effectivePatchKeys, filterDeniesAll, type IndexedDocument, isLexicalVectorStore, type LexicalVectorStore, type MetadataPatch, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, } from './vector-store.js';
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rag/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EACL,KAAK,WAAW,EAChB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,EACZ,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EACL,cAAc,EACd,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,iBAAiB,EACjB,aAAa,EACb,KAAK,oBAAoB,EACzB,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,KAAK,eAAe,EACpB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rag/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EACL,KAAK,WAAW,EAChB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,EACZ,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,iBAAiB,EACjB,aAAa,EACb,KAAK,oBAAoB,EACzB,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,KAAK,eAAe,EACpB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC"}
@@ -5,10 +5,12 @@ export { HttpReranker } from './http-reranker.js';
5
5
  export { HybridRetriever } from './hybrid-retriever.js';
6
6
  export { chunkDocuments, ingestChunks, ingestDocuments, } from './ingest.js';
7
7
  export { KeywordRetriever } from './keyword-retriever.js';
8
+ export { anyTermTsquery, DEFAULT_STOP_WORDS, hasSearchSyntax, keywordTerms, } from './lexical-query.js';
8
9
  export { LexicalRetriever } from './lexical-retriever.js';
9
10
  export { cosineSimilarity, MemoryVectorStore } from './memory-vector-store.js';
10
- export { HttpModelError, openAiEmbeddings, } from './openai-embeddings.js';
11
+ export { HttpModelError, isBatchTooLarge, openAiEmbeddings, } from './openai-embeddings.js';
11
12
  export { PgLexicalVectorStore, PgVectorRetriever, PgVectorStore, stripNulBytes, toVectorLiteral, } from './pg-vector-store.js';
12
13
  export { RerankingRetriever, } from './reranking-retriever.js';
14
+ export * from './tree/index.js';
13
15
  export { applyMetadataPatch, assertRemovalFilter, documentIdOf, effectivePatchKeys, filterDeniesAll, isLexicalVectorStore, UnsafeRemovalError, } from './vector-store.js';
14
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rag/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAA+B,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAEL,cAAc,EAId,YAAY,EACZ,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAgC,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EACL,cAAc,EAEd,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,oBAAoB,EAGpB,iBAAiB,EACjB,aAAa,EAEb,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,eAAe,EAEf,oBAAoB,EAGpB,kBAAkB,GAInB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rag/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAA+B,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAEL,cAAc,EAId,YAAY,EACZ,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAgC,MAAM,wBAAwB,CAAC;AACxF,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EACL,cAAc,EACd,eAAe,EAEf,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,oBAAoB,EAGpB,iBAAiB,EACjB,aAAa,EAEb,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,eAAe,EAEf,oBAAoB,EAGpB,kBAAkB,GAInB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Turning a natural-language question into full-text search terms, for the lexical leg of
3
+ * `PgLexicalVectorStore`.
4
+ *
5
+ * With the `simple` text-search configuration (no stemming, no stop words; any language) a question
6
+ * searched as is either matches nothing (every word required) or, as "any word", ranks chunks full
7
+ * of "the / of / which / de / que" first. So the query side drops the stop words of the question's
8
+ * language(s) and keeps the meaningful terms, which the store then ranks by how many of them a chunk
9
+ * holds, rare terms weighing more (an IDF over the matching rows, BM25-style).
10
+ */
11
+ /**
12
+ * Postgres' Snowball stop word lists (`tsearch_data/{english,portuguese,spanish}.stop`), minus
13
+ * one-letter words (never terms here) and words that carry meaning in a sibling language
14
+ * ("estado", "sentido").
15
+ */
16
+ export declare const DEFAULT_STOP_WORDS: Readonly<Record<string, ReadonlySet<string>>>;
17
+ /**
18
+ * The meaningful terms of a question, in order, deduplicated, at most `max`: the stop words of the
19
+ * language(s) the question is written in are dropped (the language(s) with the most stop words in
20
+ * it, so "sea"/"son" stay terms in an English question although they are Spanish stop words). A
21
+ * question made only of stop words keeps them all (there is nothing else to search for).
22
+ */
23
+ export declare function keywordTerms(query: string, max?: number, stopWords?: Readonly<Record<string, ReadonlySet<string>>>): string[];
24
+ /** Whether the user wrote search syntax (a quoted phrase, `-exclusion`) worth honoring as such. */
25
+ export declare function hasSearchSyntax(query: string): boolean;
26
+ /** `'a' | 'b' | …` for `to_tsquery` (terms are letter/digit runs, so quoting is enough). */
27
+ export declare function anyTermTsquery(terms: string[]): string;
28
+ //# sourceMappingURL=lexical-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexical-query.d.ts","sourceRoot":"","sources":["../../../src/rag/lexical-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CA+C5E,CAAC;AAIF;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,GAAG,SAAK,EACR,SAAS,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAsB,GAC5E,MAAM,EAAE,CAUV;AAED,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Turning a natural-language question into full-text search terms, for the lexical leg of
3
+ * `PgLexicalVectorStore`.
4
+ *
5
+ * With the `simple` text-search configuration (no stemming, no stop words; any language) a question
6
+ * searched as is either matches nothing (every word required) or, as "any word", ranks chunks full
7
+ * of "the / of / which / de / que" first. So the query side drops the stop words of the question's
8
+ * language(s) and keeps the meaningful terms, which the store then ranks by how many of them a chunk
9
+ * holds, rare terms weighing more (an IDF over the matching rows, BM25-style).
10
+ */
11
+ const words = (s) => new Set(s.split(/\s+/).filter(Boolean));
12
+ /**
13
+ * Postgres' Snowball stop word lists (`tsearch_data/{english,portuguese,spanish}.stop`), minus
14
+ * one-letter words (never terms here) and words that carry meaning in a sibling language
15
+ * ("estado", "sentido").
16
+ */
17
+ export const DEFAULT_STOP_WORDS = {
18
+ english: words(`
19
+ me my myself we our ours ourselves you your yours yourself yourselves he him his himself she
20
+ her hers herself it its itself they them their theirs themselves what which who whom this that
21
+ these those am is are was were be been being have has had having do does did doing an the and
22
+ but if or because as until while of at by for with about against between into through during
23
+ before after above below to from up down in out on off over under again further then once here
24
+ there when where why how all any both each few more most other some such no nor not only own
25
+ same so than too very can will just don should now`),
26
+ portuguese: words(`
27
+ de que do da em um para com não uma os no se na por mais as dos como mas ao ele das seu sua ou
28
+ quando muito nos já eu também só pelo pela até isso ela entre depois sem mesmo aos seus quem
29
+ nas me esse eles você essa num nem suas meu às minha numa pelos elas qual nós lhe deles essas
30
+ esses pelas este dele tu te vocês vos lhes meus minhas teu tua teus tuas nosso nossa nossos
31
+ nossas dela delas esta estes estas aquele aquela aqueles aquelas isto aquilo estou está estamos
32
+ estão estive esteve estivemos estiveram estava estávamos estavam estivera estivéramos esteja
33
+ estejamos estejam estivesse estivéssemos estivessem estiver estivermos estiverem hei há havemos
34
+ hão houve houvemos houveram houvera houvéramos haja hajamos hajam houvesse houvéssemos
35
+ houvessem houver houvermos houverem houverei houverá houveremos houverão houveria houveríamos
36
+ houveriam sou somos são era éramos eram fui foi fomos foram fora fôramos seja sejamos sejam
37
+ fosse fôssemos fossem for formos forem serei será seremos serão seria seríamos seriam tenho tem
38
+ temos tém tinha tínhamos tinham tive teve tivemos tiveram tivera tivéramos tenha tenhamos
39
+ tenham tivesse tivéssemos tivessem tiver tivermos tiverem terei terá teremos terão teria
40
+ teríamos teriam`),
41
+ spanish: words(`
42
+ de la que el en los del se las por un para con no una su al lo como más pero sus le ya este sí
43
+ porque esta entre cuando muy sin sobre también me hasta hay donde quien desde todo nos durante
44
+ todos uno les ni contra otros ese eso ante ellos esto mí antes algunos qué unos yo otro otras
45
+ otra él tanto esa estos mucho quienes nada muchos cual poco ella estar estas algunas algo
46
+ nosotros mi mis tú te ti tu tus ellas nosotras vosotros vosotras os mío mía míos mías tuyo
47
+ tuya tuyos tuyas suyo suya suyos suyas nuestro nuestra nuestros nuestras vuestro vuestra
48
+ vuestros vuestras esos esas estoy estás está estamos estáis están esté estés estemos estéis
49
+ estén estaré estarás estará estaremos estaréis estarán estaría estarías estaríamos estaríais
50
+ estarían estaba estabas estábamos estabais estaban estuve estuviste estuvo estuvimos
51
+ estuvisteis estuvieron estuviera estuvieras estuviéramos estuvierais estuvieran estuviese
52
+ estuvieses estuviésemos estuvieseis estuviesen estando estad he has ha hemos habéis han haya
53
+ hayas hayamos hayáis hayan habré habrás habrá habremos habréis habrán habría habrías habríamos
54
+ habríais habrían había habías habíamos habíais habían hube hubiste hubo hubimos hubisteis
55
+ hubieron hubiera hubieras hubiéramos hubierais hubieran hubiese hubieses hubiésemos hubieseis
56
+ hubiesen habiendo habido habida habidos habidas soy eres es somos sois son sea seas seamos
57
+ seáis sean seré serás será seremos seréis serán sería serías seríamos seríais serían era eras
58
+ éramos erais eran fui fuiste fue fuimos fuisteis fueron fuera fueras fuéramos fuerais fueran
59
+ fuese fueses fuésemos fueseis fuesen tengo tienes tiene tenemos tenéis tienen tenga tengas
60
+ tengamos tengáis tengan tendré tendrás tendrá tendremos tendréis tendrán tendría tendrías
61
+ tendríamos tendríais tendrían tenía tenías teníamos teníais tenían tuve tuviste tuvo tuvimos
62
+ tuvisteis tuvieron tuviera tuvieras tuviéramos tuvierais tuvieran tuviese tuvieses tuviésemos
63
+ tuvieseis tuviesen teniendo tenido tenida tenidos tenidas tened`),
64
+ };
65
+ const TOKEN = /[\p{L}\p{N}]{2,}/gu;
66
+ /**
67
+ * The meaningful terms of a question, in order, deduplicated, at most `max`: the stop words of the
68
+ * language(s) the question is written in are dropped (the language(s) with the most stop words in
69
+ * it, so "sea"/"son" stay terms in an English question although they are Spanish stop words). A
70
+ * question made only of stop words keeps them all (there is nothing else to search for).
71
+ */
72
+ export function keywordTerms(query, max = 24, stopWords = DEFAULT_STOP_WORDS) {
73
+ const tokens = [...new Set(query.toLowerCase().normalize('NFC').match(TOKEN) ?? [])];
74
+ const counts = Object.entries(stopWords).map(([, stop]) => [stop, tokens.filter((t) => stop.has(t)).length]);
75
+ const best = Math.max(0, ...counts.map(([, n]) => n));
76
+ if (best === 0)
77
+ return tokens.slice(0, max);
78
+ const stop = counts.filter(([, n]) => n === best).map(([s]) => s);
79
+ const terms = tokens.filter((t) => !stop.some((s) => s.has(t)));
80
+ return (terms.length > 0 ? terms : tokens).slice(0, max);
81
+ }
82
+ /** Whether the user wrote search syntax (a quoted phrase, `-exclusion`) worth honoring as such. */
83
+ export function hasSearchSyntax(query) {
84
+ return /"[^"]+"/.test(query) || /(^|\s)-[\p{L}\p{N}]/u.test(query);
85
+ }
86
+ /** `'a' | 'b' | …` for `to_tsquery` (terms are letter/digit runs, so quoting is enough). */
87
+ export function anyTermTsquery(terms) {
88
+ return terms.map((t) => `'${t.replace(/'/g, '')}'`).join(' | ');
89
+ }
90
+ //# sourceMappingURL=lexical-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexical-query.js","sourceRoot":"","sources":["../../../src/rag/lexical-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAkD;IAC/E,OAAO,EAAE,KAAK,CAAC;;;;;;;uDAOsC,CAAC;IACtD,UAAU,EAAE,KAAK,CAAC;;;;;;;;;;;;;;oBAcA,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;oEAsBmD,CAAC;CACpE,CAAC;AAEF,MAAM,KAAK,GAAG,oBAAoB,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,GAAG,GAAG,EAAE,EACR,SAAS,GAAkD,kBAAkB;IAE7E,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAC1C,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAU,CACxE,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,KAAe;IAC5C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC"}
@@ -20,7 +20,12 @@ export interface OpenAiEmbeddingsOptions {
20
20
  apiKey?: string;
21
21
  /** Output width for models that support shortening (`text-embedding-3-*`), sent as `dimensions`. */
22
22
  dimensions?: number;
23
- /** Inputs per request. Default 64. */
23
+ /**
24
+ * Inputs per request. Default 64. When the server refuses a request for its size (see
25
+ * {@link isBatchTooLarge}) the batch is split in halves and retried, and later requests use the
26
+ * size that worked — so a server whose limit is below this (TEI's `--max-client-batch-size`
27
+ * defaults to 32) costs one refused request, not a failed ingestion.
28
+ */
24
29
  batchSize?: number;
25
30
  /**
26
31
  * Inputs longer than this many characters are truncated before sending, so one oversized chunk
@@ -37,15 +42,28 @@ export interface OpenAiEmbeddingsOptions {
37
42
  * ledger and quota through `embedWithUsage`; this is for your own metering on top.
38
43
  */
39
44
  onUsage?: (tokens: number) => void;
45
+ /**
46
+ * Called when the server refused a batch for its size and it was split — a misconfiguration worth
47
+ * surfacing: raise the server's limit (TEI: `--max-client-batch-size`) or lower `batchSize`.
48
+ */
49
+ onWarn?: (message: string) => void;
40
50
  /** `fetch` to use. Default the global one. */
41
51
  fetch?: typeof fetch;
42
52
  }
53
+ /**
54
+ * Did the server refuse this request for its **size** — too many inputs (TEI's
55
+ * `--max-client-batch-size`: `batch size 64 > maximum allowed batch size 32`) or too large a
56
+ * payload? Those are fixed by sending fewer inputs per request; anything else (auth, a bad model,
57
+ * one input over the context window) is not, and is rethrown as is.
58
+ */
59
+ export declare function isBatchTooLarge(error: unknown): boolean;
43
60
  /**
44
61
  * An {@link EmbeddingProvider} over any OpenAI-compatible `POST /v1/embeddings` endpoint, with no SDK
45
62
  * dependency: OpenAI itself, or a gateway/local server speaking the same protocol (TEI, Ollama,
46
63
  * vLLM, LocalAI…). For a Vercel AI SDK model use `aiSdkEmbedding` from `-ai-sdk` instead.
47
64
  *
48
- * Batches by {@link OpenAiEmbeddingsOptions.batchSize}, restores input order from each item's
65
+ * Batches by {@link OpenAiEmbeddingsOptions.batchSize} — splitting and retrying a batch the server
66
+ * refuses as too large, and remembering the size that worked — restores input order from each item's
49
67
  * `index`, and fails loudly (an {@link HttpModelError}) on a non-2xx answer or a vector count that
50
68
  * doesn't match the inputs. Empty strings are sent as a single space, since OpenAI rejects them.
51
69
  * Implements `embedWithUsage` with the provider-reported tokens (summed over the batches) and model, so
@@ -1 +1 @@
1
- {"version":3,"file":"openai-embeddings.d.ts","sourceRoot":"","sources":["../../../src/rag/openai-embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,8BAA8B,CAAC;AAEvF,mFAAmF;AACnF,qBAAa,cAAe,SAAQ,KAAK;IAErC,mGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE,MAAM;IAFzB;IACE,mGAAmG;IAC1F,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EAIhB;CACF;AAED,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,iBAAiB,CA6EpF;AAUD,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUnD"}
1
+ {"version":3,"file":"openai-embeddings.d.ts","sourceRoot":"","sources":["../../../src/rag/openai-embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,8BAA8B,CAAC;AAEvF,mFAAmF;AACnF,qBAAa,cAAe,SAAQ,KAAK;IAErC,mGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE,MAAM;IAFzB;IACE,mGAAmG;IAC1F,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EAIhB;CACF;AAED,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAavD;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,iBAAiB,CA+HpF;AAUD,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUnD"}
@@ -9,12 +9,35 @@ export class HttpModelError extends Error {
9
9
  this.name = 'HttpModelError';
10
10
  }
11
11
  }
12
+ /**
13
+ * Did the server refuse this request for its **size** — too many inputs (TEI's
14
+ * `--max-client-batch-size`: `batch size 64 > maximum allowed batch size 32`) or too large a
15
+ * payload? Those are fixed by sending fewer inputs per request; anything else (auth, a bad model,
16
+ * one input over the context window) is not, and is rethrown as is.
17
+ */
18
+ export function isBatchTooLarge(error) {
19
+ if (!(error instanceof HttpModelError)) {
20
+ return false;
21
+ }
22
+ if (error.status === 413) {
23
+ return true;
24
+ }
25
+ return ((error.status === 400 || error.status === 422) &&
26
+ /batch.?size|too many inputs|maximum allowed batch|payload too large|max.*(inputs|batch)/i.test(error.message));
27
+ }
28
+ /**
29
+ * Batch sizes a server accepted after refusing larger ones, per endpoint URL and model, for the
30
+ * life of the process — so a second provider built for the same server (one per request, say)
31
+ * starts at the size that works instead of re-learning it with a refused request.
32
+ */
33
+ const learnedBatchLimits = new Map();
12
34
  /**
13
35
  * An {@link EmbeddingProvider} over any OpenAI-compatible `POST /v1/embeddings` endpoint, with no SDK
14
36
  * dependency: OpenAI itself, or a gateway/local server speaking the same protocol (TEI, Ollama,
15
37
  * vLLM, LocalAI…). For a Vercel AI SDK model use `aiSdkEmbedding` from `-ai-sdk` instead.
16
38
  *
17
- * Batches by {@link OpenAiEmbeddingsOptions.batchSize}, restores input order from each item's
39
+ * Batches by {@link OpenAiEmbeddingsOptions.batchSize} — splitting and retrying a batch the server
40
+ * refuses as too large, and remembering the size that worked — restores input order from each item's
18
41
  * `index`, and fails loudly (an {@link HttpModelError}) on a non-2xx answer or a vector count that
19
42
  * doesn't match the inputs. Empty strings are sent as a single space, since OpenAI rejects them.
20
43
  * Implements `embedWithUsage` with the provider-reported tokens (summed over the batches) and model, so
@@ -27,60 +50,102 @@ export class HttpModelError extends Error {
27
50
  */
28
51
  export function openAiEmbeddings(options) {
29
52
  const url = `${(options.baseUrl ?? 'https://api.openai.com/v1').replace(/\/+$/, '')}/embeddings`;
30
- const batchSize = Math.max(1, Math.floor(options.batchSize ?? 64));
53
+ const limitKey = `${url} ${options.model}`;
54
+ const configured = Math.max(1, Math.floor(options.batchSize ?? 64));
55
+ let batchSize = Math.min(configured, learnedBatchLimits.get(limitKey) ?? configured);
31
56
  const maxInputChars = options.maxInputChars ?? 24_000;
32
57
  const timeoutMs = options.timeoutMs ?? 120_000;
33
58
  const doFetch = options.fetch ?? fetch;
59
+ const request = async (input) => {
60
+ const response = await doFetch(url, {
61
+ method: 'POST',
62
+ headers: {
63
+ 'content-type': 'application/json',
64
+ ...(options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {}),
65
+ ...options.headers,
66
+ },
67
+ body: JSON.stringify({
68
+ model: options.model,
69
+ input,
70
+ ...(options.dimensions !== undefined ? { dimensions: options.dimensions } : {}),
71
+ }),
72
+ signal: AbortSignal.timeout(timeoutMs),
73
+ });
74
+ const body = await response.text();
75
+ if (!response.ok) {
76
+ throw new HttpModelError(response.status, `Embeddings request failed (${response.status}): ${errorMessageOf(body)}`);
77
+ }
78
+ const json = parseJson(body);
79
+ const items = [...(json?.data ?? [])].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
80
+ if (items.length !== input.length || !items.every((item) => Array.isArray(item.embedding))) {
81
+ throw new HttpModelError(502, `Embeddings returned ${items.length} vectors for ${input.length} inputs`);
82
+ }
83
+ const tokens = json?.usage?.total_tokens ?? json?.usage?.prompt_tokens;
84
+ const counted = typeof tokens === 'number' && Number.isFinite(tokens) ? tokens : undefined;
85
+ if (counted !== undefined) {
86
+ options.onUsage?.(counted);
87
+ }
88
+ return {
89
+ vectors: items.map((item) => item.embedding),
90
+ ...(counted !== undefined ? { tokens: counted } : {}),
91
+ ...(typeof json?.model === 'string' ? { modelId: json.model } : {}),
92
+ };
93
+ };
94
+ const merge = (into, from) => {
95
+ into.vectors.push(...from.vectors);
96
+ if (from.tokens !== undefined) {
97
+ into.tokens = (into.tokens ?? 0) + from.tokens;
98
+ }
99
+ if (into.modelId === undefined && from.modelId !== undefined) {
100
+ into.modelId = from.modelId;
101
+ }
102
+ };
103
+ // A batch refused for its size is split in halves (recursively) and retried; the smaller size
104
+ // becomes the batch size for every later request to this server and model.
105
+ const requestSplitting = async (input) => {
106
+ if (input.length > batchSize) {
107
+ // The size was learned while this batch was being split: send the rest at the size that works.
108
+ const result = { vectors: [] };
109
+ for (let start = 0; start < input.length; start += batchSize) {
110
+ merge(result, await requestSplitting(input.slice(start, start + batchSize)));
111
+ }
112
+ return result;
113
+ }
114
+ try {
115
+ return await request(input);
116
+ }
117
+ catch (error) {
118
+ if (!isBatchTooLarge(error) || input.length <= 1) {
119
+ throw error;
120
+ }
121
+ const half = Math.ceil(input.length / 2);
122
+ if (half < batchSize) {
123
+ batchSize = half;
124
+ learnedBatchLimits.set(limitKey, half);
125
+ options.onWarn?.(`embeddings server ${url} refused a batch of ${input.length} inputs (${error.message}); retrying in batches of ${half}. Raise the server's batch limit (TEI: --max-client-batch-size) or lower batchSize.`);
126
+ }
127
+ const result = await requestSplitting(input.slice(0, half));
128
+ merge(result, await requestSplitting(input.slice(half)));
129
+ return result;
130
+ }
131
+ };
34
132
  const provider = {
35
133
  async embed(texts) {
36
134
  return (await provider.embedWithUsage(texts)).vectors;
37
135
  },
38
136
  async embedWithUsage(texts) {
39
- const vectors = [];
40
- let inputTokens;
41
- let modelId;
42
- for (let start = 0; start < texts.length; start += batchSize) {
137
+ const result = { vectors: [] };
138
+ for (let start = 0; start < texts.length;) {
43
139
  const input = texts
44
140
  .slice(start, start + batchSize)
45
141
  .map((text) => (text.length > maxInputChars ? text.slice(0, maxInputChars) : text) || ' ');
46
- const response = await doFetch(url, {
47
- method: 'POST',
48
- headers: {
49
- 'content-type': 'application/json',
50
- ...(options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {}),
51
- ...options.headers,
52
- },
53
- body: JSON.stringify({
54
- model: options.model,
55
- input,
56
- ...(options.dimensions !== undefined ? { dimensions: options.dimensions } : {}),
57
- }),
58
- signal: AbortSignal.timeout(timeoutMs),
59
- });
60
- const body = await response.text();
61
- if (!response.ok) {
62
- throw new HttpModelError(response.status, `Embeddings request failed (${response.status}): ${errorMessageOf(body)}`);
63
- }
64
- const json = parseJson(body);
65
- const items = [...(json?.data ?? [])].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
66
- if (items.length !== input.length ||
67
- !items.every((item) => Array.isArray(item.embedding))) {
68
- throw new HttpModelError(502, `Embeddings returned ${items.length} vectors for ${input.length} inputs`);
69
- }
70
- for (const item of items) {
71
- vectors.push(item.embedding);
72
- }
73
- const tokens = json?.usage?.total_tokens ?? json?.usage?.prompt_tokens;
74
- if (typeof tokens === 'number' && Number.isFinite(tokens)) {
75
- inputTokens = (inputTokens ?? 0) + tokens;
76
- options.onUsage?.(tokens);
77
- }
78
- modelId ??= typeof json?.model === 'string' ? json.model : undefined;
142
+ merge(result, await requestSplitting(input));
143
+ start += input.length;
79
144
  }
80
145
  return {
81
- vectors,
82
- ...(inputTokens !== undefined
83
- ? { usage: { inputTokens, modelId: modelId ?? options.model } }
146
+ vectors: result.vectors,
147
+ ...(result.tokens !== undefined
148
+ ? { usage: { inputTokens: result.tokens, modelId: result.modelId ?? options.model } }
84
149
  : {}),
85
150
  };
86
151
  },
@@ -1 +1 @@
1
- {"version":3,"file":"openai-embeddings.js","sourceRoot":"","sources":["../../../src/rag/openai-embeddings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai-embeddings.js","sourceRoot":"","sources":["../../../src/rag/openai-embeddings.ts"],"names":[],"mappings":"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"}