codesearch-mcp 0.2.3-alpha → 0.3.0-alpha
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 +31 -1
- package/README.md +46 -26
- package/dist/app.js +5 -19
- package/dist/src/chunking/AstChunker.d.ts +12 -0
- package/dist/src/chunking/AstChunker.js +30 -0
- package/dist/src/chunking/CharacterChunkStrategy.d.ts +8 -0
- package/dist/src/chunking/CharacterChunkStrategy.js +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.d.ts +15 -0
- package/dist/src/chunking/DeclarationChunkStrategy.js +87 -0
- package/dist/src/chunking/FragmentMerger.d.ts +11 -0
- package/dist/src/chunking/FragmentMerger.js +61 -0
- package/dist/src/chunking/ImportOnlyFilter.d.ts +5 -0
- package/dist/src/chunking/ImportOnlyFilter.js +14 -0
- package/dist/src/chunking/LangchainTextSplitter.d.ts +21 -0
- package/dist/src/chunking/LangchainTextSplitter.js +133 -0
- package/dist/src/chunking/MinSizeSplitPolicy.d.ts +9 -0
- package/dist/src/chunking/MinSizeSplitPolicy.js +18 -0
- package/dist/src/chunking/RunPacker.d.ts +12 -0
- package/dist/src/chunking/RunPacker.js +84 -0
- package/dist/src/chunking/create-chunker.d.ts +3 -0
- package/dist/src/chunking/create-chunker.js +15 -0
- package/dist/src/chunking/interfaces.d.ts +27 -0
- package/dist/src/chunking/interfaces.js +2 -0
- package/dist/src/commands/ConfigCommand.js +1 -9
- package/dist/src/commands/EvalCommand.d.ts +4 -3
- package/dist/src/commands/EvalCommand.js +85 -52
- package/dist/src/commands/IndexCommand.js +0 -22
- package/dist/src/commands/McpCommand.js +2 -8
- package/dist/src/commands/WebCommand.js +2 -5
- package/dist/src/config-error.d.ts +0 -4
- package/dist/src/config-error.js +0 -4
- package/dist/src/config-store.d.ts +1 -54
- package/dist/src/config-store.js +8 -69
- package/dist/src/config.d.ts +8 -17
- package/dist/src/config.js +35 -34
- package/dist/src/constants.d.ts +2 -28
- package/dist/src/constants.js +6 -28
- package/dist/src/container.d.ts +6 -0
- package/dist/src/container.js +12 -3
- package/dist/src/db/schema.d.ts +17 -0
- package/dist/src/db/schema.js +1 -6
- package/dist/src/declarations/DeclarationTree.d.ts +19 -0
- package/dist/src/declarations/DeclarationTree.js +66 -0
- package/dist/src/declarations/DeclarationTreeBuilder.d.ts +22 -0
- package/dist/src/declarations/DeclarationTreeBuilder.js +179 -0
- package/dist/src/declarations/SourceLines.d.ts +37 -0
- package/dist/src/declarations/SourceLines.js +415 -0
- package/dist/src/dependency-traversal.d.ts +0 -6
- package/dist/src/dependency-traversal.js +0 -6
- package/dist/src/enriched-prompt.d.ts +0 -6
- package/dist/src/enriched-prompt.js +0 -6
- package/dist/src/error-record.d.ts +12 -0
- package/dist/src/error-record.js +248 -0
- package/dist/src/eval-corpus.d.ts +16 -0
- package/dist/src/eval-corpus.js +169 -0
- package/dist/src/eval-results.d.ts +6 -3
- package/dist/src/eval-results.js +309 -26
- package/dist/src/file-utils.d.ts +0 -5
- package/dist/src/file-utils.js +0 -5
- package/dist/src/graph.d.ts +0 -25
- package/dist/src/graph.js +0 -28
- package/dist/src/hnsw-settings.d.ts +3 -0
- package/dist/src/hnsw-settings.js +24 -0
- package/dist/src/interfaces/IAstParser.d.ts +2 -39
- package/dist/src/interfaces/IChunker.d.ts +3 -12
- package/dist/src/interfaces/ICorpusInspector.d.ts +7 -0
- package/dist/src/interfaces/ICorpusInspector.js +2 -0
- package/dist/src/interfaces/IDatabase.d.ts +2 -22
- package/dist/src/interfaces/IEmbedder.d.ts +0 -9
- package/dist/src/interfaces/ILogger.d.ts +1 -11
- package/dist/src/interfaces/IMcpToolHandler.d.ts +0 -5
- package/dist/src/interfaces/IMonorepoResolver.d.ts +0 -22
- package/dist/src/interfaces/IProjectDiscovery.d.ts +0 -17
- package/dist/src/interfaces/IRetrievalEvaluator.d.ts +134 -12
- package/dist/src/interfaces/ISearcher.d.ts +16 -5
- package/dist/src/interrupt.js +0 -2
- package/dist/src/owner-path.d.ts +9 -10
- package/dist/src/owner-path.js +11 -23
- package/dist/src/project-scope.d.ts +0 -12
- package/dist/src/project-scope.js +0 -12
- package/dist/src/project-selector.d.ts +0 -5
- package/dist/src/project-selector.js +0 -11
- package/dist/src/retrieval-eval.d.ts +14 -6
- package/dist/src/retrieval-eval.js +345 -59
- package/dist/src/search/result-shaping.d.ts +4 -0
- package/dist/src/search/result-shaping.js +19 -0
- package/dist/src/services/AstParserService.d.ts +2 -62
- package/dist/src/services/AstParserService.js +25 -137
- package/dist/src/services/DatabaseService.d.ts +2 -7
- package/dist/src/services/DatabaseService.js +42 -29
- package/dist/src/services/EmbedderFactory.d.ts +0 -4
- package/dist/src/services/EmbedderFactory.js +0 -5
- package/dist/src/services/IndexerService.d.ts +0 -16
- package/dist/src/services/IndexerService.js +16 -45
- package/dist/src/services/InfinityEmbedder.d.ts +0 -21
- package/dist/src/services/InfinityEmbedder.js +0 -15
- package/dist/src/services/LoggerService.d.ts +7 -5
- package/dist/src/services/LoggerService.js +37 -41
- package/dist/src/services/McpServer.d.ts +22 -14
- package/dist/src/services/McpServer.js +70 -37
- package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +1 -45
- package/dist/src/services/MonorepoAwareDiscoveryService.js +1 -51
- package/dist/src/services/NxMonorepoResolver.d.ts +0 -29
- package/dist/src/services/NxMonorepoResolver.js +5 -56
- package/dist/src/services/OpenAICompatibleEmbedder.d.ts +0 -6
- package/dist/src/services/OpenAICompatibleEmbedder.js +0 -14
- package/dist/src/services/ProjectDiscoveryService.d.ts +0 -4
- package/dist/src/services/ProjectDiscoveryService.js +0 -4
- package/dist/src/services/SearcherService.d.ts +9 -116
- package/dist/src/services/SearcherService.js +95 -176
- package/dist/src/services/VoyageEmbedder.d.ts +0 -6
- package/dist/src/services/VoyageEmbedder.js +0 -6
- package/dist/src/services/WebServerService.d.ts +1 -53
- package/dist/src/services/WebServerService.js +11 -57
- package/dist/src/services/http-retry.d.ts +0 -7
- package/dist/src/services/http-retry.js +1 -10
- package/dist/src/services/infinity-provisioning.d.ts +0 -74
- package/dist/src/services/infinity-provisioning.js +5 -88
- package/dist/src/services/legacy-ollama-index.d.ts +0 -16
- package/dist/src/services/legacy-ollama-index.js +0 -23
- package/dist/src/services/reranker.d.ts +0 -9
- package/dist/src/services/reranker.js +46 -12
- package/dist/src/setup.d.ts +11 -23
- package/dist/src/setup.js +98 -71
- package/dist/src/symbol-name.d.ts +0 -12
- package/dist/src/symbol-name.js +0 -13
- package/dist/src/symbol-rows.d.ts +0 -14
- package/dist/src/symbol-rows.js +0 -14
- package/dist/src/tool-call-log.d.ts +2 -0
- package/dist/src/tool-call-log.js +24 -0
- package/dist/src/tools/GetDependenciesTool.js +1 -1
- package/dist/src/tools/GetDependentsTool.js +1 -1
- package/dist/src/tools/SearchCodebaseTool.js +6 -3
- package/dist/src/tools/format-helpers.d.ts +0 -6
- package/dist/src/tools/format-helpers.js +87 -14
- package/dist/src/web/dashboard.d.ts +0 -8
- package/dist/src/web/dashboard.js +7 -10
- package/package.json +2 -2
- package/dist/src/services/AstChunker.d.ts +0 -42
- package/dist/src/services/AstChunker.js +0 -158
- package/docs/web-dashboard.md +0 -206
|
@@ -8,7 +8,6 @@ const MonorepoAwareDiscoveryService_1 = require("../services/MonorepoAwareDiscov
|
|
|
8
8
|
const project_scope_1 = require("../project-scope");
|
|
9
9
|
const file_utils_1 = require("../file-utils");
|
|
10
10
|
const legacy_ollama_index_1 = require("../services/legacy-ollama-index");
|
|
11
|
-
/** `--project`'s index in `args`; the following token is its name unless absent or itself a flag. */
|
|
12
11
|
function parseProjectFlag(args) {
|
|
13
12
|
const i = args.indexOf('--project');
|
|
14
13
|
if (i === -1)
|
|
@@ -38,8 +37,6 @@ class IndexCommand {
|
|
|
38
37
|
process.exitCode = 1;
|
|
39
38
|
return;
|
|
40
39
|
}
|
|
41
|
-
// Resolve --project before any database or embedding work, so a bad name
|
|
42
|
-
// (or a cancelled no-name prompt) has zero side effects.
|
|
43
40
|
let scanTargets;
|
|
44
41
|
if (projectFlag.present) {
|
|
45
42
|
let name = projectFlag.name;
|
|
@@ -65,11 +62,6 @@ class IndexCommand {
|
|
|
65
62
|
scanTargets = targets;
|
|
66
63
|
}
|
|
67
64
|
await db.initDbWithSchema();
|
|
68
|
-
// A former Ollama installation the config layer cannot recognise (old
|
|
69
|
-
// defaults, no `embedding`/`ollama` block) is protected here: without an
|
|
70
|
-
// explicit `--reindex`, refuse to touch an index built by `ollama` (or a
|
|
71
|
-
// pre-`index_meta` install, which is indistinguishable from one) before
|
|
72
|
-
// any backend readiness check, Docker command, or schema work.
|
|
73
65
|
if (!force) {
|
|
74
66
|
let identity;
|
|
75
67
|
try {
|
|
@@ -96,17 +88,9 @@ class IndexCommand {
|
|
|
96
88
|
const selected = await new project_selector_1.ProjectSelector(config.workspaceRoot).select(discovered, forceSelect);
|
|
97
89
|
if (selected.length === 0)
|
|
98
90
|
return;
|
|
99
|
-
// Expand any selected nested-monorepo unit (e.g. an Nx repo) into its
|
|
100
|
-
// sub-projects; ordinary projects pass through unchanged.
|
|
101
91
|
projects = (0, MonorepoAwareDiscoveryService_1.toLeafProjects)(selected);
|
|
102
92
|
}
|
|
103
|
-
// The post-index graph always reflects the full workspace, regardless of
|
|
104
|
-
// what this run actually scanned.
|
|
105
93
|
const fullLeafProjects = (0, MonorepoAwareDiscoveryService_1.toLeafProjects)(discovered);
|
|
106
|
-
// Ensure the local model is served (Infinity only — no-op for cloud), then
|
|
107
|
-
// resolve the active embedding dimension and reconcile it with index_meta
|
|
108
|
-
// (recreate code_chunk on a dimension change, clear rows on a model change)
|
|
109
|
-
// before scanning. A clear error here beats a mid-run vector width mismatch.
|
|
110
94
|
try {
|
|
111
95
|
await embedder.ensureModelAvailable?.();
|
|
112
96
|
const meta = await indexer.prepareEmbeddingSchema();
|
|
@@ -120,8 +104,6 @@ class IndexCommand {
|
|
|
120
104
|
const controller = new AbortController();
|
|
121
105
|
const teardown = (0, interrupt_1.setupInterrupt)(controller);
|
|
122
106
|
try {
|
|
123
|
-
// --project bypasses pruning entirely — it promises "touch only this
|
|
124
|
-
// project," so nothing outside the resolved target(s) is ever removed.
|
|
125
107
|
if (!scanTargets) {
|
|
126
108
|
let pruneResult;
|
|
127
109
|
try {
|
|
@@ -150,11 +132,7 @@ class IndexCommand {
|
|
|
150
132
|
console.log('\nIndexing interrupted.');
|
|
151
133
|
this.printSummary(allStats);
|
|
152
134
|
if (!controller.signal.aborted) {
|
|
153
|
-
// The graph is a post-index nicety — a failure here (DB dropped after a
|
|
154
|
-
// long run, unreadable dir) must not fail the whole run: indexing already
|
|
155
|
-
// succeeded and the summary is printed.
|
|
156
135
|
try {
|
|
157
|
-
// Real import edges (code_dependency, just written) → intra-repo graph links.
|
|
158
136
|
const projectEdges = await searcher.getProjectEdges();
|
|
159
137
|
(0, graph_1.generateProjectGraph)(fullLeafProjects, projectEdges, config.workspaceRoot);
|
|
160
138
|
}
|
|
@@ -11,25 +11,19 @@ class McpCommand {
|
|
|
11
11
|
let startupError;
|
|
12
12
|
try {
|
|
13
13
|
await db.initDb();
|
|
14
|
-
// Refuse to serve searches embedded into a foreign vector space — surface
|
|
15
|
-
// the mismatch through the existing startup-error plumbing so every tool
|
|
16
|
-
// call reports it clearly.
|
|
17
14
|
await searcher.assertIndexMatches();
|
|
18
15
|
}
|
|
19
16
|
catch (err) {
|
|
20
17
|
startupError = err instanceof Error ? err : new Error(String(err));
|
|
21
|
-
logger.error('McpCommand: startup check failed', {
|
|
18
|
+
logger.error('McpCommand: startup check failed', { err: startupError });
|
|
22
19
|
}
|
|
23
20
|
try {
|
|
24
21
|
await mcp.start(startupError);
|
|
25
22
|
}
|
|
26
23
|
catch (err) {
|
|
27
|
-
|
|
28
|
-
logger.error('McpCommand: MCP server failed to start', { reason });
|
|
24
|
+
logger.error('McpCommand: MCP server failed to start', { err });
|
|
29
25
|
return;
|
|
30
26
|
}
|
|
31
|
-
// server.connect() resolves immediately after setup; keep the process alive
|
|
32
|
-
// until the MCP client disconnects (stdin closes)
|
|
33
27
|
await new Promise(resolve => {
|
|
34
28
|
process.stdin.once('close', resolve);
|
|
35
29
|
process.stdin.once('end', resolve);
|
|
@@ -17,9 +17,8 @@ class WebCommand {
|
|
|
17
17
|
}
|
|
18
18
|
catch (err) {
|
|
19
19
|
const reason = err instanceof Error ? err.message : String(err);
|
|
20
|
-
logger.error('WebCommand: database connection failed', {
|
|
20
|
+
logger.error('WebCommand: database connection failed', { err });
|
|
21
21
|
console.error(`Database connection failed: ${reason}`);
|
|
22
|
-
// Continue: config editing still works; project/graph tabs will error in-page.
|
|
23
22
|
}
|
|
24
23
|
const server = new WebServerService_1.WebServer(searcher, config, logger, embedder);
|
|
25
24
|
const host = '127.0.0.1';
|
|
@@ -28,13 +27,11 @@ class WebCommand {
|
|
|
28
27
|
}
|
|
29
28
|
catch (err) {
|
|
30
29
|
const reason = err instanceof Error ? err.message : String(err);
|
|
31
|
-
logger.error('WebCommand: server failed to start', {
|
|
30
|
+
logger.error('WebCommand: server failed to start', { err });
|
|
32
31
|
console.error(`Failed to start web server on ${host}:${this.port}: ${reason}`);
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
35
34
|
console.log(`RAG dashboard: http://${host}:${this.port} (Ctrl+C to stop)`);
|
|
36
|
-
// Keep execute() pending until a shutdown signal, otherwise app.ts tears
|
|
37
|
-
// down the DB and exits immediately (mirrors McpCommand's keep-alive).
|
|
38
35
|
await new Promise(resolve => {
|
|
39
36
|
const shutdown = () => { void server.stop().then(resolve); };
|
|
40
37
|
process.once('SIGINT', shutdown);
|
package/dist/src/config-error.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ConfigError = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Own module (not `config.ts`) so `config-store.ts` can throw it too without
|
|
6
|
-
* an import cycle — `config.ts` imports from `config-store.ts`.
|
|
7
|
-
*/
|
|
8
4
|
class ConfigError extends Error {
|
|
9
5
|
constructor(message) {
|
|
10
6
|
super(message);
|
|
@@ -42,6 +42,7 @@ declare const jsonSchema: z.ZodObject<{
|
|
|
42
42
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
43
43
|
provider: z.ZodOptional<z.ZodEnum<{
|
|
44
44
|
"openai-compatible": "openai-compatible";
|
|
45
|
+
voyage: "voyage";
|
|
45
46
|
}>>;
|
|
46
47
|
baseURL: z.ZodOptional<z.ZodString>;
|
|
47
48
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
@@ -58,24 +59,11 @@ declare const jsonSchema: z.ZodObject<{
|
|
|
58
59
|
warn: "warn";
|
|
59
60
|
}>>;
|
|
60
61
|
dir: z.ZodOptional<z.ZodString>;
|
|
61
|
-
searchMetrics: z.ZodOptional<z.ZodBoolean>;
|
|
62
62
|
}, z.core.$strip>>;
|
|
63
63
|
workspaceRoot: z.ZodOptional<z.ZodString>;
|
|
64
64
|
}, z.core.$strip>;
|
|
65
65
|
export type JsonConfig = z.infer<typeof jsonSchema>;
|
|
66
66
|
export declare const ALL_KEYS: string[];
|
|
67
|
-
/**
|
|
68
|
-
* Single source of truth for built-in defaults, keyed by raw config.json dotted
|
|
69
|
-
* key. Consumed both by the web dashboard (`GET /api/defaults`) and by
|
|
70
|
-
* `resolvedSchema` in `src/config.ts`, whose `.default(...)` calls reference
|
|
71
|
-
* these values — so the runtime default and the dashboard-displayed default
|
|
72
|
-
* cannot diverge. `as const` keeps the literal types so those `.default()`
|
|
73
|
-
* lookups stay type-safe. Keys omitted here have no static default: required
|
|
74
|
-
* fields (db.host/user/password) and values derived at runtime — db.name →
|
|
75
|
-
* rag_<user>, and the provider-dependent URLs (embedding.baseURL,
|
|
76
|
-
* reranker.baseURL, reranker.url), which are computed in the `Config`
|
|
77
|
-
* constructor per provider rather than through a schema `.default(...)`.
|
|
78
|
-
*/
|
|
79
67
|
export declare const CONFIG_DEFAULTS: {
|
|
80
68
|
readonly 'db.port': 5433;
|
|
81
69
|
readonly 'embedding.provider': "infinity";
|
|
@@ -97,54 +85,13 @@ export declare const CONFIG_DEFAULTS: {
|
|
|
97
85
|
readonly 'reranker.overfetch': 5;
|
|
98
86
|
readonly 'reranker.candidateFloor': 80;
|
|
99
87
|
readonly 'logging.level': "error";
|
|
100
|
-
readonly 'logging.searchMetrics': false;
|
|
101
88
|
};
|
|
102
89
|
export declare function readConfigJson(): JsonConfig | null;
|
|
103
|
-
/**
|
|
104
|
-
* Detect a raw (pre-migration) config object that still selects the removed
|
|
105
|
-
* `ollama` embedding provider — explicitly, via a legacy top-level `ollama`
|
|
106
|
-
* block, or implicitly (no `provider` set, but `baseURL`/`model` point at
|
|
107
|
-
* Ollama's defaults) — and throw a `ConfigError` with migration guidance
|
|
108
|
-
* instead of letting it validate under a silently reinterpreted provider.
|
|
109
|
-
* Runs before `migrateRawConfig` so a legacy `ollama.*` block is never
|
|
110
|
-
* converted into an `embedding` block first. Pure — never mutates `raw`.
|
|
111
|
-
*/
|
|
112
90
|
export declare function detectFormerOllamaConfig(raw: unknown): void;
|
|
113
|
-
/**
|
|
114
|
-
* Migrate a legacy raw config object to the current shape, in place of a
|
|
115
|
-
* versioned migration system. Pure — returns a new object, never mutates input.
|
|
116
|
-
*
|
|
117
|
-
* - Removed translation keys (`search.autoTranslate`, `search.translateModel`)
|
|
118
|
-
* are dropped so they neither validate-fail nor take effect.
|
|
119
|
-
* - Legacy `reranker.provider: 'ollama'` (a dead value — Ollama has no rerank
|
|
120
|
-
* API) is rewritten to `openai-compatible`, and a stale `/api/rerank` URL is
|
|
121
|
-
* dropped so it re-derives to `/rerank`. Otherwise the tightened enum would
|
|
122
|
-
* fail validation on existing configs and lock the user out.
|
|
123
|
-
*
|
|
124
|
-
* A legacy top-level `ollama` block reaches here only when `detectFormerOllamaConfig`
|
|
125
|
-
* did not already reject it (i.e. an `embedding` block is also present); it is
|
|
126
|
-
* dropped rather than merged. Exported for unit testing.
|
|
127
|
-
*/
|
|
128
91
|
export declare function migrateRawConfig(raw: unknown): unknown;
|
|
129
|
-
/**
|
|
130
|
-
* Validate a raw (pre-migration) config object against the Zod schema: first
|
|
131
|
-
* `detectFormerOllamaConfig` (a `ConfigError` on a former-Ollama config takes
|
|
132
|
-
* priority over both migration and the regular Zod enum rejection), then
|
|
133
|
-
* `migrateRawConfig`, then the schema itself. Unknown keys (e.g. the "$schema"
|
|
134
|
-
* editor reference) are stripped. Throws with a caller-friendly message on
|
|
135
|
-
* validation failure. The single entry point for both `readConfigJson` and
|
|
136
|
-
* `PUT /api/config`, so both apply identical rules with an identical message.
|
|
137
|
-
* Pure — no file I/O.
|
|
138
|
-
*/
|
|
139
92
|
export declare function parseConfig(raw: unknown, source?: string): JsonConfig;
|
|
140
|
-
/** Build the JSON Schema object generated from the Zod config schema. Pure. */
|
|
141
93
|
export declare function buildJsonSchema(): Record<string, unknown>;
|
|
142
94
|
export declare function writeConfigJson(data: JsonConfig): void;
|
|
143
|
-
/**
|
|
144
|
-
* Generate a JSON Schema from the Zod config schema and write it next to
|
|
145
|
-
* config.json. Editors resolve it via the "$schema" reference and provide
|
|
146
|
-
* key autocomplete, type hints, and inline validation while editing.
|
|
147
|
-
*/
|
|
148
95
|
export declare function writeSchemaFile(): void;
|
|
149
96
|
export declare function getNestedValue(cfg: JsonConfig, key: string): unknown;
|
|
150
97
|
export declare function setNestedValue(cfg: JsonConfig, key: string, raw: string): JsonConfig;
|
package/dist/src/config-store.js
CHANGED
|
@@ -54,11 +54,7 @@ exports.CONFIG_DIR = path.join(os.homedir(), '.config', 'codesearch-mcp');
|
|
|
54
54
|
exports.CONFIG_FILE = path.join(exports.CONFIG_DIR, 'config.json');
|
|
55
55
|
exports.SCHEMA_FILE = path.join(exports.CONFIG_DIR, 'config.schema.json');
|
|
56
56
|
exports.ENV_FILE = path.join(exports.CONFIG_DIR, '.env');
|
|
57
|
-
// Relative $schema reference written into config.json so editors resolve the
|
|
58
|
-
// sibling schema file for autocomplete / inline validation.
|
|
59
57
|
const SCHEMA_REF = './config.schema.json';
|
|
60
|
-
// All fields optional — defaults live in config.ts. The file stays minimal.
|
|
61
|
-
// Descriptions surface as tooltips in schema-aware editors (VS Code, JetBrains).
|
|
62
58
|
const jsonSchema = zod_1.z.object({
|
|
63
59
|
db: zod_1.z.object({
|
|
64
60
|
host: zod_1.z.string().describe('PostgreSQL host').optional(),
|
|
@@ -88,11 +84,11 @@ const jsonSchema = zod_1.z.object({
|
|
|
88
84
|
maxLimit: zod_1.z.number().int().describe('Hard cap on results (default 20)').optional(),
|
|
89
85
|
}).optional(),
|
|
90
86
|
reranker: zod_1.z.object({
|
|
91
|
-
enabled: zod_1.z.boolean().describe('Enable cross-encoder reranking
|
|
92
|
-
provider: zod_1.z.enum(['openai-compatible']).describe('Rerank backend
|
|
93
|
-
baseURL: zod_1.z.string().describe('Rerank server base URL (
|
|
94
|
-
apiKey: zod_1.z.string().describe('API key, or env:VAR_NAME. Empty for local servers.').optional(),
|
|
95
|
-
model: zod_1.z.string().describe('Reranker model name (
|
|
87
|
+
enabled: zod_1.z.boolean().describe('Enable cross-encoder reranking (default true with infinity embedding or when reranker.provider/baseURL/url is set, else false)').optional(),
|
|
88
|
+
provider: zod_1.z.enum(['openai-compatible', 'voyage']).describe('Rerank backend: openai-compatible /rerank servers (Infinity, vLLM) or voyage (Voyage AI, requires reranker.apiKey) (default openai-compatible)').optional(),
|
|
89
|
+
baseURL: zod_1.z.string().describe('Rerank server base URL (default embedding.baseURL for openai-compatible — Infinity serves both; https://api.voyageai.com/v1 for voyage)').optional(),
|
|
90
|
+
apiKey: zod_1.z.string().describe('API key, or env:VAR_NAME. Empty for local servers; required for voyage.').optional(),
|
|
91
|
+
model: zod_1.z.string().describe('Reranker model name (default mixedbread-ai/mxbai-rerank-xsmall-v1 for openai-compatible, rerank-3 for voyage)').optional(),
|
|
96
92
|
url: zod_1.z.string().describe('Full URL of the rerank endpoint (default derived from provider + baseURL)').optional(),
|
|
97
93
|
overfetch: zod_1.z.number().int().min(1).max(20).describe('Candidate multiplier before reranking (limit × N, default 5)').optional(),
|
|
98
94
|
candidateFloor: zod_1.z.number().int().min(1).describe('Minimum candidate pool fed to the reranker regardless of limit; fetchLimit = min(max(limit × overfetch, candidateFloor), 200) (default 80)').optional(),
|
|
@@ -100,7 +96,6 @@ const jsonSchema = zod_1.z.object({
|
|
|
100
96
|
logging: zod_1.z.object({
|
|
101
97
|
level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).describe('File log level (default error)').optional(),
|
|
102
98
|
dir: zod_1.z.string().describe('Base log directory; "false" to disable file logging').optional(),
|
|
103
|
-
searchMetrics: zod_1.z.boolean().describe('Emit one structured search-tuning line (info level) per search, independent of level (default false)').optional(),
|
|
104
99
|
}).optional(),
|
|
105
100
|
workspaceRoot: zod_1.z.string().describe('Full path to the directory containing all your projects').optional(),
|
|
106
101
|
});
|
|
@@ -134,22 +129,9 @@ const FIELD_TYPES = {
|
|
|
134
129
|
'reranker.candidateFloor': 'number',
|
|
135
130
|
'logging.level': 'string',
|
|
136
131
|
'logging.dir': 'string',
|
|
137
|
-
'logging.searchMetrics': 'boolean',
|
|
138
132
|
'workspaceRoot': 'string',
|
|
139
133
|
};
|
|
140
134
|
exports.ALL_KEYS = Object.keys(FIELD_TYPES);
|
|
141
|
-
/**
|
|
142
|
-
* Single source of truth for built-in defaults, keyed by raw config.json dotted
|
|
143
|
-
* key. Consumed both by the web dashboard (`GET /api/defaults`) and by
|
|
144
|
-
* `resolvedSchema` in `src/config.ts`, whose `.default(...)` calls reference
|
|
145
|
-
* these values — so the runtime default and the dashboard-displayed default
|
|
146
|
-
* cannot diverge. `as const` keeps the literal types so those `.default()`
|
|
147
|
-
* lookups stay type-safe. Keys omitted here have no static default: required
|
|
148
|
-
* fields (db.host/user/password) and values derived at runtime — db.name →
|
|
149
|
-
* rag_<user>, and the provider-dependent URLs (embedding.baseURL,
|
|
150
|
-
* reranker.baseURL, reranker.url), which are computed in the `Config`
|
|
151
|
-
* constructor per provider rather than through a schema `.default(...)`.
|
|
152
|
-
*/
|
|
153
135
|
exports.CONFIG_DEFAULTS = {
|
|
154
136
|
'db.port': 5433,
|
|
155
137
|
'embedding.provider': 'infinity',
|
|
@@ -171,7 +153,6 @@ exports.CONFIG_DEFAULTS = {
|
|
|
171
153
|
'reranker.overfetch': 5,
|
|
172
154
|
'reranker.candidateFloor': 80,
|
|
173
155
|
'logging.level': 'error',
|
|
174
|
-
'logging.searchMetrics': false,
|
|
175
156
|
};
|
|
176
157
|
function readConfigJson() {
|
|
177
158
|
if (!fs.existsSync(exports.CONFIG_FILE))
|
|
@@ -181,7 +162,7 @@ function readConfigJson() {
|
|
|
181
162
|
raw = fs.readFileSync(exports.CONFIG_FILE, 'utf-8');
|
|
182
163
|
}
|
|
183
164
|
catch (e) {
|
|
184
|
-
throw new Error(`Cannot read ${exports.CONFIG_FILE}: ${e.message}
|
|
165
|
+
throw new Error(`Cannot read ${exports.CONFIG_FILE}: ${e.message}`, { cause: e });
|
|
185
166
|
}
|
|
186
167
|
let parsed;
|
|
187
168
|
try {
|
|
@@ -189,7 +170,7 @@ function readConfigJson() {
|
|
|
189
170
|
}
|
|
190
171
|
catch (e) {
|
|
191
172
|
throw new Error(`Invalid JSON in ${exports.CONFIG_FILE}: ${e.message}\n` +
|
|
192
|
-
`Fix the file manually or delete it and run: rag-setup
|
|
173
|
+
`Fix the file manually or delete it and run: rag-setup`, { cause: e });
|
|
193
174
|
}
|
|
194
175
|
return parseConfig(parsed, exports.CONFIG_FILE);
|
|
195
176
|
}
|
|
@@ -198,19 +179,9 @@ const OLLAMA_MIGRATION_REMEDY = 'The "ollama" embedding provider has been remove
|
|
|
198
179
|
'and embedding.baseURL to "http://localhost:11434/v1" — via `rag-config edit` (or by ' +
|
|
199
180
|
'editing the config file directly) or by re-running `rag-setup`. After switching, rebuild ' +
|
|
200
181
|
'the index with `rag-index --reindex`.';
|
|
201
|
-
/** Strip a trailing `:tag` (Docker-style) from a model name, if present. */
|
|
202
182
|
function stripModelTag(model) {
|
|
203
183
|
return model.replace(/:[^:]*$/, '');
|
|
204
184
|
}
|
|
205
|
-
/**
|
|
206
|
-
* Detect a raw (pre-migration) config object that still selects the removed
|
|
207
|
-
* `ollama` embedding provider — explicitly, via a legacy top-level `ollama`
|
|
208
|
-
* block, or implicitly (no `provider` set, but `baseURL`/`model` point at
|
|
209
|
-
* Ollama's defaults) — and throw a `ConfigError` with migration guidance
|
|
210
|
-
* instead of letting it validate under a silently reinterpreted provider.
|
|
211
|
-
* Runs before `migrateRawConfig` so a legacy `ollama.*` block is never
|
|
212
|
-
* converted into an `embedding` block first. Pure — never mutates `raw`.
|
|
213
|
-
*/
|
|
214
185
|
function detectFormerOllamaConfig(raw) {
|
|
215
186
|
if (raw == null || typeof raw !== 'object')
|
|
216
187
|
return;
|
|
@@ -227,7 +198,7 @@ function detectFormerOllamaConfig(raw) {
|
|
|
227
198
|
try {
|
|
228
199
|
port = new URL(e.baseURL).port;
|
|
229
200
|
}
|
|
230
|
-
catch {
|
|
201
|
+
catch { }
|
|
231
202
|
if (port === '11434') {
|
|
232
203
|
throw new config_error_1.ConfigError(`Config's embedding.baseURL targets Ollama's default port (11434) with no embedding.provider set. ${OLLAMA_MIGRATION_REMEDY}`);
|
|
233
204
|
}
|
|
@@ -242,21 +213,6 @@ function detectFormerOllamaConfig(raw) {
|
|
|
242
213
|
throw new config_error_1.ConfigError(`Config has a legacy "ollama" configuration block. ${OLLAMA_MIGRATION_REMEDY}`);
|
|
243
214
|
}
|
|
244
215
|
}
|
|
245
|
-
/**
|
|
246
|
-
* Migrate a legacy raw config object to the current shape, in place of a
|
|
247
|
-
* versioned migration system. Pure — returns a new object, never mutates input.
|
|
248
|
-
*
|
|
249
|
-
* - Removed translation keys (`search.autoTranslate`, `search.translateModel`)
|
|
250
|
-
* are dropped so they neither validate-fail nor take effect.
|
|
251
|
-
* - Legacy `reranker.provider: 'ollama'` (a dead value — Ollama has no rerank
|
|
252
|
-
* API) is rewritten to `openai-compatible`, and a stale `/api/rerank` URL is
|
|
253
|
-
* dropped so it re-derives to `/rerank`. Otherwise the tightened enum would
|
|
254
|
-
* fail validation on existing configs and lock the user out.
|
|
255
|
-
*
|
|
256
|
-
* A legacy top-level `ollama` block reaches here only when `detectFormerOllamaConfig`
|
|
257
|
-
* did not already reject it (i.e. an `embedding` block is also present); it is
|
|
258
|
-
* dropped rather than merged. Exported for unit testing.
|
|
259
|
-
*/
|
|
260
216
|
function migrateRawConfig(raw) {
|
|
261
217
|
if (raw == null || typeof raw !== 'object')
|
|
262
218
|
return raw;
|
|
@@ -274,16 +230,6 @@ function migrateRawConfig(raw) {
|
|
|
274
230
|
}
|
|
275
231
|
return obj;
|
|
276
232
|
}
|
|
277
|
-
/**
|
|
278
|
-
* Validate a raw (pre-migration) config object against the Zod schema: first
|
|
279
|
-
* `detectFormerOllamaConfig` (a `ConfigError` on a former-Ollama config takes
|
|
280
|
-
* priority over both migration and the regular Zod enum rejection), then
|
|
281
|
-
* `migrateRawConfig`, then the schema itself. Unknown keys (e.g. the "$schema"
|
|
282
|
-
* editor reference) are stripped. Throws with a caller-friendly message on
|
|
283
|
-
* validation failure. The single entry point for both `readConfigJson` and
|
|
284
|
-
* `PUT /api/config`, so both apply identical rules with an identical message.
|
|
285
|
-
* Pure — no file I/O.
|
|
286
|
-
*/
|
|
287
233
|
function parseConfig(raw, source = exports.CONFIG_FILE) {
|
|
288
234
|
detectFormerOllamaConfig(raw);
|
|
289
235
|
const migrated = migrateRawConfig(raw);
|
|
@@ -295,7 +241,6 @@ function parseConfig(raw, source = exports.CONFIG_FILE) {
|
|
|
295
241
|
}
|
|
296
242
|
return result.data;
|
|
297
243
|
}
|
|
298
|
-
/** Build the JSON Schema object generated from the Zod config schema. Pure. */
|
|
299
244
|
function buildJsonSchema() {
|
|
300
245
|
return {
|
|
301
246
|
$id: 'https://github.com/oleksandr-antoniuk/RAG/config.schema.json',
|
|
@@ -306,15 +251,9 @@ function buildJsonSchema() {
|
|
|
306
251
|
function writeConfigJson(data) {
|
|
307
252
|
fs.mkdirSync(exports.CONFIG_DIR, { recursive: true });
|
|
308
253
|
writeSchemaFile();
|
|
309
|
-
// $schema first so editors pick it up; readConfigJson strips it on validation.
|
|
310
254
|
const withSchema = { $schema: SCHEMA_REF, ...data };
|
|
311
255
|
fs.writeFileSync(exports.CONFIG_FILE, JSON.stringify(withSchema, null, 2) + '\n', { mode: 0o600 });
|
|
312
256
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Generate a JSON Schema from the Zod config schema and write it next to
|
|
315
|
-
* config.json. Editors resolve it via the "$schema" reference and provide
|
|
316
|
-
* key autocomplete, type hints, and inline validation while editing.
|
|
317
|
-
*/
|
|
318
257
|
function writeSchemaFile() {
|
|
319
258
|
fs.mkdirSync(exports.CONFIG_DIR, { recursive: true });
|
|
320
259
|
fs.writeFileSync(exports.SCHEMA_FILE, JSON.stringify(buildJsonSchema(), null, 2) + '\n');
|
package/dist/src/config.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import { type JsonConfig } from './config-store';
|
|
1
2
|
import { ConfigError } from './config-error';
|
|
2
3
|
export { ConfigError };
|
|
3
|
-
/**
|
|
4
|
-
* Resolve a config value that may reference an environment variable. A value of
|
|
5
|
-
* the form `env:VAR_NAME` resolves to `process.env.VAR_NAME`; any other string
|
|
6
|
-
* (including empty) is returned verbatim. A referenced-but-unset variable throws
|
|
7
|
-
* a clear error naming the variable and the `.env` file. Pure aside from the
|
|
8
|
-
* injected env map. See `src/app.ts` for how `.env` is loaded into the process.
|
|
9
|
-
*/
|
|
10
4
|
export declare function resolveEnvRef(value: string, env?: NodeJS.ProcessEnv): string;
|
|
5
|
+
export declare function rerankContext(raw: JsonConfig): {
|
|
6
|
+
wanted: boolean;
|
|
7
|
+
missingKey: boolean;
|
|
8
|
+
model: string;
|
|
9
|
+
};
|
|
11
10
|
export declare class Config {
|
|
12
11
|
private readonly data;
|
|
13
12
|
get db(): {
|
|
@@ -17,13 +16,6 @@ export declare class Config {
|
|
|
17
16
|
password: string;
|
|
18
17
|
database: string;
|
|
19
18
|
};
|
|
20
|
-
/**
|
|
21
|
-
* Embedding config. `apiKey` is returned **raw** (possibly an `env:VAR` ref) —
|
|
22
|
-
* callers resolve it lazily via `resolveEnvRef` at the moment they build a
|
|
23
|
-
* request, so an unset referenced variable fails only when the key is actually
|
|
24
|
-
* needed (during embed/search) rather than crashing container construction and
|
|
25
|
-
* locking the user out of `rag-web`/`rag-mcp`.
|
|
26
|
-
*/
|
|
27
19
|
get embedding(): {
|
|
28
20
|
provider: "openai-compatible" | "voyage" | "infinity";
|
|
29
21
|
baseURL: string;
|
|
@@ -32,14 +24,14 @@ export declare class Config {
|
|
|
32
24
|
dimensions: number | null;
|
|
33
25
|
batchSize: number;
|
|
34
26
|
};
|
|
35
|
-
/** Reranker config. `apiKey` is raw — resolve lazily with `resolveEnvRef`. */
|
|
36
27
|
get reranker(): {
|
|
37
28
|
enabled: boolean;
|
|
38
|
-
provider: "openai-compatible";
|
|
29
|
+
provider: "openai-compatible" | "voyage";
|
|
39
30
|
baseURL: string;
|
|
40
31
|
apiKey: string;
|
|
41
32
|
model: string;
|
|
42
33
|
url: string;
|
|
34
|
+
missingApiKey: boolean;
|
|
43
35
|
overfetch: number;
|
|
44
36
|
candidateFloor: number;
|
|
45
37
|
};
|
|
@@ -57,7 +49,6 @@ export declare class Config {
|
|
|
57
49
|
};
|
|
58
50
|
get logging(): {
|
|
59
51
|
level: "error" | "debug" | "info" | "warn";
|
|
60
|
-
searchMetrics: boolean;
|
|
61
52
|
dir?: string | undefined;
|
|
62
53
|
};
|
|
63
54
|
get workspaceRoot(): string;
|
package/dist/src/config.js
CHANGED
|
@@ -35,18 +35,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Config = exports.ConfigError = void 0;
|
|
37
37
|
exports.resolveEnvRef = resolveEnvRef;
|
|
38
|
+
exports.rerankContext = rerankContext;
|
|
38
39
|
const os = __importStar(require("os"));
|
|
39
40
|
const zod_1 = require("zod");
|
|
40
41
|
const config_store_1 = require("./config-store");
|
|
42
|
+
const constants_1 = require("./constants");
|
|
41
43
|
const config_error_1 = require("./config-error");
|
|
42
44
|
Object.defineProperty(exports, "ConfigError", { enumerable: true, get: function () { return config_error_1.ConfigError; } });
|
|
43
|
-
/**
|
|
44
|
-
* Resolve a config value that may reference an environment variable. A value of
|
|
45
|
-
* the form `env:VAR_NAME` resolves to `process.env.VAR_NAME`; any other string
|
|
46
|
-
* (including empty) is returned verbatim. A referenced-but-unset variable throws
|
|
47
|
-
* a clear error naming the variable and the `.env` file. Pure aside from the
|
|
48
|
-
* injected env map. See `src/app.ts` for how `.env` is loaded into the process.
|
|
49
|
-
*/
|
|
50
45
|
function resolveEnvRef(value, env = process.env) {
|
|
51
46
|
if (!value.startsWith('env:'))
|
|
52
47
|
return value;
|
|
@@ -66,12 +61,19 @@ const DEFAULT_BASE_URLS = {
|
|
|
66
61
|
function defaultBaseURL(provider) {
|
|
67
62
|
return DEFAULT_BASE_URLS[provider] ?? DEFAULT_BASE_URLS.infinity;
|
|
68
63
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
function rerankContext(raw) {
|
|
65
|
+
const rr = raw.reranker;
|
|
66
|
+
const embProvider = raw.embedding?.provider ?? config_store_1.CONFIG_DEFAULTS['embedding.provider'];
|
|
67
|
+
const isVoyage = (rr?.provider ?? config_store_1.CONFIG_DEFAULTS['reranker.provider']) === 'voyage';
|
|
68
|
+
return {
|
|
69
|
+
wanted: embProvider === 'infinity' || rr?.provider != null || rr?.baseURL != null || rr?.url != null,
|
|
70
|
+
missingKey: isVoyage && !rr?.apiKey,
|
|
71
|
+
model: isVoyage ? constants_1.VOYAGE_RERANK_MODELS[0] : config_store_1.CONFIG_DEFAULTS['reranker.model'],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function deriveRerankUrl(provider, baseURL) {
|
|
75
|
+
const trimmed = baseURL.replace(/\/+$/, '');
|
|
76
|
+
const root = provider === 'voyage' ? trimmed : trimmed.replace(/\/(embeddings|v1)$/, '');
|
|
75
77
|
return `${root}/rerank`;
|
|
76
78
|
}
|
|
77
79
|
const resolvedSchema = zod_1.z.object({
|
|
@@ -91,12 +93,13 @@ const resolvedSchema = zod_1.z.object({
|
|
|
91
93
|
batchSize: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['embedding.batchSize']),
|
|
92
94
|
}),
|
|
93
95
|
reranker: zod_1.z.object({
|
|
94
|
-
enabled: zod_1.z.boolean()
|
|
95
|
-
provider: zod_1.z.enum(['openai-compatible']).default(config_store_1.CONFIG_DEFAULTS['reranker.provider']),
|
|
96
|
+
enabled: zod_1.z.boolean(),
|
|
97
|
+
provider: zod_1.z.enum(['openai-compatible', 'voyage']).default(config_store_1.CONFIG_DEFAULTS['reranker.provider']),
|
|
96
98
|
baseURL: zod_1.z.string().min(1),
|
|
97
99
|
apiKey: zod_1.z.string().default(config_store_1.CONFIG_DEFAULTS['reranker.apiKey']),
|
|
98
|
-
model: zod_1.z.string()
|
|
100
|
+
model: zod_1.z.string(),
|
|
99
101
|
url: zod_1.z.string().min(1),
|
|
102
|
+
missingApiKey: zod_1.z.boolean(),
|
|
100
103
|
overfetch: zod_1.z.number().int().min(1).max(20).default(config_store_1.CONFIG_DEFAULTS['reranker.overfetch']),
|
|
101
104
|
candidateFloor: zod_1.z.number().int().min(1).default(config_store_1.CONFIG_DEFAULTS['reranker.candidateFloor']),
|
|
102
105
|
}),
|
|
@@ -115,22 +118,13 @@ const resolvedSchema = zod_1.z.object({
|
|
|
115
118
|
logging: zod_1.z.object({
|
|
116
119
|
level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).default(config_store_1.CONFIG_DEFAULTS['logging.level']),
|
|
117
120
|
dir: zod_1.z.string().optional(),
|
|
118
|
-
searchMetrics: zod_1.z.boolean().default(config_store_1.CONFIG_DEFAULTS['logging.searchMetrics']),
|
|
119
121
|
}),
|
|
120
122
|
workspaceRoot: zod_1.z.string().default(''),
|
|
121
123
|
});
|
|
122
124
|
class Config {
|
|
123
125
|
data;
|
|
124
126
|
get db() { return this.data.db; }
|
|
125
|
-
/**
|
|
126
|
-
* Embedding config. `apiKey` is returned **raw** (possibly an `env:VAR` ref) —
|
|
127
|
-
* callers resolve it lazily via `resolveEnvRef` at the moment they build a
|
|
128
|
-
* request, so an unset referenced variable fails only when the key is actually
|
|
129
|
-
* needed (during embed/search) rather than crashing container construction and
|
|
130
|
-
* locking the user out of `rag-web`/`rag-mcp`.
|
|
131
|
-
*/
|
|
132
127
|
get embedding() { return this.data.embedding; }
|
|
133
|
-
/** Reranker config. `apiKey` is raw — resolve lazily with `resolveEnvRef`. */
|
|
134
128
|
get reranker() { return this.data.reranker; }
|
|
135
129
|
get indexer() { return this.data.indexer; }
|
|
136
130
|
get search() { return this.data.search; }
|
|
@@ -142,8 +136,15 @@ class Config {
|
|
|
142
136
|
throw new config_error_1.ConfigError('Config not found. Run rag-setup.');
|
|
143
137
|
const embProvider = (raw.embedding?.provider ?? config_store_1.CONFIG_DEFAULTS['embedding.provider']);
|
|
144
138
|
const embBaseURL = raw.embedding?.baseURL ?? defaultBaseURL(embProvider);
|
|
145
|
-
const
|
|
146
|
-
const
|
|
139
|
+
const rr = raw.reranker;
|
|
140
|
+
const rerankProvider = (rr?.provider ?? config_store_1.CONFIG_DEFAULTS['reranker.provider']);
|
|
141
|
+
const isVoyageRerank = rerankProvider === 'voyage';
|
|
142
|
+
const rerankBaseURL = rr?.baseURL ?? (isVoyageRerank ? DEFAULT_BASE_URLS.voyage : embBaseURL);
|
|
143
|
+
const rerankUrl = rr?.url ?? deriveRerankUrl(rerankProvider, rerankBaseURL);
|
|
144
|
+
const rerankCtx = rerankContext(raw);
|
|
145
|
+
const rerankModel = rr?.model ?? rerankCtx.model;
|
|
146
|
+
const rerankWanted = rr?.enabled ?? rerankCtx.wanted;
|
|
147
|
+
const missingApiKey = rerankWanted && rerankCtx.missingKey;
|
|
147
148
|
const result = resolvedSchema.safeParse({
|
|
148
149
|
db: {
|
|
149
150
|
host: raw.db?.host,
|
|
@@ -161,14 +162,15 @@ class Config {
|
|
|
161
162
|
batchSize: raw.embedding?.batchSize,
|
|
162
163
|
},
|
|
163
164
|
reranker: {
|
|
164
|
-
enabled:
|
|
165
|
-
provider:
|
|
165
|
+
enabled: rerankWanted && !missingApiKey,
|
|
166
|
+
provider: rr?.provider,
|
|
166
167
|
baseURL: rerankBaseURL,
|
|
167
|
-
apiKey:
|
|
168
|
-
model:
|
|
168
|
+
apiKey: rr?.apiKey,
|
|
169
|
+
model: rerankModel,
|
|
169
170
|
url: rerankUrl,
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
missingApiKey,
|
|
172
|
+
overfetch: rr?.overfetch,
|
|
173
|
+
candidateFloor: rr?.candidateFloor,
|
|
172
174
|
},
|
|
173
175
|
indexer: {
|
|
174
176
|
chunkSize: raw.indexing?.chunkSize,
|
|
@@ -185,7 +187,6 @@ class Config {
|
|
|
185
187
|
logging: {
|
|
186
188
|
level: raw.logging?.level,
|
|
187
189
|
dir: raw.logging?.dir,
|
|
188
|
-
searchMetrics: raw.logging?.searchMetrics,
|
|
189
190
|
},
|
|
190
191
|
workspaceRoot: raw.workspaceRoot,
|
|
191
192
|
});
|
package/dist/src/constants.d.ts
CHANGED
|
@@ -1,42 +1,16 @@
|
|
|
1
1
|
export type EmbeddingProvider = 'openai-compatible' | 'voyage' | 'infinity';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* must not exceed this; larger cloud models must reduce via the Matryoshka
|
|
5
|
-
* `dimensions` request parameter (`embedding.dimensions`).
|
|
6
|
-
*/
|
|
2
|
+
export type RerankProvider = 'openai-compatible' | 'voyage';
|
|
3
|
+
export declare const VOYAGE_RERANK_MODELS: readonly ["rerank-3", "rerank-3-lite"];
|
|
7
4
|
export declare const MAX_EMBED_DIM = 2000;
|
|
8
5
|
export interface ModelInfo {
|
|
9
6
|
provider: EmbeddingProvider;
|
|
10
|
-
/** Native / default output width. */
|
|
11
7
|
dimensions: number;
|
|
12
|
-
/** Model supports Matryoshka dimension reduction via a request parameter. */
|
|
13
8
|
dimConfigurable?: boolean;
|
|
14
|
-
/** Max width that can be indexed under the pgvector cap (advisory). */
|
|
15
9
|
maxIndexable?: number;
|
|
16
|
-
/** Max width the model itself can emit (advisory). */
|
|
17
10
|
maxDim?: number;
|
|
18
11
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Advisory (NOT exhaustive) registry of known embedding models. Used to resolve
|
|
21
|
-
* a model's dimension without a runtime probe and to suggest models in the
|
|
22
|
-
* dashboard. Models absent here still work — their dimension is probed at index
|
|
23
|
-
* time (see `resolveDimension`).
|
|
24
|
-
*/
|
|
25
12
|
export declare const MODEL_REGISTRY: Record<string, ModelInfo>;
|
|
26
13
|
export declare const DEFAULT_EMBED_MODEL = "jinaai/jina-embeddings-v2-base-code";
|
|
27
|
-
/**
|
|
28
|
-
* Former Ollama registry model names, kept only so config validation can
|
|
29
|
-
* recognise an implicit (provider-less) config that still targets Ollama —
|
|
30
|
-
* see `detectFormerOllamaConfig` in `config-store.ts`.
|
|
31
|
-
*/
|
|
32
14
|
export declare const FORMER_OLLAMA_MODELS: readonly ["bge-m3", "nomic-embed-text", "mxbai-embed-large"];
|
|
33
|
-
/** Advisory dimension for a model, or `undefined` when the model is unknown. */
|
|
34
15
|
export declare function registryDimension(model: string): number | undefined;
|
|
35
|
-
/**
|
|
36
|
-
* Resolve the effective vector dimension: explicit override → registry → probe.
|
|
37
|
-
* Validates the result is ≤ MAX_EMBED_DIM, throwing an error that names
|
|
38
|
-
* `embedding.dimensions` as the fix. `probeFn` embeds a short string once and
|
|
39
|
-
* returns the observed length; only called when neither override nor registry
|
|
40
|
-
* yields a value. Pure aside from the injected probe.
|
|
41
|
-
*/
|
|
42
16
|
export declare function resolveDimension(override: number | null | undefined, model: string, probeFn: () => Promise<number>, registry?: Record<string, ModelInfo>): Promise<number>;
|