vemora 0.1.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +851 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +682 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ask.d.ts +14 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +137 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/audit.d.ts +17 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +398 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/brief.d.ts +16 -0
- package/dist/commands/brief.d.ts.map +1 -0
- package/dist/commands/brief.js +84 -0
- package/dist/commands/brief.js.map +1 -0
- package/dist/commands/chat.d.ts +7 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +155 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/context.d.ts +63 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +794 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/dead-code.d.ts +15 -0
- package/dist/commands/dead-code.d.ts.map +1 -0
- package/dist/commands/dead-code.js +206 -0
- package/dist/commands/dead-code.js.map +1 -0
- package/dist/commands/deps.d.ts +20 -0
- package/dist/commands/deps.d.ts.map +1 -0
- package/dist/commands/deps.js +138 -0
- package/dist/commands/deps.js.map +1 -0
- package/dist/commands/focus.d.ts +8 -0
- package/dist/commands/focus.d.ts.map +1 -0
- package/dist/commands/focus.js +310 -0
- package/dist/commands/focus.js.map +1 -0
- package/dist/commands/index.d.ts +10 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +366 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init-agent.d.ts +23 -0
- package/dist/commands/init-agent.d.ts.map +1 -0
- package/dist/commands/init-agent.js +384 -0
- package/dist/commands/init-agent.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +122 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/knowledge.d.ts +14 -0
- package/dist/commands/knowledge.d.ts.map +1 -0
- package/dist/commands/knowledge.js +115 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/plan.d.ts +24 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +867 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/query.d.ts +39 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +392 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/remember.d.ts +11 -0
- package/dist/commands/remember.d.ts.map +1 -0
- package/dist/commands/remember.js +267 -0
- package/dist/commands/remember.js.map +1 -0
- package/dist/commands/report.d.ts +10 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +243 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +127 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/summarize.d.ts +14 -0
- package/dist/commands/summarize.d.ts.map +1 -0
- package/dist/commands/summarize.js +170 -0
- package/dist/commands/summarize.js.map +1 -0
- package/dist/commands/triage.d.ts +33 -0
- package/dist/commands/triage.d.ts.map +1 -0
- package/dist/commands/triage.js +419 -0
- package/dist/commands/triage.js.map +1 -0
- package/dist/commands/usages.d.ts +14 -0
- package/dist/commands/usages.d.ts.map +1 -0
- package/dist/commands/usages.js +236 -0
- package/dist/commands/usages.js.map +1 -0
- package/dist/core/config.d.ts +35 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +159 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/types.d.ts +287 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -0
- package/dist/embeddings/factory.d.ts +9 -0
- package/dist/embeddings/factory.d.ts.map +1 -0
- package/dist/embeddings/factory.js +26 -0
- package/dist/embeddings/factory.js.map +1 -0
- package/dist/embeddings/noop.d.ts +17 -0
- package/dist/embeddings/noop.d.ts.map +1 -0
- package/dist/embeddings/noop.js +22 -0
- package/dist/embeddings/noop.js.map +1 -0
- package/dist/embeddings/ollama.d.ts +11 -0
- package/dist/embeddings/ollama.d.ts.map +1 -0
- package/dist/embeddings/ollama.js +49 -0
- package/dist/embeddings/ollama.js.map +1 -0
- package/dist/embeddings/openai.d.ts +10 -0
- package/dist/embeddings/openai.d.ts.map +1 -0
- package/dist/embeddings/openai.js +67 -0
- package/dist/embeddings/openai.js.map +1 -0
- package/dist/embeddings/provider.d.ts +19 -0
- package/dist/embeddings/provider.d.ts.map +1 -0
- package/dist/embeddings/provider.js +3 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/indexer/callgraph.d.ts +16 -0
- package/dist/indexer/callgraph.d.ts.map +1 -0
- package/dist/indexer/callgraph.js +154 -0
- package/dist/indexer/callgraph.js.map +1 -0
- package/dist/indexer/chunkBySlidingWindow.d.ts +6 -0
- package/dist/indexer/chunkBySlidingWindow.d.ts.map +1 -0
- package/dist/indexer/chunkBySlidingWindow.js +30 -0
- package/dist/indexer/chunkBySlidingWindow.js.map +1 -0
- package/dist/indexer/chunkBySymbols.d.ts +7 -0
- package/dist/indexer/chunkBySymbols.d.ts.map +1 -0
- package/dist/indexer/chunkBySymbols.js +57 -0
- package/dist/indexer/chunkBySymbols.js.map +1 -0
- package/dist/indexer/chunker.d.ts +15 -0
- package/dist/indexer/chunker.d.ts.map +1 -0
- package/dist/indexer/chunker.js +26 -0
- package/dist/indexer/chunker.js.map +1 -0
- package/dist/indexer/classHeader.d.ts +7 -0
- package/dist/indexer/classHeader.d.ts.map +1 -0
- package/dist/indexer/classHeader.js +37 -0
- package/dist/indexer/classHeader.js.map +1 -0
- package/dist/indexer/deps.d.ts +66 -0
- package/dist/indexer/deps.d.ts.map +1 -0
- package/dist/indexer/deps.js +412 -0
- package/dist/indexer/deps.js.map +1 -0
- package/dist/indexer/hasher.d.ts +17 -0
- package/dist/indexer/hasher.d.ts.map +1 -0
- package/dist/indexer/hasher.js +38 -0
- package/dist/indexer/hasher.js.map +1 -0
- package/dist/indexer/parser.d.ts +18 -0
- package/dist/indexer/parser.d.ts.map +1 -0
- package/dist/indexer/parser.js +355 -0
- package/dist/indexer/parser.js.map +1 -0
- package/dist/indexer/scanner.d.ts +18 -0
- package/dist/indexer/scanner.d.ts.map +1 -0
- package/dist/indexer/scanner.js +37 -0
- package/dist/indexer/scanner.js.map +1 -0
- package/dist/indexer/strategy.d.ts +11 -0
- package/dist/indexer/strategy.d.ts.map +1 -0
- package/dist/indexer/strategy.js +15 -0
- package/dist/indexer/strategy.js.map +1 -0
- package/dist/indexer/tests.d.ts +15 -0
- package/dist/indexer/tests.d.ts.map +1 -0
- package/dist/indexer/tests.js +68 -0
- package/dist/indexer/tests.js.map +1 -0
- package/dist/indexer/todos.d.ts +9 -0
- package/dist/indexer/todos.d.ts.map +1 -0
- package/dist/indexer/todos.js +29 -0
- package/dist/indexer/todos.js.map +1 -0
- package/dist/llm/anthropic.d.ts +8 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +76 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/claude-code.d.ts +37 -0
- package/dist/llm/claude-code.d.ts.map +1 -0
- package/dist/llm/claude-code.js +97 -0
- package/dist/llm/claude-code.js.map +1 -0
- package/dist/llm/factory.d.ts +7 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +47 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/ollama.d.ts +8 -0
- package/dist/llm/ollama.d.ts.map +1 -0
- package/dist/llm/ollama.js +83 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +8 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +68 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/provider.d.ts +35 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +3 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/search/bm25.d.ts +3 -0
- package/dist/search/bm25.d.ts.map +1 -0
- package/dist/search/bm25.js +104 -0
- package/dist/search/bm25.js.map +1 -0
- package/dist/search/formatter.d.ts +43 -0
- package/dist/search/formatter.d.ts.map +1 -0
- package/dist/search/formatter.js +208 -0
- package/dist/search/formatter.js.map +1 -0
- package/dist/search/hybrid.d.ts +10 -0
- package/dist/search/hybrid.d.ts.map +1 -0
- package/dist/search/hybrid.js +53 -0
- package/dist/search/hybrid.js.map +1 -0
- package/dist/search/merge.d.ts +33 -0
- package/dist/search/merge.d.ts.map +1 -0
- package/dist/search/merge.js +158 -0
- package/dist/search/merge.js.map +1 -0
- package/dist/search/mmr.d.ts +23 -0
- package/dist/search/mmr.d.ts.map +1 -0
- package/dist/search/mmr.js +95 -0
- package/dist/search/mmr.js.map +1 -0
- package/dist/search/rerank.d.ts +12 -0
- package/dist/search/rerank.d.ts.map +1 -0
- package/dist/search/rerank.js +113 -0
- package/dist/search/rerank.js.map +1 -0
- package/dist/search/signature.d.ts +42 -0
- package/dist/search/signature.d.ts.map +1 -0
- package/dist/search/signature.js +112 -0
- package/dist/search/signature.js.map +1 -0
- package/dist/search/vector.d.ts +41 -0
- package/dist/search/vector.d.ts.map +1 -0
- package/dist/search/vector.js +185 -0
- package/dist/search/vector.js.map +1 -0
- package/dist/storage/cache.d.ts +30 -0
- package/dist/storage/cache.d.ts.map +1 -0
- package/dist/storage/cache.js +160 -0
- package/dist/storage/cache.js.map +1 -0
- package/dist/storage/knowledge.d.ts +23 -0
- package/dist/storage/knowledge.d.ts.map +1 -0
- package/dist/storage/knowledge.js +81 -0
- package/dist/storage/knowledge.js.map +1 -0
- package/dist/storage/planSession.d.ts +39 -0
- package/dist/storage/planSession.d.ts.map +1 -0
- package/dist/storage/planSession.js +78 -0
- package/dist/storage/planSession.js.map +1 -0
- package/dist/storage/repository.d.ts +27 -0
- package/dist/storage/repository.d.ts.map +1 -0
- package/dist/storage/repository.js +95 -0
- package/dist/storage/repository.js.map +1 -0
- package/dist/storage/session.d.ts +38 -0
- package/dist/storage/session.d.ts.map +1 -0
- package/dist/storage/session.js +100 -0
- package/dist/storage/session.js.map +1 -0
- package/dist/storage/summaries.d.ts +19 -0
- package/dist/storage/summaries.d.ts.map +1 -0
- package/dist/storage/summaries.js +66 -0
- package/dist/storage/summaries.js.map +1 -0
- package/dist/storage/usage.d.ts +39 -0
- package/dist/storage/usage.d.ts.map +1 -0
- package/dist/storage/usage.js +55 -0
- package/dist/storage/usage.js.map +1 -0
- package/dist/utils/git.d.ts +20 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +49 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/tokenizer.d.ts +32 -0
- package/dist/utils/tokenizer.d.ts.map +1 -0
- package/dist/utils/tokenizer.js +66 -0
- package/dist/utils/tokenizer.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AnthropicProvider = void 0;
|
|
4
|
+
class AnthropicProvider {
|
|
5
|
+
name = "anthropic";
|
|
6
|
+
// biome-ignore lint/suspicious/noExplicitAny: @anthropic-ai/sdk is an optional peer dependency
|
|
7
|
+
client;
|
|
8
|
+
constructor(apiKey) {
|
|
9
|
+
let Anthropic;
|
|
10
|
+
try {
|
|
11
|
+
// biome-ignore lint/suspicious/noExplicitAny: optional peer dependency
|
|
12
|
+
Anthropic = require("@anthropic-ai/sdk").default ?? require("@anthropic-ai/sdk");
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error('Package "@anthropic-ai/sdk" is not installed. Run: npm install @anthropic-ai/sdk');
|
|
16
|
+
}
|
|
17
|
+
this.client = new Anthropic({ apiKey });
|
|
18
|
+
}
|
|
19
|
+
async chat(messages, options = {}) {
|
|
20
|
+
const stream = options.stream || !!options.onToken;
|
|
21
|
+
const model = options.model || "claude-3-5-sonnet-20240620";
|
|
22
|
+
// Anthropic requires a separate system prompt from the messages array
|
|
23
|
+
const systemMessage = messages.find((m) => m.role === "system");
|
|
24
|
+
const userMessages = messages.filter((m) => m.role !== "system");
|
|
25
|
+
if (stream) {
|
|
26
|
+
const responseStream = await this.client.messages.create({
|
|
27
|
+
model,
|
|
28
|
+
system: systemMessage?.content,
|
|
29
|
+
messages: userMessages.map((m) => ({
|
|
30
|
+
role: m.role,
|
|
31
|
+
content: m.content,
|
|
32
|
+
})),
|
|
33
|
+
max_tokens: options.maxTokens || 4096,
|
|
34
|
+
temperature: options.temperature ?? 0.7,
|
|
35
|
+
stream: true,
|
|
36
|
+
});
|
|
37
|
+
let fullContent = "";
|
|
38
|
+
for await (const event of responseStream) {
|
|
39
|
+
if (event.type === "content_block_delta" &&
|
|
40
|
+
event.delta.type === "text_delta") {
|
|
41
|
+
const token = event.delta.text;
|
|
42
|
+
fullContent += token;
|
|
43
|
+
options.onToken?.(token);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { content: fullContent };
|
|
47
|
+
}
|
|
48
|
+
const response = await this.client.messages.create({
|
|
49
|
+
model,
|
|
50
|
+
system: systemMessage?.content,
|
|
51
|
+
messages: userMessages.map((m) => ({
|
|
52
|
+
role: m.role,
|
|
53
|
+
content: m.content,
|
|
54
|
+
})),
|
|
55
|
+
max_tokens: options.maxTokens || 4096,
|
|
56
|
+
temperature: options.temperature ?? 0.7,
|
|
57
|
+
});
|
|
58
|
+
// Anthropic response content can be an array of blocks
|
|
59
|
+
const content = response.content
|
|
60
|
+
.filter((block) => block.type === "text")
|
|
61
|
+
.map((block) => block.text)
|
|
62
|
+
.join("\n");
|
|
63
|
+
return {
|
|
64
|
+
content,
|
|
65
|
+
usage: response.usage
|
|
66
|
+
? {
|
|
67
|
+
promptTokens: response.usage.input_tokens,
|
|
68
|
+
completionTokens: response.usage.output_tokens,
|
|
69
|
+
totalTokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
70
|
+
}
|
|
71
|
+
: undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
76
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/llm/anthropic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ChatMessage, ChatOptions, LLMProvider, LLMResponse } from "./provider";
|
|
2
|
+
/**
|
|
3
|
+
* LLMProvider that delegates to the local `claude` CLI in non-interactive mode.
|
|
4
|
+
*
|
|
5
|
+
* Unlike API-based providers, ClaudeCodeProvider runs a subprocess and can
|
|
6
|
+
* grant the subprocess access to the project filesystem via its file tools
|
|
7
|
+
* (Read, Grep, Glob). This lets the planner explore the codebase autonomously
|
|
8
|
+
* rather than receiving pre-built context from vemora.
|
|
9
|
+
*
|
|
10
|
+
* Config example:
|
|
11
|
+
* ```json
|
|
12
|
+
* "planner": {
|
|
13
|
+
* "provider": "claude-code",
|
|
14
|
+
* "model": "claude-sonnet-4-6",
|
|
15
|
+
* "allowedTools": ["Read", "Grep", "Glob"],
|
|
16
|
+
* "maxBudgetUsd": 0.50
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The `baseUrl` field is repurposed as the path to the `claude` binary
|
|
21
|
+
* (default: "claude", assumed to be on PATH).
|
|
22
|
+
*/
|
|
23
|
+
export declare class ClaudeCodeProvider implements LLMProvider {
|
|
24
|
+
readonly name = "claude-code";
|
|
25
|
+
private readonly command;
|
|
26
|
+
private readonly model;
|
|
27
|
+
private readonly allowedTools;
|
|
28
|
+
private readonly maxBudgetUsd;
|
|
29
|
+
constructor(options?: {
|
|
30
|
+
command?: string;
|
|
31
|
+
model?: string;
|
|
32
|
+
allowedTools?: string[];
|
|
33
|
+
maxBudgetUsd?: number;
|
|
34
|
+
});
|
|
35
|
+
chat(messages: ChatMessage[], options?: ChatOptions): Promise<LLMResponse>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/llm/claude-code.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIrF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,kBAAmB,YAAW,WAAW;IACpD,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,OAAO,GAAE;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;KAClB;IAOA,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CA2EjF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClaudeCodeProvider = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
// ─── Provider ─────────────────────────────────────────────────────────────────
|
|
6
|
+
/**
|
|
7
|
+
* LLMProvider that delegates to the local `claude` CLI in non-interactive mode.
|
|
8
|
+
*
|
|
9
|
+
* Unlike API-based providers, ClaudeCodeProvider runs a subprocess and can
|
|
10
|
+
* grant the subprocess access to the project filesystem via its file tools
|
|
11
|
+
* (Read, Grep, Glob). This lets the planner explore the codebase autonomously
|
|
12
|
+
* rather than receiving pre-built context from vemora.
|
|
13
|
+
*
|
|
14
|
+
* Config example:
|
|
15
|
+
* ```json
|
|
16
|
+
* "planner": {
|
|
17
|
+
* "provider": "claude-code",
|
|
18
|
+
* "model": "claude-sonnet-4-6",
|
|
19
|
+
* "allowedTools": ["Read", "Grep", "Glob"],
|
|
20
|
+
* "maxBudgetUsd": 0.50
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The `baseUrl` field is repurposed as the path to the `claude` binary
|
|
25
|
+
* (default: "claude", assumed to be on PATH).
|
|
26
|
+
*/
|
|
27
|
+
class ClaudeCodeProvider {
|
|
28
|
+
name = "claude-code";
|
|
29
|
+
command;
|
|
30
|
+
model;
|
|
31
|
+
allowedTools;
|
|
32
|
+
maxBudgetUsd;
|
|
33
|
+
constructor(options = {}) {
|
|
34
|
+
this.command = options.command ?? "claude";
|
|
35
|
+
this.model = options.model;
|
|
36
|
+
this.allowedTools = options.allowedTools ?? ["Read", "Grep", "Glob"];
|
|
37
|
+
this.maxBudgetUsd = options.maxBudgetUsd ?? 0.5;
|
|
38
|
+
}
|
|
39
|
+
async chat(messages, options) {
|
|
40
|
+
const projectRoot = options?.projectRoot ?? process.cwd();
|
|
41
|
+
// ── Separate system message from conversation ─────────────────────────────
|
|
42
|
+
const systemMsg = messages.find((m) => m.role === "system");
|
|
43
|
+
const otherMsgs = messages.filter((m) => m.role !== "system");
|
|
44
|
+
// Combine non-system messages into a single prompt string
|
|
45
|
+
const prompt = otherMsgs.length === 1
|
|
46
|
+
? otherMsgs[0].content
|
|
47
|
+
: otherMsgs.map((m) => `[${m.role}]\n${m.content}`).join("\n\n");
|
|
48
|
+
// ── Build argument list ───────────────────────────────────────────────────
|
|
49
|
+
const args = [
|
|
50
|
+
"-p", prompt,
|
|
51
|
+
"--output-format", "text",
|
|
52
|
+
"--allowedTools", this.allowedTools.join(","),
|
|
53
|
+
"--max-budget-usd", String(this.maxBudgetUsd),
|
|
54
|
+
"--dangerously-skip-permissions", // non-interactive: no permission prompts
|
|
55
|
+
];
|
|
56
|
+
if (this.model) {
|
|
57
|
+
args.push("--model", this.model);
|
|
58
|
+
}
|
|
59
|
+
if (systemMsg) {
|
|
60
|
+
args.push("--append-system-prompt", systemMsg.content);
|
|
61
|
+
}
|
|
62
|
+
// ── Spawn subprocess ──────────────────────────────────────────────────────
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const proc = (0, child_process_1.spawn)(this.command, args, {
|
|
65
|
+
cwd: projectRoot,
|
|
66
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
67
|
+
});
|
|
68
|
+
let stdout = "";
|
|
69
|
+
let stderr = "";
|
|
70
|
+
proc.stdout.on("data", (chunk) => {
|
|
71
|
+
stdout += chunk.toString();
|
|
72
|
+
});
|
|
73
|
+
proc.stderr.on("data", (chunk) => {
|
|
74
|
+
stderr += chunk.toString();
|
|
75
|
+
});
|
|
76
|
+
proc.on("error", (err) => {
|
|
77
|
+
if (err.code === "ENOENT") {
|
|
78
|
+
reject(new Error(`claude CLI not found at "${this.command}". ` +
|
|
79
|
+
"Install it with: npm install -g @anthropic-ai/claude-code"));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
reject(err);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
proc.on("close", (code) => {
|
|
86
|
+
if (code !== 0 && !stdout.trim()) {
|
|
87
|
+
reject(new Error(`claude exited with code ${code}.\nstderr: ${stderr.trim()}`));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// --output-format text: stdout is the raw assistant response
|
|
91
|
+
resolve({ content: stdout.trim() });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.ClaudeCodeProvider = ClaudeCodeProvider;
|
|
97
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/llm/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SummarizationConfig } from "../core/types";
|
|
2
|
+
import type { LLMProvider } from "./provider";
|
|
3
|
+
/**
|
|
4
|
+
* Factory that instantiates the correct LLMProvider from config.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createLLMProvider(config: SummarizationConfig): LLMProvider;
|
|
7
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/llm/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAKzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,WAAW,CAwC1E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLLMProvider = createLLMProvider;
|
|
4
|
+
const anthropic_1 = require("./anthropic");
|
|
5
|
+
const claude_code_1 = require("./claude-code");
|
|
6
|
+
const ollama_1 = require("./ollama");
|
|
7
|
+
const openai_1 = require("./openai");
|
|
8
|
+
/**
|
|
9
|
+
* Factory that instantiates the correct LLMProvider from config.
|
|
10
|
+
*/
|
|
11
|
+
function createLLMProvider(config) {
|
|
12
|
+
const apiKey = config.apiKey ??
|
|
13
|
+
(config.provider === "anthropic"
|
|
14
|
+
? process.env.ANTHROPIC_API_KEY
|
|
15
|
+
: config.provider === "gemini"
|
|
16
|
+
? (process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY)
|
|
17
|
+
: process.env.OPENAI_API_KEY);
|
|
18
|
+
switch (config.provider) {
|
|
19
|
+
case "openai":
|
|
20
|
+
if (!apiKey)
|
|
21
|
+
throw new Error("OPENAI_API_KEY not found");
|
|
22
|
+
return new openai_1.OpenAIProvider(apiKey, config.baseUrl);
|
|
23
|
+
case "anthropic":
|
|
24
|
+
if (!apiKey)
|
|
25
|
+
throw new Error("ANTHROPIC_API_KEY not found");
|
|
26
|
+
return new anthropic_1.AnthropicProvider(apiKey);
|
|
27
|
+
case "gemini": {
|
|
28
|
+
if (!apiKey)
|
|
29
|
+
throw new Error("GEMINI_API_KEY (or GOOGLE_API_KEY) not found");
|
|
30
|
+
const baseUrl = config.baseUrl ??
|
|
31
|
+
"https://generativelanguage.googleapis.com/v1beta/openai/";
|
|
32
|
+
return new openai_1.OpenAIProvider(apiKey, baseUrl);
|
|
33
|
+
}
|
|
34
|
+
case "ollama":
|
|
35
|
+
return new ollama_1.OllamaProvider(config.baseUrl || "http://localhost:11434");
|
|
36
|
+
case "claude-code":
|
|
37
|
+
return new claude_code_1.ClaudeCodeProvider({
|
|
38
|
+
command: config.baseUrl || "claude", // baseUrl repurposed as binary path
|
|
39
|
+
model: config.model || undefined,
|
|
40
|
+
allowedTools: config.allowedTools,
|
|
41
|
+
maxBudgetUsd: config.maxBudgetUsd,
|
|
42
|
+
});
|
|
43
|
+
default:
|
|
44
|
+
throw new Error(`Unknown LLM provider: "${config.provider}"`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/llm/factory.ts"],"names":[],"mappings":";;AAUA,8CAwCC;AAjDD,2CAAgD;AAChD,+CAAmD;AACnD,qCAA0C;AAC1C,qCAA0C;AAG1C;;GAEG;AACH,SAAgB,iBAAiB,CAAC,MAA2B;IAC3D,MAAM,MAAM,GACV,MAAM,CAAC,MAAM;QACb,CAAC,MAAM,CAAC,QAAQ,KAAK,WAAW;YAC9B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB;YAC/B,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ;gBAC5B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;gBAC5D,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEpC,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ;YACX,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACzD,OAAO,IAAI,uBAAc,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAEpD,KAAK,WAAW;YACd,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC5D,OAAO,IAAI,6BAAiB,CAAC,MAAM,CAAC,CAAC;QAEvC,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC7E,MAAM,OAAO,GACX,MAAM,CAAC,OAAO;gBACd,0DAA0D,CAAC;YAC7D,OAAO,IAAI,uBAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC7C,CAAC;QAED,KAAK,QAAQ;YACX,OAAO,IAAI,uBAAc,CAAC,MAAM,CAAC,OAAO,IAAI,wBAAwB,CAAC,CAAC;QAExE,KAAK,aAAa;YAChB,OAAO,IAAI,gCAAkB,CAAC;gBAC5B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,QAAQ,EAAE,oCAAoC;gBACzE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,SAAS;gBAChC,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,YAAY,EAAE,MAAM,CAAC,YAAY;aAClC,CAAC,CAAC;QAEL;YACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChatMessage, ChatOptions, LLMProvider, LLMResponse } from "./provider";
|
|
2
|
+
export declare class OllamaProvider implements LLMProvider {
|
|
3
|
+
readonly name = "ollama";
|
|
4
|
+
private baseUrl;
|
|
5
|
+
constructor(baseUrl?: string);
|
|
6
|
+
chat(messages: ChatMessage[], options?: ChatOptions): Promise<LLMResponse>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=ollama.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/llm/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE,MAAiC;IAIhD,IAAI,CACR,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,WAAW,CAAC;CA6ExB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OllamaProvider = void 0;
|
|
4
|
+
class OllamaProvider {
|
|
5
|
+
name = "ollama";
|
|
6
|
+
baseUrl;
|
|
7
|
+
constructor(baseUrl = "http://localhost:11434") {
|
|
8
|
+
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
9
|
+
}
|
|
10
|
+
async chat(messages, options = {}) {
|
|
11
|
+
const stream = options.stream || !!options.onToken;
|
|
12
|
+
const model = options.model;
|
|
13
|
+
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
14
|
+
method: "POST",
|
|
15
|
+
headers: { "Content-Type": "application/json" },
|
|
16
|
+
body: JSON.stringify({
|
|
17
|
+
model,
|
|
18
|
+
messages: messages.map((m) => ({
|
|
19
|
+
role: m.role,
|
|
20
|
+
content: m.content,
|
|
21
|
+
})),
|
|
22
|
+
options: {
|
|
23
|
+
temperature: options.temperature ?? 0.7,
|
|
24
|
+
num_predict: options.maxTokens,
|
|
25
|
+
},
|
|
26
|
+
stream,
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const error = await response.text();
|
|
31
|
+
throw new Error(`Ollama API error: ${error}`);
|
|
32
|
+
}
|
|
33
|
+
if (stream) {
|
|
34
|
+
if (!response.body)
|
|
35
|
+
throw new Error("Ollama response body is empty");
|
|
36
|
+
const reader = response.body.getReader();
|
|
37
|
+
const decoder = new TextDecoder();
|
|
38
|
+
let fullContent = "";
|
|
39
|
+
try {
|
|
40
|
+
while (true) {
|
|
41
|
+
const { done, value } = await reader.read();
|
|
42
|
+
if (done)
|
|
43
|
+
break;
|
|
44
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
45
|
+
// Ollama sends multiple JSON objects, one per line (NDJSON)
|
|
46
|
+
const lines = chunk.split("\n");
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
if (!line.trim())
|
|
49
|
+
continue;
|
|
50
|
+
try {
|
|
51
|
+
const data = JSON.parse(line);
|
|
52
|
+
if (data.message?.content) {
|
|
53
|
+
const token = data.message.content;
|
|
54
|
+
fullContent += token;
|
|
55
|
+
options.onToken?.(token);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (_e) {
|
|
59
|
+
// Partial JSON, wait for more data
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
reader.releaseLock();
|
|
66
|
+
}
|
|
67
|
+
return { content: fullContent };
|
|
68
|
+
}
|
|
69
|
+
const data = (await response.json());
|
|
70
|
+
return {
|
|
71
|
+
content: data.message?.content || "",
|
|
72
|
+
usage: data.prompt_eval_count
|
|
73
|
+
? {
|
|
74
|
+
promptTokens: data.prompt_eval_count,
|
|
75
|
+
completionTokens: data.eval_count ?? 0,
|
|
76
|
+
totalTokens: data.prompt_eval_count + (data.eval_count ?? 0),
|
|
77
|
+
}
|
|
78
|
+
: undefined,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.OllamaProvider = OllamaProvider;
|
|
83
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/llm/ollama.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChatMessage, ChatOptions, LLMProvider, LLMResponse } from "./provider";
|
|
2
|
+
export declare class OpenAIProvider implements LLMProvider {
|
|
3
|
+
readonly name = "openai";
|
|
4
|
+
private client;
|
|
5
|
+
constructor(apiKey: string, baseUrl?: string);
|
|
6
|
+
chat(messages: ChatMessage[], options?: ChatOptions): Promise<LLMResponse>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/llm/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,MAAM,CAAM;gBAER,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAgBtC,IAAI,CACR,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,WAAW,CAAC;CAkDxB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenAIProvider = void 0;
|
|
4
|
+
class OpenAIProvider {
|
|
5
|
+
name = "openai";
|
|
6
|
+
// biome-ignore lint/suspicious/noExplicitAny: openai is an optional peer dependency
|
|
7
|
+
client;
|
|
8
|
+
constructor(apiKey, baseUrl) {
|
|
9
|
+
let OpenAI;
|
|
10
|
+
try {
|
|
11
|
+
// biome-ignore lint/suspicious/noExplicitAny: optional peer dependency
|
|
12
|
+
OpenAI = require("openai").default ?? require("openai");
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error('Package "openai" is not installed. Run: npm install openai');
|
|
16
|
+
}
|
|
17
|
+
this.client = new OpenAI({
|
|
18
|
+
apiKey,
|
|
19
|
+
...(baseUrl ? { baseURL: baseUrl } : {}),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async chat(messages, options = {}) {
|
|
23
|
+
const stream = options.stream || !!options.onToken;
|
|
24
|
+
if (stream) {
|
|
25
|
+
const responseStream = await this.client.chat.completions.create({
|
|
26
|
+
model: options.model || "gpt-4o-mini",
|
|
27
|
+
messages: messages.map((m) => ({
|
|
28
|
+
role: m.role,
|
|
29
|
+
content: m.content,
|
|
30
|
+
})),
|
|
31
|
+
temperature: options.temperature ?? 0.7,
|
|
32
|
+
max_tokens: options.maxTokens,
|
|
33
|
+
stream: true,
|
|
34
|
+
});
|
|
35
|
+
let fullContent = "";
|
|
36
|
+
for await (const chunk of responseStream) {
|
|
37
|
+
const content = chunk.choices[0]?.delta?.content || "";
|
|
38
|
+
if (content) {
|
|
39
|
+
fullContent += content;
|
|
40
|
+
options.onToken?.(content);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { content: fullContent };
|
|
44
|
+
}
|
|
45
|
+
const response = await this.client.chat.completions.create({
|
|
46
|
+
model: options.model || "gpt-4o-mini",
|
|
47
|
+
messages: messages.map((m) => ({
|
|
48
|
+
role: m.role,
|
|
49
|
+
content: m.content,
|
|
50
|
+
})),
|
|
51
|
+
temperature: options.temperature ?? 0.7,
|
|
52
|
+
max_tokens: options.maxTokens,
|
|
53
|
+
});
|
|
54
|
+
const content = response.choices[0]?.message?.content || "";
|
|
55
|
+
return {
|
|
56
|
+
content,
|
|
57
|
+
usage: response.usage
|
|
58
|
+
? {
|
|
59
|
+
promptTokens: response.usage.prompt_tokens,
|
|
60
|
+
completionTokens: response.usage.completion_tokens,
|
|
61
|
+
totalTokens: response.usage.total_tokens,
|
|
62
|
+
}
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.OpenAIProvider = OpenAIProvider;
|
|
68
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/llm/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ChatMessage {
|
|
2
|
+
role: "system" | "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ChatOptions {
|
|
6
|
+
model?: string;
|
|
7
|
+
temperature?: number;
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
stream?: boolean;
|
|
10
|
+
onToken?: (token: string) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Absolute path to the project root.
|
|
13
|
+
* Used by subprocess-based providers (e.g. claude-code) as the working
|
|
14
|
+
* directory and to grant file-tool access.
|
|
15
|
+
*/
|
|
16
|
+
projectRoot?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface LLMResponse {
|
|
19
|
+
content: string;
|
|
20
|
+
usage?: {
|
|
21
|
+
promptTokens: number;
|
|
22
|
+
completionTokens: number;
|
|
23
|
+
totalTokens: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface LLMProvider {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/**
|
|
29
|
+
* Sends a chat request to the LLM.
|
|
30
|
+
* If options.stream is true, it should ideally handle streaming
|
|
31
|
+
* (to be refined based on CLI requirements).
|
|
32
|
+
*/
|
|
33
|
+
chat(messages: ChatMessage[], options?: ChatOptions): Promise<LLMResponse>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/llm/provider.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QACN,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC5E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/llm/provider.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bm25.d.ts","sourceRoot":"","sources":["../../src/search/bm25.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AA2BtE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,EAAE,WAAW,EACpB,IAAI,SAAK,GACR,YAAY,EAAE,CAqGhB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeBM25Scores = computeBM25Scores;
|
|
4
|
+
/**
|
|
5
|
+
* BM25 implementation for high-precision keyword search.
|
|
6
|
+
*/
|
|
7
|
+
const K1 = 1.5;
|
|
8
|
+
const B = 0.75;
|
|
9
|
+
let bm25Cache = null;
|
|
10
|
+
function computeHash(chunks) {
|
|
11
|
+
if (chunks.length === 0)
|
|
12
|
+
return "";
|
|
13
|
+
const chunkIds = chunks.map((c) => c.id).sort();
|
|
14
|
+
return chunkIds.join("|");
|
|
15
|
+
}
|
|
16
|
+
function computeBM25Scores(query, chunks, symbols, topK = 10) {
|
|
17
|
+
if (!query)
|
|
18
|
+
return [];
|
|
19
|
+
// 1. Tokenize query
|
|
20
|
+
const queryTerms = query
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.split(/[\s\W]+/)
|
|
23
|
+
.filter((t) => t.length >= 2); // Slightly more inclusive than current TF search
|
|
24
|
+
if (queryTerms.length === 0)
|
|
25
|
+
return [];
|
|
26
|
+
const N = chunks.length;
|
|
27
|
+
if (N === 0)
|
|
28
|
+
return [];
|
|
29
|
+
const currentHash = computeHash(chunks);
|
|
30
|
+
// 2. Tokenize docs and compute stats (with CACHING)
|
|
31
|
+
if (!bm25Cache || bm25Cache.chunkIdsHash !== currentHash) {
|
|
32
|
+
// Cache miss: compute everything
|
|
33
|
+
const docTokens = [];
|
|
34
|
+
const tfMaps = [];
|
|
35
|
+
let totalLen = 0;
|
|
36
|
+
const dfMap = new Map();
|
|
37
|
+
for (const chunk of chunks) {
|
|
38
|
+
const tokens = (chunk.content ?? "").toLowerCase().split(/[\s\W]+/);
|
|
39
|
+
docTokens.push(tokens);
|
|
40
|
+
totalLen += tokens.length;
|
|
41
|
+
const tfMap = new Map();
|
|
42
|
+
for (const token of tokens) {
|
|
43
|
+
tfMap.set(token, (tfMap.get(token) || 0) + 1);
|
|
44
|
+
}
|
|
45
|
+
tfMaps.push(tfMap);
|
|
46
|
+
// Unique tokens for DF calculation
|
|
47
|
+
for (const token of tfMap.keys()) {
|
|
48
|
+
dfMap.set(token, (dfMap.get(token) || 0) + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
bm25Cache = {
|
|
52
|
+
chunkIdsHash: currentHash,
|
|
53
|
+
docTokens,
|
|
54
|
+
tfMaps,
|
|
55
|
+
dfMap,
|
|
56
|
+
avgdl: totalLen / N,
|
|
57
|
+
N,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const { docTokens, tfMaps, dfMap, avgdl } = bm25Cache;
|
|
61
|
+
// 3. Compute IDF for query terms
|
|
62
|
+
const idfMap = new Map();
|
|
63
|
+
for (const term of queryTerms) {
|
|
64
|
+
const df = dfMap.get(term) || 0;
|
|
65
|
+
// Standard BM25 IDF: ln((N - df + 0.5) / (df + 0.5) + 1)
|
|
66
|
+
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
67
|
+
idfMap.set(term, Math.max(0, idf)); // IDF can be negative if term is in > half docs
|
|
68
|
+
}
|
|
69
|
+
// 4. Compute BM25 scores
|
|
70
|
+
const results = chunks.map((chunk, i) => {
|
|
71
|
+
const tokens = docTokens[i];
|
|
72
|
+
const tfMap = tfMaps[i];
|
|
73
|
+
const dl = tokens.length;
|
|
74
|
+
let score = 0;
|
|
75
|
+
for (const term of queryTerms) {
|
|
76
|
+
const tf = tfMap.get(term) || 0;
|
|
77
|
+
const idf = idfMap.get(term) || 0;
|
|
78
|
+
// BM25 Formula: IDF * (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (dl / avgdl)))
|
|
79
|
+
const numerator = tf * (K1 + 1);
|
|
80
|
+
const denominator = tf + K1 * (1 - B + B * (dl / avgdl));
|
|
81
|
+
score += idf * (numerator / denominator);
|
|
82
|
+
}
|
|
83
|
+
// Bonus: exact symbol match
|
|
84
|
+
if (chunk.symbol) {
|
|
85
|
+
const symLower = chunk.symbol.toLowerCase();
|
|
86
|
+
if (queryTerms.some((t) => symLower.includes(t))) {
|
|
87
|
+
// Boost for keyword match in symbol name
|
|
88
|
+
score *= 1.5;
|
|
89
|
+
}
|
|
90
|
+
if (queryTerms.some((t) => symLower === t)) {
|
|
91
|
+
// Higher boost for exact identifier match
|
|
92
|
+
score *= 2.0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const symbol = chunk.symbol ? symbols[chunk.symbol] : undefined;
|
|
96
|
+
return { chunk, score, symbol };
|
|
97
|
+
});
|
|
98
|
+
// 5. Sort and return
|
|
99
|
+
return results
|
|
100
|
+
.filter((r) => r.score > 0)
|
|
101
|
+
.sort((a, b) => b.score - a.score)
|
|
102
|
+
.slice(0, topK);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=bm25.js.map
|