@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.
- package/CHANGELOG.md +23 -0
- package/NOTICE +31 -0
- package/README.md +5 -5
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +1 -0
- package/dist/configure.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/rag/index.d.ts +3 -1
- package/dist/src/rag/index.d.ts.map +1 -1
- package/dist/src/rag/index.js +3 -1
- package/dist/src/rag/index.js.map +1 -1
- package/dist/src/rag/lexical-query.d.ts +28 -0
- package/dist/src/rag/lexical-query.d.ts.map +1 -0
- package/dist/src/rag/lexical-query.js +90 -0
- package/dist/src/rag/lexical-query.js.map +1 -0
- package/dist/src/rag/openai-embeddings.d.ts +20 -2
- package/dist/src/rag/openai-embeddings.d.ts.map +1 -1
- package/dist/src/rag/openai-embeddings.js +107 -42
- package/dist/src/rag/openai-embeddings.js.map +1 -1
- package/dist/src/rag/pg-vector-store.d.ts +56 -7
- package/dist/src/rag/pg-vector-store.d.ts.map +1 -1
- package/dist/src/rag/pg-vector-store.js +65 -17
- package/dist/src/rag/pg-vector-store.js.map +1 -1
- package/dist/src/rag/tree/build.d.ts +96 -0
- package/dist/src/rag/tree/build.d.ts.map +1 -0
- package/dist/src/rag/tree/build.js +659 -0
- package/dist/src/rag/tree/build.js.map +1 -0
- package/dist/src/rag/tree/headings.d.ts +29 -0
- package/dist/src/rag/tree/headings.d.ts.map +1 -0
- package/dist/src/rag/tree/headings.js +227 -0
- package/dist/src/rag/tree/headings.js.map +1 -0
- package/dist/src/rag/tree/index-document.d.ts +33 -0
- package/dist/src/rag/tree/index-document.d.ts.map +1 -0
- package/dist/src/rag/tree/index-document.js +36 -0
- package/dist/src/rag/tree/index-document.js.map +1 -0
- package/dist/src/rag/tree/index.d.ts +13 -0
- package/dist/src/rag/tree/index.d.ts.map +1 -0
- package/dist/src/rag/tree/index.js +12 -0
- package/dist/src/rag/tree/index.js.map +1 -0
- package/dist/src/rag/tree/keyword-tree-llm.d.ts +15 -0
- package/dist/src/rag/tree/keyword-tree-llm.d.ts.map +1 -0
- package/dist/src/rag/tree/keyword-tree-llm.js +105 -0
- package/dist/src/rag/tree/keyword-tree-llm.js.map +1 -0
- package/dist/src/rag/tree/llm.d.ts +179 -0
- package/dist/src/rag/tree/llm.d.ts.map +1 -0
- package/dist/src/rag/tree/llm.js +264 -0
- package/dist/src/rag/tree/llm.js.map +1 -0
- package/dist/src/rag/tree/navigate-tool.d.ts +58 -0
- package/dist/src/rag/tree/navigate-tool.d.ts.map +1 -0
- package/dist/src/rag/tree/navigate-tool.js +95 -0
- package/dist/src/rag/tree/navigate-tool.js.map +1 -0
- package/dist/src/rag/tree/navigate.d.ts +137 -0
- package/dist/src/rag/tree/navigate.d.ts.map +1 -0
- package/dist/src/rag/tree/navigate.js +404 -0
- package/dist/src/rag/tree/navigate.js.map +1 -0
- package/dist/src/rag/tree/pg-tree-store.d.ts +45 -0
- package/dist/src/rag/tree/pg-tree-store.d.ts.map +1 -0
- package/dist/src/rag/tree/pg-tree-store.js +205 -0
- package/dist/src/rag/tree/pg-tree-store.js.map +1 -0
- package/dist/src/rag/tree/store.d.ts +57 -0
- package/dist/src/rag/tree/store.d.ts.map +1 -0
- package/dist/src/rag/tree/store.js +66 -0
- package/dist/src/rag/tree/store.js.map +1 -0
- package/dist/src/rag/tree/two-stage.d.ts +54 -0
- package/dist/src/rag/tree/two-stage.d.ts.map +1 -0
- package/dist/src/rag/tree/two-stage.js +92 -0
- package/dist/src/rag/tree/two-stage.js.map +1 -0
- package/dist/src/rag/tree/types.d.ts +135 -0
- package/dist/src/rag/tree/types.d.ts.map +1 -0
- package/dist/src/rag/tree/types.js +21 -0
- package/dist/src/rag/tree/types.js.map +1 -0
- package/dist/stubs/database/migrations/create_agent_rag_trees.stub +40 -0
- 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
|
|
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
|
|
30
|
-
|
|
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:
|
package/dist/configure.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
package/dist/configure.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const VERSION = '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.
|
package/dist/src/rag/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/src/rag/index.js
CHANGED
|
@@ -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,
|
|
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
|
-
/**
|
|
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}
|
|
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
|
|
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}
|
|
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
|
|
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
|
|
40
|
-
let
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
...(
|
|
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":"AAEA,mFAAmF;AACnF,MAAM,OAAO,cAAe,SAAQ,KAAK;IAG5B,MAAM;IAFjB;IACE,mGAAmG;IAC1F,MAAc,EACvB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;sBAHN,MAAM;QAIf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"openai-embeddings.js","sourceRoot":"","sources":["../../../src/rag/openai-embeddings.ts"],"names":[],"mappings":"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"}
|