Add local Ollama-compatible AI provider support

swadhinbiswas committed 8 months ago
7dbf5b1
1 parent 7ceb46b
commit 7dbf5b1
5 files changed
+60 -3
▼doc/feature_audit.md
+1-1
71
71
| Required approval policies | ✅ | Via branch protection rules |
72
72
| Partial file approvals | ✅ | Implemented via API + PR UI (`partial-file-approvals`, `file-approvals.ts`) |
73
73
| Multi-reviewer rules | ⚠️ | Merge gates now enforce explicit required-reviewer approvals; broader state/rule UX still maturing |
74
-| AI code review | ✅ | Multi-provider adapters (OpenAI/Anthropic/Groq/Bytez/OpenRouter/Together/Google) + external-agent async callback flow |
74
+| AI code review | ✅ | Multi-provider adapters (OpenAI/Anthropic/Groq/Bytez/OpenRouter/Together/Google/Local-Ollama) + external-agent async callback flow |
75
75
76
76
## 4. Issues & Planning
77
77
| Feature | Status | Implementation Notes |
78
78
▼src/lib/ai/index.ts
+3-0
6
6
import { TogetherAdapter } from "./together";
7
7
import { GoogleAdapter } from "./google";
8
8
import { ExternalAgentAdapter } from "./external-agent";
9
+import { LocalAdapter } from "./local";
9
10
import type { AIAdapter } from "./types";
10
11
11
12
export function getAIAdapter(provider: string): AIAdapter {
24
25
return new GoogleAdapter();
25
26
case "external_agent":
26
27
return new ExternalAgentAdapter();
28
+ case "local":
29
+ return new LocalAdapter();
27
30
case "anthropic":
28
31
return new AnthropicAdapter();
29
32
default:
30
33
▼src/lib/ai/local.ts
+39-0
1
+import OpenAI from "openai";
2
+import type { AIAdapter, AICompletionResult, AIConfig, AIReviewPrompt } from "./types";
3
+import { logger } from "@/lib/logger";
4
+
5
+export class LocalAdapter implements AIAdapter {
6
+ async complete(prompt: AIReviewPrompt, config: AIConfig): Promise<AICompletionResult> {
7
+ const baseURL = config.baseUrl || process.env.LOCAL_AI_BASE_URL || "http://127.0.0.1:11434/v1";
8
+ const apiKey = config.apiKey || process.env.LOCAL_AI_API_KEY || "ollama";
9
+
10
+ const client = new OpenAI({
11
+ apiKey,
12
+ baseURL,
13
+ });
14
+
15
+ const model = config.model || "llama3.1";
16
+ logger.info({ model, provider: "local", baseURL }, "Calling local OpenAI-compatible model");
17
+
18
+ const response = await client.chat.completions.create({
19
+ model,
20
+ messages: [
21
+ { role: "system", content: prompt.system },
22
+ { role: "user", content: prompt.user },
23
+ ],
24
+ temperature: config.temperature ?? 0.2,
25
+ });
26
+
27
+ const content = response.choices[0]?.message?.content || "{}";
28
+ const usage = response.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
29
+
30
+ return {
31
+ content,
32
+ usage: {
33
+ inputTokens: usage.prompt_tokens,
34
+ outputTokens: usage.completion_tokens,
35
+ totalTokens: usage.total_tokens,
36
+ },
37
+ };
38
+ }
39
+}
0
40
▼src/pages/settings/ai.astro
+3-2
163
163
value="local"
164
164
selected={aiConfig.provider ===
165
165
"local"}
166
- >Local LLM (Coming Soon)</option
166
+ >Local LLM (Ollama/OpenAI-compatible)</option
167
167
>
168
168
</select>
169
169
</div>
180
180
Leave blank to use provider default.
181
181
Recommended: `gpt-4-turbo` (OpenAI),
182
182
`llama3-70b-8192` (Groq),
183
- `meta-llama/llama-3.1-8b-instruct:free` (OpenRouter).
183
+ `meta-llama/llama-3.1-8b-instruct:free` (OpenRouter),
184
+ `llama3.1` (Local).
184
185
</p>
185
186
</div>
186
187
</CardContent>
187
188
▼tests/unit/ai-adapters.test.ts
+14-0
1
+import { describe, expect, it } from "vitest";
2
+import { getAIAdapter } from "@/lib/ai";
3
+import { LocalAdapter } from "@/lib/ai/local";
4
+
5
+describe("ai adapter registry", () => {
6
+ it("returns local adapter for local provider", () => {
7
+ const adapter = getAIAdapter("local");
8
+ expect(adapter).toBeInstanceOf(LocalAdapter);
9
+ });
10
+
11
+ it("throws for unknown providers", () => {
12
+ expect(() => getAIAdapter("unknown-provider")).toThrow(/Unknown AI provider/i);
13
+ });
14
+});
0
15