brainbank 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +155 -0
  3. package/assets/architecture.png +0 -0
  4. package/bin/brainbank +18 -0
  5. package/bin/brainbank-mcp +19 -0
  6. package/dist/chunk-3YBCD6DI.js +117 -0
  7. package/dist/chunk-3YBCD6DI.js.map +1 -0
  8. package/dist/chunk-63GBCDS5.js +3249 -0
  9. package/dist/chunk-63GBCDS5.js.map +1 -0
  10. package/dist/chunk-DMFMTOHF.js +123 -0
  11. package/dist/chunk-DMFMTOHF.js.map +1 -0
  12. package/dist/chunk-FQYKWB2Q.js +136 -0
  13. package/dist/chunk-FQYKWB2Q.js.map +1 -0
  14. package/dist/chunk-IMJJ2VEM.js +74 -0
  15. package/dist/chunk-IMJJ2VEM.js.map +1 -0
  16. package/dist/chunk-M744PCJQ.js +43 -0
  17. package/dist/chunk-M744PCJQ.js.map +1 -0
  18. package/dist/chunk-O3J6ZIXK.js +82 -0
  19. package/dist/chunk-O3J6ZIXK.js.map +1 -0
  20. package/dist/chunk-OPH7GZ7U.js +124 -0
  21. package/dist/chunk-OPH7GZ7U.js.map +1 -0
  22. package/dist/chunk-PXEWQMN7.js +89 -0
  23. package/dist/chunk-PXEWQMN7.js.map +1 -0
  24. package/dist/chunk-RDQYDLYZ.js +69 -0
  25. package/dist/chunk-RDQYDLYZ.js.map +1 -0
  26. package/dist/chunk-VIIHPCC4.js +254 -0
  27. package/dist/chunk-VIIHPCC4.js.map +1 -0
  28. package/dist/chunk-WCQVDF3K.js +14 -0
  29. package/dist/chunk-WCQVDF3K.js.map +1 -0
  30. package/dist/cli.d.ts +1 -0
  31. package/dist/cli.js +3076 -0
  32. package/dist/cli.js.map +1 -0
  33. package/dist/haiku-expander-YRSIPGKP.js +8 -0
  34. package/dist/haiku-expander-YRSIPGKP.js.map +1 -0
  35. package/dist/haiku-pruner-SHAXUPY6.js +8 -0
  36. package/dist/haiku-pruner-SHAXUPY6.js.map +1 -0
  37. package/dist/http-server-QUXHLWUM.js +9 -0
  38. package/dist/http-server-QUXHLWUM.js.map +1 -0
  39. package/dist/index.d.ts +2161 -0
  40. package/dist/index.js +357 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/local-embedding-NZQTILGV.js +8 -0
  43. package/dist/local-embedding-NZQTILGV.js.map +1 -0
  44. package/dist/mcp.d.ts +2 -0
  45. package/dist/mcp.js +334 -0
  46. package/dist/mcp.js.map +1 -0
  47. package/dist/openai-embedding-ZP5TSUJG.js +8 -0
  48. package/dist/openai-embedding-ZP5TSUJG.js.map +1 -0
  49. package/dist/perplexity-context-embedding-GI5PHE6X.js +9 -0
  50. package/dist/perplexity-context-embedding-GI5PHE6X.js.map +1 -0
  51. package/dist/perplexity-embedding-KZRYGJRC.js +10 -0
  52. package/dist/perplexity-embedding-KZRYGJRC.js.map +1 -0
  53. package/dist/plugin-IKQ6IRSJ.js +32 -0
  54. package/dist/plugin-IKQ6IRSJ.js.map +1 -0
  55. package/dist/resolve-ASGLBNUC.js +10 -0
  56. package/dist/resolve-ASGLBNUC.js.map +1 -0
  57. package/dist/stats-tui-ZY2NQSEA.js +1904 -0
  58. package/dist/stats-tui-ZY2NQSEA.js.map +1 -0
  59. package/package.json +96 -0
  60. package/src/brainbank.ts +617 -0
  61. package/src/cli/commands/collection.ts +77 -0
  62. package/src/cli/commands/context.ts +179 -0
  63. package/src/cli/commands/daemon.ts +100 -0
  64. package/src/cli/commands/docs.ts +71 -0
  65. package/src/cli/commands/files.ts +69 -0
  66. package/src/cli/commands/help.ts +77 -0
  67. package/src/cli/commands/index.ts +482 -0
  68. package/src/cli/commands/kv.ts +140 -0
  69. package/src/cli/commands/mcp-export.ts +273 -0
  70. package/src/cli/commands/mcp.ts +6 -0
  71. package/src/cli/commands/reembed.ts +30 -0
  72. package/src/cli/commands/scan.ts +336 -0
  73. package/src/cli/commands/search.ts +203 -0
  74. package/src/cli/commands/stats.ts +68 -0
  75. package/src/cli/commands/status.ts +47 -0
  76. package/src/cli/commands/watch.ts +47 -0
  77. package/src/cli/factory/brain-context.ts +43 -0
  78. package/src/cli/factory/builtin-registration.ts +87 -0
  79. package/src/cli/factory/config-loader.ts +77 -0
  80. package/src/cli/factory/index.ts +69 -0
  81. package/src/cli/factory/plugin-loader.ts +325 -0
  82. package/src/cli/index.ts +71 -0
  83. package/src/cli/server-client.ts +178 -0
  84. package/src/cli/tui/index-tui.tsx +667 -0
  85. package/src/cli/tui/stats-data.ts +523 -0
  86. package/src/cli/tui/stats-search.ts +262 -0
  87. package/src/cli/tui/stats-tui.tsx +1465 -0
  88. package/src/cli/tui/tree-scanner.ts +650 -0
  89. package/src/cli/utils.ts +137 -0
  90. package/src/config.ts +49 -0
  91. package/src/constants.ts +21 -0
  92. package/src/db/adapter.ts +112 -0
  93. package/src/db/metadata.ts +130 -0
  94. package/src/db/migrations.ts +66 -0
  95. package/src/db/sqlite-adapter.ts +218 -0
  96. package/src/db/tracker.ts +91 -0
  97. package/src/engine/index-api.ts +81 -0
  98. package/src/engine/reembed.ts +206 -0
  99. package/src/engine/search-api.ts +218 -0
  100. package/src/index.ts +154 -0
  101. package/src/lib/fts.ts +57 -0
  102. package/src/lib/languages.ts +180 -0
  103. package/src/lib/logger.ts +126 -0
  104. package/src/lib/math.ts +87 -0
  105. package/src/lib/provider-key.ts +20 -0
  106. package/src/lib/prune.ts +71 -0
  107. package/src/lib/rrf.ts +133 -0
  108. package/src/lib/write-lock.ts +108 -0
  109. package/src/mcp/mcp-server.ts +195 -0
  110. package/src/mcp/workspace-factory.ts +68 -0
  111. package/src/mcp/workspace-pool.ts +224 -0
  112. package/src/plugin.ts +381 -0
  113. package/src/providers/embeddings/embedding-worker-thread.ts +95 -0
  114. package/src/providers/embeddings/embedding-worker.ts +141 -0
  115. package/src/providers/embeddings/local-embedding.ts +115 -0
  116. package/src/providers/embeddings/openai-embedding.ts +167 -0
  117. package/src/providers/embeddings/perplexity-context-embedding.ts +195 -0
  118. package/src/providers/embeddings/perplexity-embedding.ts +165 -0
  119. package/src/providers/embeddings/resolve.ts +34 -0
  120. package/src/providers/pruners/haiku-expander.ts +166 -0
  121. package/src/providers/pruners/haiku-pruner.ts +112 -0
  122. package/src/providers/vector/hnsw-index.ts +174 -0
  123. package/src/providers/vector/hnsw-loader.ts +129 -0
  124. package/src/search/bm25-boost.ts +69 -0
  125. package/src/search/context-builder.ts +251 -0
  126. package/src/search/keyword/composite-bm25-search.ts +47 -0
  127. package/src/search/types.ts +37 -0
  128. package/src/search/vector/composite-vector-search.ts +61 -0
  129. package/src/search/vector/mmr.ts +64 -0
  130. package/src/services/collection.ts +384 -0
  131. package/src/services/daemon.ts +87 -0
  132. package/src/services/http-server.ts +336 -0
  133. package/src/services/kv-service.ts +64 -0
  134. package/src/services/plugin-registry.ts +77 -0
  135. package/src/services/watch.ts +340 -0
  136. package/src/services/webhook-server.ts +100 -0
  137. package/src/types.ts +493 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bernardo Castro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # 🧠 BrainBank
2
+
3
+ **Persistent, searchable memory for AI agents.** Index your codebase, git history, documents, and any custom data into a single SQLite file — then search it all with hybrid vector + keyword retrieval.
4
+
5
+ BrainBank gives LLMs a long-term memory that persists between sessions.
6
+
7
+ - **Pluggable** — `.use()` only what you need: [code](#packages), [git](#packages), [docs](#packages), or [custom](docs/custom-plugins.md)
8
+ - **Hybrid search** — vector + BM25 fused with Reciprocal Rank Fusion
9
+ - **Dynamic collections** — `brain.collection('errors')` for any structured data
10
+ - **Pluggable embeddings** — local WASM (free), OpenAI, or Perplexity
11
+ - **Multi-process safe** — concurrent CLI, MCP, and watch processes with automatic hot-reload
12
+ - **Portable** — single `.brainbank/brainbank.db` SQLite file
13
+ - **Modular** — lightweight core + optional [`@brainbank/*`](#packages) packages
14
+
15
+ ---
16
+
17
+ <img src="assets/architecture.png" alt="BrainBank Architecture" width="600">
18
+
19
+ ---
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ npm i -g brainbank @brainbank/code @brainbank/git @brainbank/docs
25
+ ```
26
+
27
+ > [!IMPORTANT]
28
+ > **Node 23+ users:** `@brainbank/code` uses [tree-sitter](https://github.com/tree-sitter/node-tree-sitter) native bindings for AST parsing. On **Node 23 and 24**, V8 headers require C++20 but tree-sitter's `binding.gyp` defaults to C++17, causing the install to fail with `"C++20 or later required."`. Fix it by setting the C++ standard before install:
29
+ >
30
+ > ```bash
31
+ > CXXFLAGS="-std=c++20" npm i -g brainbank
32
+ > ```
33
+ >
34
+ > Node ≤22 is unaffected — prebuilt binaries are available and no compilation is needed.
35
+
36
+ > If you get `ERESOLVE` errors, use `npm i --legacy-peer-deps` — tree-sitter grammars have overlapping peer dep ranges.
37
+
38
+ ### CLI — zero code
39
+
40
+ ```bash
41
+ brainbank index . # scans repo → interactive select → index
42
+ brainbank index . --yes # skip prompts, auto-select all
43
+ brainbank hsearch "rate limiting" # hybrid search
44
+ brainbank kv add decisions "Use Redis..." # store a memory
45
+ brainbank kv search decisions "caching" # recall it
46
+ ```
47
+
48
+ ### Programmatic API
49
+
50
+ ```typescript
51
+ import { BrainBank } from 'brainbank';
52
+ import { code } from '@brainbank/code';
53
+ import { git } from '@brainbank/git';
54
+
55
+ const brain = new BrainBank({ repoPath: '.' })
56
+ .use(code())
57
+ .use(git());
58
+
59
+ await brain.index();
60
+
61
+ const results = await brain.hybridSearch('authentication middleware');
62
+
63
+ const log = brain.collection('decisions');
64
+ await log.add('Switched to argon2id for password hashing', { tags: ['security'] });
65
+
66
+ brain.close();
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Packages
72
+
73
+ `brainbank` is the core framework — strictly plugin-agnostic. Plugins are separate `@brainbank/*` packages that own their database schema, search strategies, and context formatting. Install only what you need:
74
+
75
+ ### Indexer Plugins
76
+
77
+ Data sources that feed into BrainBank's hybrid search engine. Each plugin manages its own tables via the built-in migration system.
78
+
79
+ | Package | Description | Install |
80
+ |---------|-------------|----------|
81
+ | [`@brainbank/code`](packages/code/) | AST chunking, import graph, symbol index (20 languages) | `npm i @brainbank/code` |
82
+ | [`@brainbank/git`](packages/git/) | Git history indexing + co-edit analysis | `npm i @brainbank/git` |
83
+ | [`@brainbank/docs`](packages/docs/) | Document collection search with smart chunking | `npm i @brainbank/docs` |
84
+
85
+ ### Integrations
86
+
87
+ Extensions that connect BrainBank to external tools and workflows.
88
+
89
+ | Package | Description | Install |
90
+ |---------|-------------|----------|
91
+ | [`@brainbank/mcp`](packages/mcp/) | MCP server for Antigravity, Claude, Cursor (read-only, 2 tools) | `npm i @brainbank/mcp` |
92
+
93
+ ---
94
+
95
+ ## Documentation
96
+
97
+ | Guide | Description |
98
+ |-------|-------------|
99
+ | **[Getting Started](docs/getting-started.md)** | Installation, quick start, first search |
100
+ | **[CLI Reference](docs/cli.md)** | Complete command reference |
101
+ | **[Plugins](docs/plugins.md)** | Built-in plugins overview + configuration |
102
+ | **[Collections](docs/collections.md)** | Dynamic KV store with semantic search |
103
+ | **[Search](docs/search.md)** | Hybrid search, scoped queries, context generation |
104
+ | **[Custom Plugins](docs/custom-plugins.md)** | Build plugins + publish as npm packages |
105
+ | **[Configuration](docs/config.md)** | `.brainbank/config.json`, env vars |
106
+ | **[Embeddings, Reranker & Pruner](docs/embeddings.md)** | Providers, benchmarks, per-plugin overrides, LLM noise filter |
107
+ | **[Multi-Repo](docs/multi-repo.md)** | Index multiple repositories into one DB |
108
+ | **[MCP Server](docs/mcp.md)** | AI tool integration (stdio), `mcp:export` setup |
109
+ | **[Indexing](docs/indexing.md)** | Code graph, incremental indexing, re-embedding |
110
+ | **[Migrations](docs/migrations.md)** | Plugin schema migrations, built-in schemas |
111
+ | **[Architecture](docs/architecture.md)** | System internals, data flows, design patterns |
112
+
113
+ ---
114
+
115
+ ## Examples
116
+
117
+ | Example | Description |
118
+ |---------|-------------|
119
+ | [notes-plugin](examples/notes-plugin/) | Programmatic plugin — reads `.txt` files |
120
+ | [custom-plugin](examples/custom-plugin/) | CLI auto-discovery plugin |
121
+ | [custom-package](examples/custom-package/) | Standalone npm package scaffold |
122
+ | [collection](examples/collection/) | Collections, search, tags, metadata |
123
+ | [rag](examples/rag/) | RAG chatbot — docs retrieval + generation ¹ |
124
+
125
+ > ¹ Requires `OPENAI_API_KEY`. RAG also requires `PERPLEXITY_API_KEY`.
126
+
127
+ ---
128
+
129
+ ## Benchmarks
130
+
131
+ Early benchmarks on Apple Silicon — single SQLite file, no external vector DB.
132
+
133
+ | Benchmark | Corpus | Metric | Score |
134
+ |-----------|--------|--------|:-----:|
135
+ | [BEIR SciFact](https://github.com/beir-cellar/beir) | 5,183 scientific abstracts, 300 queries | NDCG@10 | **0.761** |
136
+ | Custom RAG eval | 127 Pinecall.io docs, 20 queries — 1 miss | R@5 | **83%** |
137
+
138
+ **Pipeline progression** — each stage's impact on the custom eval:
139
+
140
+ | Stage | R@5 | Δ |
141
+ |-------|:---:|---|
142
+ | Vector-only (HNSW) | 57% | — |
143
+ | + BM25 → RRF | 78% | +21pp |
144
+ | + Qwen3 reranker | 83% | +5pp |
145
+
146
+ > More benchmarks (code+graph retrieval, large-scale stress tests, multi-provider comparisons) are in progress.
147
+ > Full methodology and reproduction commands → [docs/benchmarks.md](docs/benchmarks.md)
148
+
149
+ ## Contributing
150
+
151
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
152
+
153
+ ## License
154
+
155
+ [MIT](LICENSE)
Binary file
package/bin/brainbank ADDED
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+ # Resolve symlinks to get the real script location
3
+ SCRIPT="$0"
4
+ while [ -L "$SCRIPT" ]; do
5
+ DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
6
+ SCRIPT="$(readlink "$SCRIPT")"
7
+ [ "${SCRIPT#/}" = "$SCRIPT" ] && SCRIPT="$DIR/$SCRIPT"
8
+ done
9
+ DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
10
+ ROOT="$DIR/.."
11
+
12
+ # Use compiled dist if available (npm install), fall back to tsx for development
13
+ if [ -f "$ROOT/dist/cli.js" ]; then
14
+ exec node --disable-warning=ExperimentalWarning "$ROOT/dist/cli.js" "$@"
15
+ else
16
+ export TSX_TSCONFIG_PATH="$ROOT/tsconfig.json"
17
+ NODE_OPTIONS='--disable-warning=ExperimentalWarning' exec "$ROOT/node_modules/.bin/tsx" "$ROOT/src/cli/index.ts" "$@"
18
+ fi
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+ # BrainBank MCP Server — standalone entry for mcp_config.json
3
+ # Resolve symlinks to get the real script location
4
+ SCRIPT="$0"
5
+ while [ -L "$SCRIPT" ]; do
6
+ DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
7
+ SCRIPT="$(readlink "$SCRIPT")"
8
+ [ "${SCRIPT#/}" = "$SCRIPT" ] && SCRIPT="$DIR/$SCRIPT"
9
+ done
10
+ DIR="$(cd "$(dirname "$SCRIPT")" && pwd)"
11
+ ROOT="$DIR/.."
12
+
13
+ # Use compiled dist if available (npm install), fall back to tsx for development
14
+ if [ -f "$ROOT/dist/mcp.js" ]; then
15
+ exec node --disable-warning=ExperimentalWarning "$ROOT/dist/mcp.js" "$@"
16
+ else
17
+ export TSX_TSCONFIG_PATH="$ROOT/tsconfig.json"
18
+ NODE_OPTIONS='--disable-warning=ExperimentalWarning' exec "$ROOT/node_modules/.bin/tsx" "$ROOT/src/mcp/mcp-server.ts" "$@"
19
+ fi
@@ -0,0 +1,117 @@
1
+ import {
2
+ __name
3
+ } from "./chunk-WCQVDF3K.js";
4
+
5
+ // src/providers/embeddings/perplexity-embedding.ts
6
+ var DEFAULT_MODEL = "pplx-embed-v1-4b";
7
+ var DEFAULT_DIMS = {
8
+ "pplx-embed-v1-0.6b": 1024,
9
+ "pplx-embed-v1-4b": 2560
10
+ };
11
+ var API_URL = "https://api.perplexity.ai/v1/embeddings";
12
+ var MAX_BATCH = 100;
13
+ var REQUEST_TIMEOUT_MS = 3e4;
14
+ var BATCH_DELAY_MS = 100;
15
+ var PerplexityEmbedding = class {
16
+ static {
17
+ __name(this, "PerplexityEmbedding");
18
+ }
19
+ dims;
20
+ _apiKey;
21
+ _model;
22
+ _baseUrl;
23
+ _requestDims;
24
+ _timeout;
25
+ constructor(options = {}) {
26
+ this._apiKey = options.apiKey ?? process.env.PERPLEXITY_API_KEY ?? "";
27
+ this._model = options.model ?? DEFAULT_MODEL;
28
+ this._baseUrl = options.baseUrl ?? API_URL;
29
+ this._timeout = options.timeout ?? REQUEST_TIMEOUT_MS;
30
+ if (options.dims) {
31
+ this._requestDims = options.dims;
32
+ this.dims = options.dims;
33
+ } else {
34
+ this.dims = DEFAULT_DIMS[this._model] ?? 2560;
35
+ }
36
+ }
37
+ async embed(text) {
38
+ const results = await this._request([text]);
39
+ return results[0];
40
+ }
41
+ async embedBatch(texts) {
42
+ if (texts.length === 0) return [];
43
+ const results = [];
44
+ for (let i = 0; i < texts.length; i += MAX_BATCH) {
45
+ if (i > 0) await sleep(BATCH_DELAY_MS);
46
+ const batch = texts.slice(i, i + MAX_BATCH);
47
+ const embeddings = await this._request(batch);
48
+ results.push(...embeddings);
49
+ }
50
+ return results;
51
+ }
52
+ async close() {
53
+ }
54
+ async _request(input) {
55
+ if (!this._apiKey) {
56
+ throw new Error(
57
+ "BrainBank: Perplexity API key required. Set PERPLEXITY_API_KEY env var or pass apiKey option."
58
+ );
59
+ }
60
+ const MAX_CHARS = 24e3;
61
+ const safeInput = input.map((t) => t.length > MAX_CHARS ? t.slice(0, MAX_CHARS) : t);
62
+ const body = { model: this._model, input: safeInput };
63
+ if (this._requestDims) body.dimensions = this._requestDims;
64
+ const controller = new AbortController();
65
+ const timer = setTimeout(() => controller.abort(), this._timeout);
66
+ let res;
67
+ try {
68
+ res = await fetch(this._baseUrl, {
69
+ method: "POST",
70
+ headers: {
71
+ "Content-Type": "application/json",
72
+ "Authorization": `Bearer ${this._apiKey}`
73
+ },
74
+ body: JSON.stringify(body),
75
+ signal: controller.signal
76
+ });
77
+ } catch (err) {
78
+ clearTimeout(timer);
79
+ if (err instanceof Error && err.name === "AbortError") {
80
+ throw new Error(`BrainBank: Perplexity embedding request timed out after ${this._timeout}ms.`);
81
+ }
82
+ throw err;
83
+ } finally {
84
+ clearTimeout(timer);
85
+ }
86
+ if (!res.ok) {
87
+ const errText = await res.text();
88
+ throw new Error(`BrainBank: Perplexity embedding API error (${res.status}): ${errText}`);
89
+ }
90
+ const json = await res.json();
91
+ return json.data.sort((a, b) => a.index - b.index).map((d) => decodeBase64Int8(d.embedding, this.dims));
92
+ }
93
+ };
94
+ function decodeBase64Int8(b64, expectedDims) {
95
+ const binary = atob(b64);
96
+ const bytes = new Int8Array(binary.length);
97
+ for (let i = 0; i < binary.length; i++) {
98
+ bytes[i] = binary.charCodeAt(i) << 24 >> 24;
99
+ }
100
+ const dims = Math.min(bytes.length, expectedDims);
101
+ const result = new Float32Array(dims);
102
+ for (let i = 0; i < dims; i++) {
103
+ result[i] = bytes[i];
104
+ }
105
+ return result;
106
+ }
107
+ __name(decodeBase64Int8, "decodeBase64Int8");
108
+ function sleep(ms) {
109
+ return new Promise((resolve) => setTimeout(resolve, ms));
110
+ }
111
+ __name(sleep, "sleep");
112
+
113
+ export {
114
+ PerplexityEmbedding,
115
+ decodeBase64Int8
116
+ };
117
+ //# sourceMappingURL=chunk-3YBCD6DI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/embeddings/perplexity-embedding.ts"],"sourcesContent":["/**\n * BrainBank — Perplexity Standard Embedding Provider\n *\n * Uses Perplexity's embedding API via fetch (no SDK dependency).\n * Models: pplx-embed-v1-0.6b (1024d) and pplx-embed-v1-4b (2560d).\n *\n * Perplexity returns base64-encoded signed int8 vectors by default.\n * This provider decodes them to Float32Array for HNSW compatibility.\n *\n * Usage:\n * const brain = new BrainBank({\n * embeddingProvider: new PerplexityEmbedding({ model: 'pplx-embed-v1-4b' }),\n * });\n */\n\nimport type { EmbeddingProvider } from '@/types.ts';\n\nconst DEFAULT_MODEL = 'pplx-embed-v1-4b';\nconst DEFAULT_DIMS: Record<string, number> = {\n 'pplx-embed-v1-0.6b': 1024,\n 'pplx-embed-v1-4b': 2560,\n};\nconst API_URL = 'https://api.perplexity.ai/v1/embeddings';\nconst MAX_BATCH = 100;\nconst REQUEST_TIMEOUT_MS = 30_000;\nconst BATCH_DELAY_MS = 100;\n\nexport interface PerplexityEmbeddingOptions {\n /** Perplexity API key. Falls back to PERPLEXITY_API_KEY env var. */\n apiKey?: string;\n /** Model name. Default: 'pplx-embed-v1-4b' */\n model?: string;\n /** Vector dimensions (Matryoshka reduction). If omitted, uses model default. */\n dims?: number;\n /** Base URL override. */\n baseUrl?: string;\n /** Request timeout in ms. Default: 30000 */\n timeout?: number;\n}\n\nexport class PerplexityEmbedding implements EmbeddingProvider {\n readonly dims: number;\n\n private _apiKey: string;\n private _model: string;\n private _baseUrl: string;\n private _requestDims: number | undefined;\n private _timeout: number;\n\n constructor(options: PerplexityEmbeddingOptions = {}) {\n this._apiKey = options.apiKey ?? process.env.PERPLEXITY_API_KEY ?? '';\n this._model = options.model ?? DEFAULT_MODEL;\n this._baseUrl = options.baseUrl ?? API_URL;\n this._timeout = options.timeout ?? REQUEST_TIMEOUT_MS;\n\n if (options.dims) {\n this._requestDims = options.dims;\n this.dims = options.dims;\n } else {\n this.dims = DEFAULT_DIMS[this._model] ?? 2560;\n }\n }\n\n async embed(text: string): Promise<Float32Array> {\n const results = await this._request([text]);\n return results[0];\n }\n\n async embedBatch(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return [];\n\n const results: Float32Array[] = [];\n\n for (let i = 0; i < texts.length; i += MAX_BATCH) {\n if (i > 0) await sleep(BATCH_DELAY_MS);\n const batch = texts.slice(i, i + MAX_BATCH);\n const embeddings = await this._request(batch);\n results.push(...embeddings);\n }\n\n return results;\n }\n\n async close(): Promise<void> {\n // No resources to release\n }\n\n private async _request(input: string[]): Promise<Float32Array[]> {\n if (!this._apiKey) {\n throw new Error(\n 'BrainBank: Perplexity API key required. Set PERPLEXITY_API_KEY env var or pass apiKey option.',\n );\n }\n\n const MAX_CHARS = 24_000;\n const safeInput = input.map(t => t.length > MAX_CHARS ? t.slice(0, MAX_CHARS) : t);\n\n const body: Record<string, unknown> = { model: this._model, input: safeInput };\n if (this._requestDims) body.dimensions = this._requestDims;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this._timeout);\n\n let res: Response;\n try {\n res = await fetch(this._baseUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err: unknown) {\n clearTimeout(timer);\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error(`BrainBank: Perplexity embedding request timed out after ${this._timeout}ms.`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`BrainBank: Perplexity embedding API error (${res.status}): ${errText}`);\n }\n\n const json = await res.json() as PerplexityStandardResponse;\n return json.data\n .sort((a, b) => a.index - b.index)\n .map(d => decodeBase64Int8(d.embedding, this.dims));\n }\n}\n\n\ninterface PerplexityStandardResponse {\n data: Array<{ index: number; embedding: string }>;\n}\n\n\n/**\n * Decode a base64-encoded signed int8 embedding to Float32Array.\n * Perplexity returns embeddings as base64(int8[]) by default.\n */\nexport function decodeBase64Int8(b64: string, expectedDims: number): Float32Array {\n const binary = atob(b64);\n const bytes = new Int8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i) << 24 >> 24; // sign-extend to int8\n }\n\n const dims = Math.min(bytes.length, expectedDims);\n const result = new Float32Array(dims);\n for (let i = 0; i < dims; i++) {\n result[i] = bytes[i];\n }\n return result;\n}\n\n/** Simple delay helper. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}