claude-flow 2.0.0-alpha.65 → 2.0.0-alpha.67
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/.claude/cache/agent-pool.json +33 -0
- package/.claude/cache/memory-optimization.json +19 -0
- package/.claude/cache/neural-optimization.json +25 -0
- package/.claude/cache/optimized-hooks.json +19 -0
- package/.claude/cache/parallel-processing.json +26 -0
- package/.claude/optimized-settings.json +270 -0
- package/.claude/settings-enhanced.json +278 -0
- package/.claude/settings.json +105 -8
- package/CHANGELOG.md +40 -0
- package/bin/claude-flow +1 -1
- package/dist/cli/simple-commands/hive-mind.js +1 -1
- package/dist/cli/simple-commands/hive-mind.js.map +1 -1
- package/dist/cli/simple-commands/hooks.js +6 -4
- package/dist/cli/simple-commands/hooks.js.map +1 -1
- package/dist/providers/anthropic-provider.d.ts +27 -0
- package/dist/providers/anthropic-provider.d.ts.map +1 -0
- package/dist/providers/anthropic-provider.js +247 -0
- package/dist/providers/anthropic-provider.js.map +1 -0
- package/dist/providers/base-provider.d.ts +134 -0
- package/dist/providers/base-provider.d.ts.map +1 -0
- package/dist/providers/base-provider.js +407 -0
- package/dist/providers/base-provider.js.map +1 -0
- package/dist/providers/cohere-provider.d.ts +28 -0
- package/dist/providers/cohere-provider.d.ts.map +1 -0
- package/dist/providers/cohere-provider.js +407 -0
- package/dist/providers/cohere-provider.js.map +1 -0
- package/dist/providers/google-provider.d.ts +23 -0
- package/dist/providers/google-provider.d.ts.map +1 -0
- package/dist/providers/google-provider.js +362 -0
- package/dist/providers/google-provider.js.map +1 -0
- package/dist/providers/index.d.ts +14 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +18 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama-provider.d.ts +23 -0
- package/dist/providers/ollama-provider.d.ts.map +1 -0
- package/dist/providers/ollama-provider.js +374 -0
- package/dist/providers/ollama-provider.js.map +1 -0
- package/dist/providers/openai-provider.d.ts +23 -0
- package/dist/providers/openai-provider.d.ts.map +1 -0
- package/dist/providers/openai-provider.js +349 -0
- package/dist/providers/openai-provider.js.map +1 -0
- package/dist/providers/provider-manager.d.ts +139 -0
- package/dist/providers/provider-manager.d.ts.map +1 -0
- package/dist/providers/provider-manager.js +513 -0
- package/dist/providers/provider-manager.js.map +1 -0
- package/dist/providers/types.d.ts +356 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +61 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/utils.d.ts +37 -0
- package/dist/providers/utils.d.ts.map +1 -0
- package/dist/providers/utils.js +322 -0
- package/dist/providers/utils.js.map +1 -0
- package/dist/services/agentic-flow-hooks/hook-manager.d.ts +70 -0
- package/dist/services/agentic-flow-hooks/hook-manager.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/hook-manager.js +512 -0
- package/dist/services/agentic-flow-hooks/hook-manager.js.map +1 -0
- package/dist/services/agentic-flow-hooks/index.d.ts +36 -0
- package/dist/services/agentic-flow-hooks/index.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/index.js +325 -0
- package/dist/services/agentic-flow-hooks/index.js.map +1 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.d.ts +33 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.js +415 -0
- package/dist/services/agentic-flow-hooks/llm-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.d.ts +45 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.js +532 -0
- package/dist/services/agentic-flow-hooks/memory-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.d.ts +39 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.js +561 -0
- package/dist/services/agentic-flow-hooks/neural-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.d.ts +33 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.js +621 -0
- package/dist/services/agentic-flow-hooks/performance-hooks.js.map +1 -0
- package/dist/services/agentic-flow-hooks/types.d.ts +379 -0
- package/dist/services/agentic-flow-hooks/types.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/types.js +8 -0
- package/dist/services/agentic-flow-hooks/types.js.map +1 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.d.ts +39 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.d.ts.map +1 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.js +742 -0
- package/dist/services/agentic-flow-hooks/workflow-hooks.js.map +1 -0
- package/package.json +2 -2
- package/scripts/install-arm64.js +78 -0
- package/scripts/optimize-performance.js +400 -0
- package/scripts/performance-monitor.js +263 -0
- package/src/cli/help-text.js +1 -1
- package/src/cli/simple-cli.js +1 -1
- package/src/cli/simple-commands/hive-mind.js +1 -1
- package/src/cli/simple-commands/hooks.js +8 -6
- package/src/providers/anthropic-provider.ts +282 -0
- package/src/providers/base-provider.ts +560 -0
- package/src/providers/cohere-provider.ts +521 -0
- package/src/providers/google-provider.ts +477 -0
- package/src/providers/index.ts +21 -0
- package/src/providers/ollama-provider.ts +489 -0
- package/src/providers/openai-provider.ts +476 -0
- package/src/providers/provider-manager.ts +654 -0
- package/src/providers/types.ts +531 -0
- package/src/providers/utils.ts +376 -0
- package/src/services/agentic-flow-hooks/hook-manager.ts +701 -0
- package/src/services/agentic-flow-hooks/index.ts +386 -0
- package/src/services/agentic-flow-hooks/llm-hooks.ts +557 -0
- package/src/services/agentic-flow-hooks/memory-hooks.ts +710 -0
- package/src/services/agentic-flow-hooks/neural-hooks.ts +758 -0
- package/src/services/agentic-flow-hooks/performance-hooks.ts +827 -0
- package/src/services/agentic-flow-hooks/types.ts +503 -0
- package/src/services/agentic-flow-hooks/workflow-hooks.ts +1026 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Provider Implementation
|
|
3
|
+
* Supports local models running via Ollama
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseProvider } from './base-provider.js';
|
|
7
|
+
import {
|
|
8
|
+
LLMProvider,
|
|
9
|
+
LLMModel,
|
|
10
|
+
LLMRequest,
|
|
11
|
+
LLMResponse,
|
|
12
|
+
LLMStreamEvent,
|
|
13
|
+
ModelInfo,
|
|
14
|
+
ProviderCapabilities,
|
|
15
|
+
HealthCheckResult,
|
|
16
|
+
LLMProviderError,
|
|
17
|
+
ProviderUnavailableError,
|
|
18
|
+
} from './types.js';
|
|
19
|
+
|
|
20
|
+
interface OllamaGenerateRequest {
|
|
21
|
+
model: string;
|
|
22
|
+
prompt: string;
|
|
23
|
+
system?: string;
|
|
24
|
+
template?: string;
|
|
25
|
+
context?: number[];
|
|
26
|
+
stream?: boolean;
|
|
27
|
+
raw?: boolean;
|
|
28
|
+
format?: 'json';
|
|
29
|
+
options?: {
|
|
30
|
+
temperature?: number;
|
|
31
|
+
top_k?: number;
|
|
32
|
+
top_p?: number;
|
|
33
|
+
num_predict?: number;
|
|
34
|
+
stop?: string[];
|
|
35
|
+
seed?: number;
|
|
36
|
+
num_ctx?: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface OllamaChatRequest {
|
|
41
|
+
model: string;
|
|
42
|
+
messages: Array<{
|
|
43
|
+
role: 'system' | 'user' | 'assistant';
|
|
44
|
+
content: string;
|
|
45
|
+
}>;
|
|
46
|
+
stream?: boolean;
|
|
47
|
+
format?: 'json';
|
|
48
|
+
options?: OllamaGenerateRequest['options'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface OllamaResponse {
|
|
52
|
+
model: string;
|
|
53
|
+
created_at: string;
|
|
54
|
+
response?: string;
|
|
55
|
+
message?: {
|
|
56
|
+
role: string;
|
|
57
|
+
content: string;
|
|
58
|
+
};
|
|
59
|
+
done: boolean;
|
|
60
|
+
context?: number[];
|
|
61
|
+
total_duration?: number;
|
|
62
|
+
load_duration?: number;
|
|
63
|
+
prompt_eval_count?: number;
|
|
64
|
+
prompt_eval_duration?: number;
|
|
65
|
+
eval_count?: number;
|
|
66
|
+
eval_duration?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface OllamaModelInfo {
|
|
70
|
+
name: string;
|
|
71
|
+
modified_at: string;
|
|
72
|
+
size: number;
|
|
73
|
+
digest: string;
|
|
74
|
+
details: {
|
|
75
|
+
format: string;
|
|
76
|
+
family: string;
|
|
77
|
+
families: string[] | null;
|
|
78
|
+
parameter_size: string;
|
|
79
|
+
quantization_level: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class OllamaProvider extends BaseProvider {
|
|
84
|
+
readonly name: LLMProvider = 'ollama';
|
|
85
|
+
readonly capabilities: ProviderCapabilities = {
|
|
86
|
+
supportedModels: [
|
|
87
|
+
'llama-2-7b',
|
|
88
|
+
'llama-2-13b',
|
|
89
|
+
'llama-2-70b',
|
|
90
|
+
'mistral-7b',
|
|
91
|
+
'mixtral-8x7b',
|
|
92
|
+
'custom-model',
|
|
93
|
+
],
|
|
94
|
+
maxContextLength: {
|
|
95
|
+
'llama-2-7b': 4096,
|
|
96
|
+
'llama-2-13b': 4096,
|
|
97
|
+
'llama-2-70b': 4096,
|
|
98
|
+
'mistral-7b': 8192,
|
|
99
|
+
'mixtral-8x7b': 32768,
|
|
100
|
+
'custom-model': 4096,
|
|
101
|
+
} as Record<LLMModel, number>,
|
|
102
|
+
maxOutputTokens: {
|
|
103
|
+
'llama-2-7b': 2048,
|
|
104
|
+
'llama-2-13b': 2048,
|
|
105
|
+
'llama-2-70b': 2048,
|
|
106
|
+
'mistral-7b': 4096,
|
|
107
|
+
'mixtral-8x7b': 4096,
|
|
108
|
+
'custom-model': 2048,
|
|
109
|
+
} as Record<LLMModel, number>,
|
|
110
|
+
supportsStreaming: true,
|
|
111
|
+
supportsFunctionCalling: false,
|
|
112
|
+
supportsSystemMessages: true,
|
|
113
|
+
supportsVision: false,
|
|
114
|
+
supportsAudio: false,
|
|
115
|
+
supportsTools: false,
|
|
116
|
+
supportsFineTuning: false,
|
|
117
|
+
supportsEmbeddings: true,
|
|
118
|
+
supportsLogprobs: false,
|
|
119
|
+
supportsBatching: false,
|
|
120
|
+
pricing: {
|
|
121
|
+
// Local models have no API cost
|
|
122
|
+
'llama-2-7b': { promptCostPer1k: 0, completionCostPer1k: 0, currency: 'USD' },
|
|
123
|
+
'llama-2-13b': { promptCostPer1k: 0, completionCostPer1k: 0, currency: 'USD' },
|
|
124
|
+
'llama-2-70b': { promptCostPer1k: 0, completionCostPer1k: 0, currency: 'USD' },
|
|
125
|
+
'mistral-7b': { promptCostPer1k: 0, completionCostPer1k: 0, currency: 'USD' },
|
|
126
|
+
'mixtral-8x7b': { promptCostPer1k: 0, completionCostPer1k: 0, currency: 'USD' },
|
|
127
|
+
'custom-model': { promptCostPer1k: 0, completionCostPer1k: 0, currency: 'USD' },
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
private baseUrl: string;
|
|
132
|
+
private availableModels: Set<string> = new Set();
|
|
133
|
+
|
|
134
|
+
protected async doInitialize(): Promise<void> {
|
|
135
|
+
this.baseUrl = this.config.apiUrl || 'http://localhost:11434';
|
|
136
|
+
|
|
137
|
+
// Check if Ollama is running and get available models
|
|
138
|
+
try {
|
|
139
|
+
await this.fetchAvailableModels();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
this.logger.warn('Failed to fetch Ollama models, will retry on first request', error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async fetchAvailableModels(): Promise<void> {
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(`${this.baseUrl}/api/tags`);
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error(`Failed to fetch models: ${response.status}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const data = await response.json();
|
|
153
|
+
this.availableModels.clear();
|
|
154
|
+
|
|
155
|
+
if (data.models && Array.isArray(data.models)) {
|
|
156
|
+
data.models.forEach((model: OllamaModelInfo) => {
|
|
157
|
+
this.availableModels.add(model.name);
|
|
158
|
+
// Map common model names
|
|
159
|
+
if (model.name.includes('llama2:7b')) {
|
|
160
|
+
this.availableModels.add('llama-2-7b');
|
|
161
|
+
} else if (model.name.includes('llama2:13b')) {
|
|
162
|
+
this.availableModels.add('llama-2-13b');
|
|
163
|
+
} else if (model.name.includes('llama2:70b')) {
|
|
164
|
+
this.availableModels.add('llama-2-70b');
|
|
165
|
+
} else if (model.name.includes('mistral')) {
|
|
166
|
+
this.availableModels.add('mistral-7b');
|
|
167
|
+
} else if (model.name.includes('mixtral')) {
|
|
168
|
+
this.availableModels.add('mixtral-8x7b');
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new ProviderUnavailableError('ollama', {
|
|
174
|
+
message: 'Ollama service is not available',
|
|
175
|
+
details: error,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
protected async doComplete(request: LLMRequest): Promise<LLMResponse> {
|
|
181
|
+
// Use chat endpoint for multi-turn conversations
|
|
182
|
+
const ollamaRequest: OllamaChatRequest = {
|
|
183
|
+
model: this.mapToOllamaModel(request.model || this.config.model),
|
|
184
|
+
messages: request.messages.map(msg => ({
|
|
185
|
+
role: msg.role === 'system' ? 'system' : msg.role === 'assistant' ? 'assistant' : 'user',
|
|
186
|
+
content: msg.content,
|
|
187
|
+
})),
|
|
188
|
+
stream: false,
|
|
189
|
+
options: {
|
|
190
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
191
|
+
top_k: request.topK ?? this.config.topK,
|
|
192
|
+
top_p: request.topP ?? this.config.topP,
|
|
193
|
+
num_predict: request.maxTokens ?? this.config.maxTokens,
|
|
194
|
+
stop: request.stopSequences ?? this.config.stopSequences,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const controller = new AbortController();
|
|
199
|
+
const timeout = setTimeout(() => controller.abort(), this.config.timeout || 120000); // Longer timeout for local models
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: {
|
|
205
|
+
'Content-Type': 'application/json',
|
|
206
|
+
},
|
|
207
|
+
body: JSON.stringify(ollamaRequest),
|
|
208
|
+
signal: controller.signal,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
clearTimeout(timeout);
|
|
212
|
+
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
await this.handleErrorResponse(response);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const data: OllamaResponse = await response.json();
|
|
218
|
+
|
|
219
|
+
// Calculate metrics
|
|
220
|
+
const promptTokens = data.prompt_eval_count || this.estimateTokens(JSON.stringify(request.messages));
|
|
221
|
+
const completionTokens = data.eval_count || this.estimateTokens(data.message?.content || '');
|
|
222
|
+
const totalDuration = data.total_duration ? data.total_duration / 1000000 : 0; // Convert nanoseconds to milliseconds
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
id: `ollama-${Date.now()}`,
|
|
226
|
+
model: request.model || this.config.model,
|
|
227
|
+
provider: 'ollama',
|
|
228
|
+
content: data.message?.content || '',
|
|
229
|
+
usage: {
|
|
230
|
+
promptTokens,
|
|
231
|
+
completionTokens,
|
|
232
|
+
totalTokens: promptTokens + completionTokens,
|
|
233
|
+
},
|
|
234
|
+
cost: {
|
|
235
|
+
promptCost: 0,
|
|
236
|
+
completionCost: 0,
|
|
237
|
+
totalCost: 0,
|
|
238
|
+
currency: 'USD',
|
|
239
|
+
},
|
|
240
|
+
latency: totalDuration,
|
|
241
|
+
finishReason: data.done ? 'stop' : 'length',
|
|
242
|
+
metadata: {
|
|
243
|
+
loadDuration: data.load_duration,
|
|
244
|
+
promptEvalDuration: data.prompt_eval_duration,
|
|
245
|
+
evalDuration: data.eval_duration,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
} catch (error) {
|
|
249
|
+
clearTimeout(timeout);
|
|
250
|
+
|
|
251
|
+
// Check if Ollama is running
|
|
252
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
253
|
+
throw new ProviderUnavailableError('ollama', {
|
|
254
|
+
message: 'Cannot connect to Ollama. Make sure Ollama is running on ' + this.baseUrl,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
throw this.transformError(error);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
protected async *doStreamComplete(request: LLMRequest): AsyncIterable<LLMStreamEvent> {
|
|
263
|
+
const ollamaRequest: OllamaChatRequest = {
|
|
264
|
+
model: this.mapToOllamaModel(request.model || this.config.model),
|
|
265
|
+
messages: request.messages.map(msg => ({
|
|
266
|
+
role: msg.role === 'system' ? 'system' : msg.role === 'assistant' ? 'assistant' : 'user',
|
|
267
|
+
content: msg.content,
|
|
268
|
+
})),
|
|
269
|
+
stream: true,
|
|
270
|
+
options: {
|
|
271
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
272
|
+
top_k: request.topK ?? this.config.topK,
|
|
273
|
+
top_p: request.topP ?? this.config.topP,
|
|
274
|
+
num_predict: request.maxTokens ?? this.config.maxTokens,
|
|
275
|
+
stop: request.stopSequences ?? this.config.stopSequences,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const controller = new AbortController();
|
|
280
|
+
const timeout = setTimeout(() => controller.abort(), (this.config.timeout || 120000) * 2);
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: {
|
|
286
|
+
'Content-Type': 'application/json',
|
|
287
|
+
},
|
|
288
|
+
body: JSON.stringify(ollamaRequest),
|
|
289
|
+
signal: controller.signal,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (!response.ok) {
|
|
293
|
+
await this.handleErrorResponse(response);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const reader = response.body!.getReader();
|
|
297
|
+
const decoder = new TextDecoder();
|
|
298
|
+
let buffer = '';
|
|
299
|
+
let totalContent = '';
|
|
300
|
+
let promptTokens = 0;
|
|
301
|
+
let completionTokens = 0;
|
|
302
|
+
|
|
303
|
+
while (true) {
|
|
304
|
+
const { done, value } = await reader.read();
|
|
305
|
+
if (done) break;
|
|
306
|
+
|
|
307
|
+
buffer += decoder.decode(value, { stream: true });
|
|
308
|
+
const lines = buffer.split('\n');
|
|
309
|
+
buffer = lines.pop() || '';
|
|
310
|
+
|
|
311
|
+
for (const line of lines) {
|
|
312
|
+
if (line.trim() === '') continue;
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
const data: OllamaResponse = JSON.parse(line);
|
|
316
|
+
|
|
317
|
+
if (data.message?.content) {
|
|
318
|
+
totalContent += data.message.content;
|
|
319
|
+
yield {
|
|
320
|
+
type: 'content',
|
|
321
|
+
delta: { content: data.message.content },
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (data.done) {
|
|
326
|
+
promptTokens = data.prompt_eval_count || this.estimateTokens(JSON.stringify(request.messages));
|
|
327
|
+
completionTokens = data.eval_count || this.estimateTokens(totalContent);
|
|
328
|
+
|
|
329
|
+
yield {
|
|
330
|
+
type: 'done',
|
|
331
|
+
usage: {
|
|
332
|
+
promptTokens,
|
|
333
|
+
completionTokens,
|
|
334
|
+
totalTokens: promptTokens + completionTokens,
|
|
335
|
+
},
|
|
336
|
+
cost: {
|
|
337
|
+
promptCost: 0,
|
|
338
|
+
completionCost: 0,
|
|
339
|
+
totalCost: 0,
|
|
340
|
+
currency: 'USD',
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
} catch (e) {
|
|
345
|
+
this.logger.warn('Failed to parse Ollama stream chunk', { line, error: e });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
clearTimeout(timeout);
|
|
351
|
+
|
|
352
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
353
|
+
throw new ProviderUnavailableError('ollama', {
|
|
354
|
+
message: 'Cannot connect to Ollama. Make sure Ollama is running on ' + this.baseUrl,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
throw this.transformError(error);
|
|
359
|
+
} finally {
|
|
360
|
+
clearTimeout(timeout);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async listModels(): Promise<LLMModel[]> {
|
|
365
|
+
// Refresh available models
|
|
366
|
+
await this.fetchAvailableModels();
|
|
367
|
+
|
|
368
|
+
// Return intersection of supported models and available models
|
|
369
|
+
return this.capabilities.supportedModels.filter(model =>
|
|
370
|
+
this.availableModels.has(this.mapToOllamaModel(model))
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async getModelInfo(model: LLMModel): Promise<ModelInfo> {
|
|
375
|
+
const ollamaModel = this.mapToOllamaModel(model);
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const response = await fetch(`${this.baseUrl}/api/show`, {
|
|
379
|
+
method: 'POST',
|
|
380
|
+
headers: {
|
|
381
|
+
'Content-Type': 'application/json',
|
|
382
|
+
},
|
|
383
|
+
body: JSON.stringify({ name: ollamaModel }),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
throw new Error('Model not found');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const data = await response.json();
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
model,
|
|
394
|
+
name: data.name || model,
|
|
395
|
+
description: data.description || this.getModelDescription(model),
|
|
396
|
+
contextLength: this.capabilities.maxContextLength[model] || 4096,
|
|
397
|
+
maxOutputTokens: this.capabilities.maxOutputTokens[model] || 2048,
|
|
398
|
+
supportedFeatures: ['chat', 'completion'],
|
|
399
|
+
pricing: this.capabilities.pricing![model],
|
|
400
|
+
metadata: {
|
|
401
|
+
parameterSize: data.details?.parameter_size,
|
|
402
|
+
quantization: data.details?.quantization_level,
|
|
403
|
+
format: data.details?.format,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
} catch (error) {
|
|
407
|
+
// Fallback to default info
|
|
408
|
+
return {
|
|
409
|
+
model,
|
|
410
|
+
name: model,
|
|
411
|
+
description: this.getModelDescription(model),
|
|
412
|
+
contextLength: this.capabilities.maxContextLength[model] || 4096,
|
|
413
|
+
maxOutputTokens: this.capabilities.maxOutputTokens[model] || 2048,
|
|
414
|
+
supportedFeatures: ['chat', 'completion'],
|
|
415
|
+
pricing: this.capabilities.pricing![model],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
protected async doHealthCheck(): Promise<HealthCheckResult> {
|
|
421
|
+
try {
|
|
422
|
+
const response = await fetch(`${this.baseUrl}/api/tags`);
|
|
423
|
+
|
|
424
|
+
if (!response.ok) {
|
|
425
|
+
throw new Error(`Health check failed: ${response.status}`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
healthy: true,
|
|
430
|
+
timestamp: new Date(),
|
|
431
|
+
details: {
|
|
432
|
+
modelsAvailable: this.availableModels.size,
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
} catch (error) {
|
|
436
|
+
return {
|
|
437
|
+
healthy: false,
|
|
438
|
+
error: error instanceof Error ? error.message : 'Cannot connect to Ollama',
|
|
439
|
+
timestamp: new Date(),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private mapToOllamaModel(model: LLMModel): string {
|
|
445
|
+
const modelMap: Record<string, string> = {
|
|
446
|
+
'llama-2-7b': 'llama2:7b',
|
|
447
|
+
'llama-2-13b': 'llama2:13b',
|
|
448
|
+
'llama-2-70b': 'llama2:70b',
|
|
449
|
+
'mistral-7b': 'mistral:7b',
|
|
450
|
+
'mixtral-8x7b': 'mixtral:8x7b',
|
|
451
|
+
'custom-model': this.config.providerOptions?.customModel || 'llama2:latest',
|
|
452
|
+
};
|
|
453
|
+
return modelMap[model] || model;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
private getModelDescription(model: LLMModel): string {
|
|
457
|
+
const descriptions: Record<string, string> = {
|
|
458
|
+
'llama-2-7b': 'Llama 2 7B - Efficient open-source model',
|
|
459
|
+
'llama-2-13b': 'Llama 2 13B - Balanced performance model',
|
|
460
|
+
'llama-2-70b': 'Llama 2 70B - Large open-source model',
|
|
461
|
+
'mistral-7b': 'Mistral 7B - Fast and efficient model',
|
|
462
|
+
'mixtral-8x7b': 'Mixtral 8x7B - Mixture of experts model',
|
|
463
|
+
'custom-model': 'Custom local model',
|
|
464
|
+
};
|
|
465
|
+
return descriptions[model] || 'Local language model via Ollama';
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private async handleErrorResponse(response: Response): Promise<void> {
|
|
469
|
+
const errorText = await response.text();
|
|
470
|
+
let errorData: any;
|
|
471
|
+
|
|
472
|
+
try {
|
|
473
|
+
errorData = JSON.parse(errorText);
|
|
474
|
+
} catch {
|
|
475
|
+
errorData = { error: errorText };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const message = errorData.error || 'Unknown error';
|
|
479
|
+
|
|
480
|
+
throw new LLMProviderError(
|
|
481
|
+
message,
|
|
482
|
+
`OLLAMA_${response.status}`,
|
|
483
|
+
'ollama',
|
|
484
|
+
response.status,
|
|
485
|
+
response.status >= 500,
|
|
486
|
+
errorData
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
}
|