activo 0.4.3 → 0.5.0
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 +203 -1
- package/data/2026-03-04_20-54.json +181 -0
- package/data/2026-03-04_20-56.json +181 -0
- package/data/apex-rulesets/egov.yaml +469 -0
- package/data/apex-rulesets/modernize.yaml +687 -0
- package/data/apex-rulesets/quality.yaml +1677 -0
- package/data/apex-rulesets/rule-schema.yaml +587 -0
- package/data/apex-rulesets/secure.yaml +1688 -0
- package/data/apex-rulesets/spring.yaml +455 -0
- package/data/apex-rulesets/sql-format.yaml +99 -0
- package/data/apex-rulesets/sql-oracle.yaml +281 -0
- package/data/apex-rulesets/sql.yaml +1660 -0
- package/dist/cli/headless.d.ts.map +1 -1
- package/dist/cli/headless.js +32 -10
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/index.js +31 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/core/agent.d.ts +3 -3
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +255 -17
- package/dist/core/agent.js.map +1 -1
- package/dist/core/commands.d.ts +2 -1
- package/dist/core/commands.d.ts.map +1 -1
- package/dist/core/commands.js +61 -9
- package/dist/core/commands.js.map +1 -1
- package/dist/core/config.d.ts +14 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +41 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/conversation.d.ts +2 -2
- package/dist/core/conversation.d.ts.map +1 -1
- package/dist/core/conversation.js.map +1 -1
- package/dist/core/intentRouter.d.ts +43 -0
- package/dist/core/intentRouter.d.ts.map +1 -0
- package/dist/core/intentRouter.js +804 -0
- package/dist/core/intentRouter.js.map +1 -0
- package/dist/core/llm/anthropic.d.ts +24 -0
- package/dist/core/llm/anthropic.d.ts.map +1 -0
- package/dist/core/llm/anthropic.js +226 -0
- package/dist/core/llm/anthropic.js.map +1 -0
- package/dist/core/llm/ollama.d.ts +5 -14
- package/dist/core/llm/ollama.d.ts.map +1 -1
- package/dist/core/llm/ollama.js +3 -0
- package/dist/core/llm/ollama.js.map +1 -1
- package/dist/core/llm/types.d.ts +22 -0
- package/dist/core/llm/types.d.ts.map +1 -0
- package/dist/core/llm/types.js +2 -0
- package/dist/core/llm/types.js.map +1 -0
- package/dist/core/mcp/client.d.ts +6 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +16 -0
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/init.d.ts +12 -0
- package/dist/core/mcp/init.d.ts.map +1 -0
- package/dist/core/mcp/init.js +55 -0
- package/dist/core/mcp/init.js.map +1 -0
- package/dist/core/mcp/logger.d.ts +14 -0
- package/dist/core/mcp/logger.d.ts.map +1 -0
- package/dist/core/mcp/logger.js +50 -0
- package/dist/core/mcp/logger.js.map +1 -0
- package/dist/core/tools/analyzeAll.d.ts.map +1 -1
- package/dist/core/tools/analyzeAll.js +16 -28
- package/dist/core/tools/analyzeAll.js.map +1 -1
- package/dist/core/tools/analyzePatterns.d.ts +3 -0
- package/dist/core/tools/analyzePatterns.d.ts.map +1 -0
- package/dist/core/tools/analyzePatterns.js +293 -0
- package/dist/core/tools/analyzePatterns.js.map +1 -0
- package/dist/core/tools/apexPaths.d.ts +14 -0
- package/dist/core/tools/apexPaths.d.ts.map +1 -0
- package/dist/core/tools/apexPaths.js +54 -0
- package/dist/core/tools/apexPaths.js.map +1 -0
- package/dist/core/tools/apexUtils.d.ts +36 -0
- package/dist/core/tools/apexUtils.d.ts.map +1 -0
- package/dist/core/tools/apexUtils.js +83 -0
- package/dist/core/tools/apexUtils.js.map +1 -0
- package/dist/core/tools/explainIssue.d.ts +3 -0
- package/dist/core/tools/explainIssue.d.ts.map +1 -0
- package/dist/core/tools/explainIssue.js +181 -0
- package/dist/core/tools/explainIssue.js.map +1 -0
- package/dist/core/tools/fixGen.d.ts +3 -0
- package/dist/core/tools/fixGen.d.ts.map +1 -0
- package/dist/core/tools/fixGen.js +338 -0
- package/dist/core/tools/fixGen.js.map +1 -0
- package/dist/core/tools/generateImprovements.d.ts +21 -0
- package/dist/core/tools/generateImprovements.d.ts.map +1 -0
- package/dist/core/tools/generateImprovements.js +602 -0
- package/dist/core/tools/generateImprovements.js.map +1 -0
- package/dist/core/tools/generateReport.d.ts +3 -0
- package/dist/core/tools/generateReport.d.ts.map +1 -0
- package/dist/core/tools/generateReport.js +315 -0
- package/dist/core/tools/generateReport.js.map +1 -0
- package/dist/core/tools/index.d.ts +7 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +62 -23
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/javaAst.d.ts.map +1 -1
- package/dist/core/tools/javaAst.js +191 -0
- package/dist/core/tools/javaAst.js.map +1 -1
- package/dist/core/tools/recommendProfile.d.ts +3 -0
- package/dist/core/tools/recommendProfile.d.ts.map +1 -0
- package/dist/core/tools/recommendProfile.js +334 -0
- package/dist/core/tools/recommendProfile.js.map +1 -0
- package/dist/core/tools/ruleGen.d.ts +3 -0
- package/dist/core/tools/ruleGen.d.ts.map +1 -0
- package/dist/core/tools/ruleGen.js +1103 -0
- package/dist/core/tools/ruleGen.js.map +1 -0
- package/dist/core/tools/standards.d.ts.map +1 -1
- package/dist/core/tools/standards.js +7 -3
- package/dist/core/tools/standards.js.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +86 -35
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/InputBox.d.ts +1 -3
- package/dist/ui/components/InputBox.d.ts.map +1 -1
- package/dist/ui/components/InputBox.js +146 -5
- package/dist/ui/components/InputBox.js.map +1 -1
- package/dist/ui/components/MessageList.d.ts +3 -1
- package/dist/ui/components/MessageList.d.ts.map +1 -1
- package/dist/ui/components/MessageList.js +13 -7
- package/dist/ui/components/MessageList.js.map +1 -1
- package/dist/ui/components/StatusBar.d.ts +1 -1
- package/dist/ui/components/StatusBar.d.ts.map +1 -1
- package/dist/ui/components/StatusBar.js +3 -2
- package/dist/ui/components/StatusBar.js.map +1 -1
- package/dist/ui/components/ToolStatus.d.ts +3 -1
- package/dist/ui/components/ToolStatus.d.ts.map +1 -1
- package/dist/ui/components/ToolStatus.js +19 -4
- package/dist/ui/components/ToolStatus.js.map +1 -1
- package/package.json +7 -1
- package/demo.gif +0 -0
- package/demo.tape +0 -53
- package/screenshot.png +0 -0
- package/src/cli/banner.ts +0 -38
- package/src/cli/headless.ts +0 -63
- package/src/cli/index.ts +0 -57
- package/src/core/agent.ts +0 -237
- package/src/core/commands.ts +0 -118
- package/src/core/config.ts +0 -98
- package/src/core/conversation.ts +0 -235
- package/src/core/llm/ollama.ts +0 -351
- package/src/core/mcp/client.ts +0 -143
- package/src/core/tools/analyzeAll.ts +0 -494
- package/src/core/tools/ast.ts +0 -826
- package/src/core/tools/builtIn.ts +0 -221
- package/src/core/tools/cache.ts +0 -570
- package/src/core/tools/cssAnalysis.ts +0 -324
- package/src/core/tools/dependencyAnalysis.ts +0 -363
- package/src/core/tools/embeddings.ts +0 -746
- package/src/core/tools/frontendAst.ts +0 -802
- package/src/core/tools/htmlAnalysis.ts +0 -466
- package/src/core/tools/index.ts +0 -160
- package/src/core/tools/javaAst.ts +0 -812
- package/src/core/tools/memory.ts +0 -655
- package/src/core/tools/mybatisAnalysis.ts +0 -322
- package/src/core/tools/openapiAnalysis.ts +0 -431
- package/src/core/tools/pythonAnalysis.ts +0 -477
- package/src/core/tools/sqlAnalysis.ts +0 -298
- package/src/core/tools/standards.test.ts +0 -186
- package/src/core/tools/standards.ts +0 -889
- package/src/core/tools/types.ts +0 -38
- package/src/ui/App.tsx +0 -334
- package/src/ui/components/InputBox.tsx +0 -37
- package/src/ui/components/MessageList.tsx +0 -80
- package/src/ui/components/StatusBar.tsx +0 -36
- package/src/ui/components/ToolStatus.tsx +0 -38
- package/tsconfig.json +0 -21
package/src/core/llm/ollama.ts
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import { OllamaConfig } from "../config.js";
|
|
2
|
-
import type { Tool, ToolCall } from "../tools/types.js";
|
|
3
|
-
|
|
4
|
-
export interface ChatMessage {
|
|
5
|
-
role: "system" | "user" | "assistant" | "tool";
|
|
6
|
-
content: string;
|
|
7
|
-
toolCalls?: ToolCall[];
|
|
8
|
-
toolCallId?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface StreamEvent {
|
|
12
|
-
type: "content" | "tool_call" | "done" | "error";
|
|
13
|
-
content?: string;
|
|
14
|
-
toolCall?: ToolCall;
|
|
15
|
-
error?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface OllamaChatResponse {
|
|
19
|
-
model: string;
|
|
20
|
-
message: {
|
|
21
|
-
role: string;
|
|
22
|
-
content: string;
|
|
23
|
-
tool_calls?: Array<{
|
|
24
|
-
function: {
|
|
25
|
-
name: string;
|
|
26
|
-
arguments: Record<string, unknown>;
|
|
27
|
-
};
|
|
28
|
-
}>;
|
|
29
|
-
};
|
|
30
|
-
done: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Estimate token count for a string (rough: ~3 chars per token for mixed Korean/English)
|
|
34
|
-
function estimateTokens(text: string): number {
|
|
35
|
-
if (!text) return 0;
|
|
36
|
-
return Math.ceil(text.length / 3);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Estimate tokens for tool definitions sent to Ollama
|
|
40
|
-
function estimateToolTokens(tools: Tool[]): number {
|
|
41
|
-
let total = 0;
|
|
42
|
-
for (const tool of tools) {
|
|
43
|
-
// name + description + JSON schema overhead
|
|
44
|
-
total += estimateTokens(tool.name) + estimateTokens(tool.description) + 20;
|
|
45
|
-
if (tool.parameters?.properties) {
|
|
46
|
-
for (const [key, prop] of Object.entries(tool.parameters.properties)) {
|
|
47
|
-
total += estimateTokens(key) + estimateTokens((prop as { description?: string }).description || "") + 10;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return total;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Prune messages to fit within context window
|
|
55
|
-
function pruneMessages(
|
|
56
|
-
messages: Array<{ role: string; content: string }>,
|
|
57
|
-
maxContextTokens: number,
|
|
58
|
-
toolTokens: number
|
|
59
|
-
): Array<{ role: string; content: string }> {
|
|
60
|
-
if (messages.length <= 2) return messages;
|
|
61
|
-
|
|
62
|
-
const responseReserve = 1000; // reserve tokens for model response
|
|
63
|
-
const safetyBuffer = 200;
|
|
64
|
-
const budget = maxContextTokens - toolTokens - responseReserve - safetyBuffer;
|
|
65
|
-
|
|
66
|
-
// Always preserve: first message (system) and last message (user)
|
|
67
|
-
const systemMsg = messages[0];
|
|
68
|
-
const lastMsg = messages[messages.length - 1];
|
|
69
|
-
const history = messages.slice(1, -1);
|
|
70
|
-
|
|
71
|
-
let usedTokens = estimateTokens(systemMsg.content) + estimateTokens(lastMsg.content);
|
|
72
|
-
|
|
73
|
-
// If even system + last message exceeds budget, truncate system prompt
|
|
74
|
-
if (usedTokens > budget) {
|
|
75
|
-
const maxSystemChars = Math.max(200, (budget - estimateTokens(lastMsg.content)) * 3);
|
|
76
|
-
systemMsg.content = systemMsg.content.slice(0, maxSystemChars);
|
|
77
|
-
return [systemMsg, lastMsg];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Add messages from newest to oldest
|
|
81
|
-
const kept: Array<{ role: string; content: string }> = [];
|
|
82
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
83
|
-
const msgTokens = estimateTokens(history[i].content) + 4; // 4 tokens overhead per message
|
|
84
|
-
if (usedTokens + msgTokens > budget) break;
|
|
85
|
-
kept.unshift(history[i]);
|
|
86
|
-
usedTokens += msgTokens;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return [systemMsg, ...kept, lastMsg];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export class OllamaClient {
|
|
93
|
-
private baseUrl: string;
|
|
94
|
-
private model: string;
|
|
95
|
-
private contextLength: number;
|
|
96
|
-
private keepAlive: number;
|
|
97
|
-
|
|
98
|
-
constructor(config: OllamaConfig) {
|
|
99
|
-
this.baseUrl = config.baseUrl;
|
|
100
|
-
this.model = config.model;
|
|
101
|
-
this.contextLength = config.contextLength;
|
|
102
|
-
this.keepAlive = config.keepAlive;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async isConnected(): Promise<boolean> {
|
|
106
|
-
try {
|
|
107
|
-
const response = await fetch(`${this.baseUrl}/api/tags`);
|
|
108
|
-
return response.ok;
|
|
109
|
-
} catch {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async listModels(): Promise<string[]> {
|
|
115
|
-
const response = await fetch(`${this.baseUrl}/api/tags`);
|
|
116
|
-
if (!response.ok) throw new Error("Failed to list models");
|
|
117
|
-
const data = (await response.json()) as { models: Array<{ name: string }> };
|
|
118
|
-
return data.models.map((m) => m.name);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async chat(
|
|
122
|
-
messages: ChatMessage[],
|
|
123
|
-
tools?: Tool[]
|
|
124
|
-
): Promise<ChatMessage> {
|
|
125
|
-
let ollamaMessages = this.convertMessages(messages);
|
|
126
|
-
|
|
127
|
-
// Prune messages to fit within context window
|
|
128
|
-
const toolTokens = tools?.length ? estimateToolTokens(tools) : 0;
|
|
129
|
-
ollamaMessages = pruneMessages(ollamaMessages, this.contextLength, toolTokens);
|
|
130
|
-
|
|
131
|
-
const body: Record<string, unknown> = {
|
|
132
|
-
model: this.model,
|
|
133
|
-
messages: ollamaMessages,
|
|
134
|
-
stream: false,
|
|
135
|
-
options: {
|
|
136
|
-
num_ctx: this.contextLength,
|
|
137
|
-
},
|
|
138
|
-
keep_alive: this.keepAlive,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// Always add tools if provided (needed for multi-turn tool use)
|
|
142
|
-
if (tools?.length) {
|
|
143
|
-
body.tools = tools.map((tool) => ({
|
|
144
|
-
type: "function",
|
|
145
|
-
function: {
|
|
146
|
-
name: tool.name,
|
|
147
|
-
description: tool.description,
|
|
148
|
-
parameters: tool.parameters,
|
|
149
|
-
},
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
154
|
-
method: "POST",
|
|
155
|
-
headers: { "Content-Type": "application/json" },
|
|
156
|
-
body: JSON.stringify(body),
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
if (!response.ok) {
|
|
160
|
-
const error = await response.text();
|
|
161
|
-
throw new Error(`Ollama error: ${error}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const data = (await response.json()) as OllamaChatResponse;
|
|
165
|
-
return this.parseResponse(data);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async *streamChat(
|
|
169
|
-
messages: ChatMessage[],
|
|
170
|
-
tools?: Tool[],
|
|
171
|
-
abortSignal?: AbortSignal
|
|
172
|
-
): AsyncGenerator<StreamEvent> {
|
|
173
|
-
let ollamaMessages = this.convertMessages(messages);
|
|
174
|
-
|
|
175
|
-
// Prune messages to fit within context window
|
|
176
|
-
const toolTokens = tools?.length ? estimateToolTokens(tools) : 0;
|
|
177
|
-
ollamaMessages = pruneMessages(ollamaMessages, this.contextLength, toolTokens);
|
|
178
|
-
|
|
179
|
-
// Use non-streaming mode when tools are provided to avoid hallucination
|
|
180
|
-
// (LLM often outputs fake results before tool calls in streaming mode)
|
|
181
|
-
const useStreaming = !tools?.length;
|
|
182
|
-
|
|
183
|
-
const body: Record<string, unknown> = {
|
|
184
|
-
model: this.model,
|
|
185
|
-
messages: ollamaMessages,
|
|
186
|
-
stream: useStreaming,
|
|
187
|
-
options: {
|
|
188
|
-
num_ctx: this.contextLength,
|
|
189
|
-
},
|
|
190
|
-
keep_alive: this.keepAlive,
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// Always add tools if provided (needed for multi-turn tool use)
|
|
194
|
-
if (tools?.length) {
|
|
195
|
-
body.tools = tools.map((tool) => ({
|
|
196
|
-
type: "function",
|
|
197
|
-
function: {
|
|
198
|
-
name: tool.name,
|
|
199
|
-
description: tool.description,
|
|
200
|
-
parameters: tool.parameters,
|
|
201
|
-
},
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
206
|
-
method: "POST",
|
|
207
|
-
headers: { "Content-Type": "application/json" },
|
|
208
|
-
body: JSON.stringify(body),
|
|
209
|
-
signal: abortSignal,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
if (!response.ok) {
|
|
213
|
-
const error = await response.text();
|
|
214
|
-
yield { type: "error", error: `Ollama error: ${error}` };
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Non-streaming mode for tool calls (avoids hallucination where LLM outputs text before calling tools)
|
|
219
|
-
if (!useStreaming) {
|
|
220
|
-
const data = (await response.json()) as OllamaChatResponse;
|
|
221
|
-
|
|
222
|
-
// Handle tool calls first (ignore content when tools are called)
|
|
223
|
-
if (data.message?.tool_calls?.length) {
|
|
224
|
-
for (const tc of data.message.tool_calls) {
|
|
225
|
-
const toolCall: ToolCall = {
|
|
226
|
-
id: `tc_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
227
|
-
name: tc.function.name,
|
|
228
|
-
arguments: tc.function.arguments,
|
|
229
|
-
};
|
|
230
|
-
yield { type: "tool_call", toolCall };
|
|
231
|
-
}
|
|
232
|
-
} else if (data.message?.content) {
|
|
233
|
-
// Only yield content if no tool calls
|
|
234
|
-
yield { type: "content", content: data.message.content };
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
yield { type: "done" };
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Streaming mode (no tools)
|
|
242
|
-
const reader = response.body?.getReader();
|
|
243
|
-
if (!reader) {
|
|
244
|
-
yield { type: "error", error: "No response body" };
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const decoder = new TextDecoder();
|
|
249
|
-
let buffer = "";
|
|
250
|
-
|
|
251
|
-
while (true) {
|
|
252
|
-
const { done, value } = await reader.read();
|
|
253
|
-
if (done) break;
|
|
254
|
-
|
|
255
|
-
buffer += decoder.decode(value, { stream: true });
|
|
256
|
-
const lines = buffer.split("\n");
|
|
257
|
-
buffer = lines.pop() || "";
|
|
258
|
-
|
|
259
|
-
for (const line of lines) {
|
|
260
|
-
if (!line.trim()) continue;
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
const data = JSON.parse(line) as OllamaChatResponse;
|
|
264
|
-
|
|
265
|
-
if (data.message?.content) {
|
|
266
|
-
yield { type: "content", content: data.message.content };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (data.done) {
|
|
270
|
-
yield { type: "done" };
|
|
271
|
-
}
|
|
272
|
-
} catch {
|
|
273
|
-
// Skip invalid JSON
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
private convertMessages(messages: ChatMessage[]): Array<{
|
|
280
|
-
role: string;
|
|
281
|
-
content: string;
|
|
282
|
-
}> {
|
|
283
|
-
return messages.map((msg) => {
|
|
284
|
-
if (msg.role === "tool") {
|
|
285
|
-
return {
|
|
286
|
-
role: "tool",
|
|
287
|
-
content: msg.content,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
return {
|
|
291
|
-
role: msg.role,
|
|
292
|
-
content: msg.content,
|
|
293
|
-
};
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
private parseResponse(data: OllamaChatResponse): ChatMessage {
|
|
298
|
-
const result: ChatMessage = {
|
|
299
|
-
role: "assistant",
|
|
300
|
-
content: data.message.content || "",
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
if (data.message.tool_calls?.length) {
|
|
304
|
-
result.toolCalls = data.message.tool_calls.map((tc, idx) => ({
|
|
305
|
-
id: `tc_${Date.now()}_${idx}`,
|
|
306
|
-
name: tc.function.name,
|
|
307
|
-
arguments: tc.function.arguments,
|
|
308
|
-
}));
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return result;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
getModel(): string {
|
|
315
|
-
return this.model;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
setModel(model: string): void {
|
|
319
|
-
this.model = model;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Generate embeddings for text
|
|
323
|
-
async embed(text: string, model?: string): Promise<number[]> {
|
|
324
|
-
const response = await fetch(`${this.baseUrl}/api/embeddings`, {
|
|
325
|
-
method: "POST",
|
|
326
|
-
headers: { "Content-Type": "application/json" },
|
|
327
|
-
body: JSON.stringify({
|
|
328
|
-
model: model || "nomic-embed-text",
|
|
329
|
-
prompt: text,
|
|
330
|
-
}),
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
if (!response.ok) {
|
|
334
|
-
const error = await response.text();
|
|
335
|
-
throw new Error(`Ollama embedding error: ${error}`);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const data = (await response.json()) as { embedding: number[] };
|
|
339
|
-
return data.embedding;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Generate embeddings for multiple texts (batch)
|
|
343
|
-
async embedBatch(texts: string[], model?: string): Promise<number[][]> {
|
|
344
|
-
const embeddings: number[][] = [];
|
|
345
|
-
for (const text of texts) {
|
|
346
|
-
const embedding = await this.embed(text, model);
|
|
347
|
-
embeddings.push(embedding);
|
|
348
|
-
}
|
|
349
|
-
return embeddings;
|
|
350
|
-
}
|
|
351
|
-
}
|
package/src/core/mcp/client.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
-
import { MCPServerConfig } from "../config.js";
|
|
4
|
-
import { Tool, ToolResult } from "../tools/types.js";
|
|
5
|
-
|
|
6
|
-
export interface MCPConnection {
|
|
7
|
-
id: string;
|
|
8
|
-
client: Client;
|
|
9
|
-
transport: StdioClientTransport;
|
|
10
|
-
tools: Tool[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class MCPManager {
|
|
14
|
-
private connections: Map<string, MCPConnection> = new Map();
|
|
15
|
-
|
|
16
|
-
async connect(id: string, config: MCPServerConfig): Promise<MCPConnection> {
|
|
17
|
-
// Check if already connected
|
|
18
|
-
if (this.connections.has(id)) {
|
|
19
|
-
return this.connections.get(id)!;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const transport = new StdioClientTransport({
|
|
23
|
-
command: config.command,
|
|
24
|
-
args: config.args,
|
|
25
|
-
env: config.env,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const client = new Client({
|
|
29
|
-
name: "activo",
|
|
30
|
-
version: "0.2.0",
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
await client.connect(transport);
|
|
34
|
-
|
|
35
|
-
// Get available tools
|
|
36
|
-
const toolsResult = await client.listTools();
|
|
37
|
-
const tools: Tool[] = toolsResult.tools.map((t) => ({
|
|
38
|
-
name: `mcp_${id}_${t.name}`,
|
|
39
|
-
description: t.description || `MCP tool: ${t.name}`,
|
|
40
|
-
parameters: {
|
|
41
|
-
type: "object" as const,
|
|
42
|
-
properties: (t.inputSchema as any)?.properties || {},
|
|
43
|
-
required: (t.inputSchema as any)?.required,
|
|
44
|
-
},
|
|
45
|
-
handler: async (args: Record<string, unknown>): Promise<ToolResult> => {
|
|
46
|
-
return this.callTool(id, t.name, args);
|
|
47
|
-
},
|
|
48
|
-
}));
|
|
49
|
-
|
|
50
|
-
const connection: MCPConnection = {
|
|
51
|
-
id,
|
|
52
|
-
client,
|
|
53
|
-
transport,
|
|
54
|
-
tools,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
this.connections.set(id, connection);
|
|
58
|
-
return connection;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async disconnect(id: string): Promise<void> {
|
|
62
|
-
const connection = this.connections.get(id);
|
|
63
|
-
if (connection) {
|
|
64
|
-
await connection.client.close();
|
|
65
|
-
this.connections.delete(id);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async disconnectAll(): Promise<void> {
|
|
70
|
-
for (const id of this.connections.keys()) {
|
|
71
|
-
await this.disconnect(id);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async callTool(connectionId: string, toolName: string, args: Record<string, unknown>): Promise<ToolResult> {
|
|
76
|
-
const connection = this.connections.get(connectionId);
|
|
77
|
-
if (!connection) {
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
content: "",
|
|
81
|
-
error: `MCP connection not found: ${connectionId}`,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const result = await connection.client.callTool({
|
|
87
|
-
name: toolName,
|
|
88
|
-
arguments: args,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (result.isError) {
|
|
92
|
-
return {
|
|
93
|
-
success: false,
|
|
94
|
-
content: "",
|
|
95
|
-
error: JSON.stringify(result.content),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const contentArray = result.content as Array<{ type: string; text?: string }>;
|
|
100
|
-
const content = contentArray
|
|
101
|
-
.filter((c) => c.type === "text" && c.text)
|
|
102
|
-
.map((c) => c.text!)
|
|
103
|
-
.join("\n");
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
success: true,
|
|
107
|
-
content,
|
|
108
|
-
};
|
|
109
|
-
} catch (error) {
|
|
110
|
-
return {
|
|
111
|
-
success: false,
|
|
112
|
-
content: "",
|
|
113
|
-
error: String(error),
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
getAllTools(): Tool[] {
|
|
119
|
-
const tools: Tool[] = [];
|
|
120
|
-
for (const connection of this.connections.values()) {
|
|
121
|
-
tools.push(...connection.tools);
|
|
122
|
-
}
|
|
123
|
-
return tools;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
getConnection(id: string): MCPConnection | undefined {
|
|
127
|
-
return this.connections.get(id);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
isConnected(id: string): boolean {
|
|
131
|
-
return this.connections.has(id);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Singleton instance
|
|
136
|
-
let mcpManager: MCPManager | null = null;
|
|
137
|
-
|
|
138
|
-
export function getMCPManager(): MCPManager {
|
|
139
|
-
if (!mcpManager) {
|
|
140
|
-
mcpManager = new MCPManager();
|
|
141
|
-
}
|
|
142
|
-
return mcpManager;
|
|
143
|
-
}
|