prab-cli 1.0.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/LICENSE +15 -0
- package/README.md +272 -0
- package/dist/index.js +374 -0
- package/dist/lib/chat-handler.js +219 -0
- package/dist/lib/config.js +156 -0
- package/dist/lib/context.js +44 -0
- package/dist/lib/groq-models.js +53 -0
- package/dist/lib/groq.js +33 -0
- package/dist/lib/models/groq-provider.js +82 -0
- package/dist/lib/models/provider.js +10 -0
- package/dist/lib/models/registry.js +101 -0
- package/dist/lib/safety.js +109 -0
- package/dist/lib/tools/base.js +115 -0
- package/dist/lib/tools/executor.js +127 -0
- package/dist/lib/tools/file-tools.js +283 -0
- package/dist/lib/tools/git-tools.js +280 -0
- package/dist/lib/tools/shell-tools.js +73 -0
- package/dist/lib/tools/todo-tool.js +105 -0
- package/dist/lib/tracker.js +314 -0
- package/dist/lib/ui.js +578 -0
- package/dist/log-viewer.js +374 -0
- package/dist/types/index.js +5 -0
- package/package.json +75 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChatHandler = void 0;
|
|
4
|
+
const messages_1 = require("@langchain/core/messages");
|
|
5
|
+
const context_1 = require("./context");
|
|
6
|
+
const ui_1 = require("./ui");
|
|
7
|
+
const tracker_1 = require("./tracker");
|
|
8
|
+
/**
|
|
9
|
+
* Handles chat interactions with AI, tool calling, and context management
|
|
10
|
+
*/
|
|
11
|
+
class ChatHandler {
|
|
12
|
+
constructor(toolRegistry, toolExecutor, modelProvider, initialContext) {
|
|
13
|
+
this.messages = [];
|
|
14
|
+
this.contextMessage = '';
|
|
15
|
+
this.toolRegistry = toolRegistry;
|
|
16
|
+
this.toolExecutor = toolExecutor;
|
|
17
|
+
this.modelProvider = modelProvider;
|
|
18
|
+
this.contextMessage = initialContext;
|
|
19
|
+
// Initialize with system message
|
|
20
|
+
this.initializeSystemMessage();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Initialize system message with tool descriptions and context
|
|
24
|
+
*/
|
|
25
|
+
initializeSystemMessage() {
|
|
26
|
+
const toolDescriptions = this.toolRegistry.getToolDescriptions();
|
|
27
|
+
const systemPrompt = `You are an expert AI coding assistant with access to powerful tools for file operations, shell commands, and git management.
|
|
28
|
+
|
|
29
|
+
Available Tools:
|
|
30
|
+
${toolDescriptions}
|
|
31
|
+
|
|
32
|
+
Guidelines:
|
|
33
|
+
- Use tools proactively to read, edit, and create files when the user asks for code changes
|
|
34
|
+
- For multi-step tasks, use the manage_todos tool to track progress
|
|
35
|
+
- Always use read_file before editing files to understand the current content
|
|
36
|
+
- Use glob and grep to explore unfamiliar codebases
|
|
37
|
+
- Provide clear explanations of what you're doing
|
|
38
|
+
- Ask for clarification if requirements are ambiguous
|
|
39
|
+
|
|
40
|
+
${this.contextMessage}
|
|
41
|
+
|
|
42
|
+
When you need to perform file operations, use the appropriate tools rather than just suggesting changes.`;
|
|
43
|
+
this.messages.push(new messages_1.SystemMessage(systemPrompt));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Process user input and generate response with tool support
|
|
47
|
+
*/
|
|
48
|
+
async processUserInput(input) {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
// Log prompt received
|
|
51
|
+
tracker_1.tracker.promptReceived(input);
|
|
52
|
+
try {
|
|
53
|
+
// Auto-attach file context if files are mentioned
|
|
54
|
+
const attachedFiles = await this.attachMentionedFiles(input);
|
|
55
|
+
let finalInput = input;
|
|
56
|
+
if (attachedFiles.length > 0) {
|
|
57
|
+
ui_1.log.info(`Attached context for: ${attachedFiles.join(', ')}`);
|
|
58
|
+
tracker_1.tracker.contextAttached(attachedFiles);
|
|
59
|
+
}
|
|
60
|
+
// Add user message
|
|
61
|
+
this.messages.push(new messages_1.HumanMessage(finalInput));
|
|
62
|
+
// Get tools as LangChain tools
|
|
63
|
+
const tools = this.toolRegistry.getAllAsLangChainTools();
|
|
64
|
+
// Main response loop (handles tool calls)
|
|
65
|
+
let continueLoop = true;
|
|
66
|
+
let iterationCount = 0;
|
|
67
|
+
const maxIterations = 10; // Prevent infinite loops
|
|
68
|
+
while (continueLoop && iterationCount < maxIterations) {
|
|
69
|
+
iterationCount++;
|
|
70
|
+
tracker_1.tracker.iteration(iterationCount, 'Starting API call');
|
|
71
|
+
// Log API request
|
|
72
|
+
tracker_1.tracker.apiRequest(this.modelProvider.modelId, this.messages.length, tools.length);
|
|
73
|
+
const apiStartTime = Date.now();
|
|
74
|
+
// Stream response from model
|
|
75
|
+
const stream = this.modelProvider.streamChat(this.messages, tools);
|
|
76
|
+
let fullResponse = '';
|
|
77
|
+
let toolCalls = [];
|
|
78
|
+
const formatter = new ui_1.StreamFormatter();
|
|
79
|
+
process.stdout.write('\n');
|
|
80
|
+
try {
|
|
81
|
+
for await (const chunk of stream) {
|
|
82
|
+
// Handle text content
|
|
83
|
+
if (chunk.content && typeof chunk.content === 'string') {
|
|
84
|
+
fullResponse += chunk.content;
|
|
85
|
+
// Format and output the chunk with syntax highlighting
|
|
86
|
+
const formatted = formatter.processChunk(chunk.content);
|
|
87
|
+
if (formatted) {
|
|
88
|
+
process.stdout.write(formatted);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Handle tool calls
|
|
92
|
+
if (chunk.tool_calls && chunk.tool_calls.length > 0) {
|
|
93
|
+
toolCalls = chunk.tool_calls;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Flush any remaining content in the formatter
|
|
97
|
+
const remaining = formatter.flush();
|
|
98
|
+
if (remaining) {
|
|
99
|
+
process.stdout.write(remaining);
|
|
100
|
+
}
|
|
101
|
+
// Log API response
|
|
102
|
+
const apiDuration = Date.now() - apiStartTime;
|
|
103
|
+
tracker_1.tracker.apiResponse(fullResponse.length > 0, toolCalls.length > 0, toolCalls.length, apiDuration);
|
|
104
|
+
}
|
|
105
|
+
catch (apiError) {
|
|
106
|
+
tracker_1.tracker.apiError(apiError.message, { stack: apiError.stack });
|
|
107
|
+
throw apiError;
|
|
108
|
+
}
|
|
109
|
+
process.stdout.write('\n\n');
|
|
110
|
+
// Log AI response if there's content
|
|
111
|
+
if (fullResponse.length > 0) {
|
|
112
|
+
tracker_1.tracker.aiResponse(fullResponse);
|
|
113
|
+
}
|
|
114
|
+
// If we have tool calls, execute them
|
|
115
|
+
if (toolCalls.length > 0) {
|
|
116
|
+
// Log AI's tool decision
|
|
117
|
+
tracker_1.tracker.aiToolDecision(toolCalls.map(tc => ({
|
|
118
|
+
name: tc.name,
|
|
119
|
+
args: tc.args || {}
|
|
120
|
+
})));
|
|
121
|
+
// Add AI message with tool calls
|
|
122
|
+
this.messages.push(new messages_1.AIMessage({
|
|
123
|
+
content: fullResponse,
|
|
124
|
+
tool_calls: toolCalls
|
|
125
|
+
}));
|
|
126
|
+
// Execute tools
|
|
127
|
+
const results = await this.executeToolCalls(toolCalls);
|
|
128
|
+
// Add tool results as messages
|
|
129
|
+
for (let i = 0; i < toolCalls.length; i++) {
|
|
130
|
+
const toolCall = toolCalls[i];
|
|
131
|
+
const result = results[i];
|
|
132
|
+
this.messages.push(new messages_1.ToolMessage({
|
|
133
|
+
content: result.success ? result.output : `Error: ${result.error}`,
|
|
134
|
+
tool_call_id: toolCall.id,
|
|
135
|
+
name: toolCall.name
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
tracker_1.tracker.iteration(iterationCount, 'Tool execution complete, continuing loop');
|
|
139
|
+
// Continue loop to get AI's response after tool execution
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// No tool calls, add final AI message and end loop
|
|
144
|
+
this.messages.push(new messages_1.AIMessage(fullResponse));
|
|
145
|
+
continueLoop = false;
|
|
146
|
+
tracker_1.tracker.iteration(iterationCount, 'No tool calls, ending loop');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (iterationCount >= maxIterations) {
|
|
150
|
+
ui_1.log.warning('Maximum iteration limit reached. Ending conversation turn.');
|
|
151
|
+
tracker_1.tracker.warn('Max iterations reached', { iterations: iterationCount });
|
|
152
|
+
}
|
|
153
|
+
// Log success
|
|
154
|
+
const duration = Date.now() - startTime;
|
|
155
|
+
tracker_1.tracker.promptComplete(input, duration, iterationCount);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
ui_1.log.error(`Error: ${error.message}`);
|
|
159
|
+
tracker_1.tracker.promptFailed(input, error.message);
|
|
160
|
+
tracker_1.tracker.error('Chat processing failed', error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Execute tool calls from AI
|
|
165
|
+
*/
|
|
166
|
+
async executeToolCalls(toolCalls) {
|
|
167
|
+
const formattedCalls = toolCalls.map(tc => ({
|
|
168
|
+
id: tc.id || `call-${Date.now()}`,
|
|
169
|
+
name: tc.name,
|
|
170
|
+
args: tc.args || {}
|
|
171
|
+
}));
|
|
172
|
+
return await this.toolExecutor.executeMultiple(formattedCalls);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Auto-attach file context for mentioned files
|
|
176
|
+
*/
|
|
177
|
+
async attachMentionedFiles(input) {
|
|
178
|
+
const allFiles = await (0, context_1.getFileTree)();
|
|
179
|
+
const attached = [];
|
|
180
|
+
for (const file of allFiles) {
|
|
181
|
+
const base = file.split('/').pop() || '';
|
|
182
|
+
const isBaseMatch = base.length > 2 && input.includes(base);
|
|
183
|
+
const isPathMatch = input.includes(file);
|
|
184
|
+
if (isPathMatch || isBaseMatch) {
|
|
185
|
+
const content = (0, context_1.getFileContent)(file);
|
|
186
|
+
if (content) {
|
|
187
|
+
// Add file content to the messages as context
|
|
188
|
+
attached.push(file);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return attached;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Clear chat history but keep system message
|
|
196
|
+
*/
|
|
197
|
+
clearHistory() {
|
|
198
|
+
const systemMsg = this.messages[0];
|
|
199
|
+
this.messages = [systemMsg];
|
|
200
|
+
tracker_1.tracker.debug('Chat history cleared');
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get message count
|
|
204
|
+
*/
|
|
205
|
+
getMessageCount() {
|
|
206
|
+
return this.messages.length;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Update context (e.g., if file tree changes)
|
|
210
|
+
*/
|
|
211
|
+
updateContext(newContext) {
|
|
212
|
+
this.contextMessage = newContext;
|
|
213
|
+
// Re-initialize system message with new context
|
|
214
|
+
const systemMsg = this.messages[0];
|
|
215
|
+
this.messages[0] = systemMsg; // Keep the same message for now
|
|
216
|
+
// In a more sophisticated implementation, we'd rebuild the system message
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.ChatHandler = ChatHandler;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.clearSessionData = exports.setSessionData = exports.getSessionData = exports.setPreference = exports.getPreferences = exports.setActiveModel = exports.getModelConfig = exports.clearApiKey = exports.setApiKey = exports.getApiKey = exports.getConfig = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const registry_1 = require("./models/registry");
|
|
11
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.config', 'groq-cli-tool');
|
|
12
|
+
const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
13
|
+
const ensureConfigDir = () => {
|
|
14
|
+
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
15
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const readConfig = () => {
|
|
19
|
+
try {
|
|
20
|
+
if (!fs_1.default.existsSync(CONFIG_FILE))
|
|
21
|
+
return {};
|
|
22
|
+
return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const writeConfig = (data) => {
|
|
29
|
+
ensureConfigDir();
|
|
30
|
+
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Get full config with defaults
|
|
34
|
+
*/
|
|
35
|
+
const getConfig = () => {
|
|
36
|
+
const config = readConfig();
|
|
37
|
+
// Handle legacy config format (old apiKey -> new apiKeys.groq)
|
|
38
|
+
if ('apiKey' in config && typeof config.apiKey === 'string') {
|
|
39
|
+
const legacyKey = config.apiKey;
|
|
40
|
+
delete config.apiKey;
|
|
41
|
+
config.apiKeys = { groq: legacyKey };
|
|
42
|
+
writeConfig(config);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
apiKeys: config.apiKeys || { groq: '' },
|
|
46
|
+
activeModel: config.activeModel || (0, registry_1.getDefaultModel)(),
|
|
47
|
+
preferences: {
|
|
48
|
+
temperature: config.preferences?.temperature ?? 0.7,
|
|
49
|
+
autoConfirm: config.preferences?.autoConfirm ?? false,
|
|
50
|
+
safeMode: config.preferences?.safeMode ?? true,
|
|
51
|
+
maxTokens: config.preferences?.maxTokens
|
|
52
|
+
},
|
|
53
|
+
session: config.session || { todos: [] }
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
exports.getConfig = getConfig;
|
|
57
|
+
/**
|
|
58
|
+
* Get API key (backward compatible)
|
|
59
|
+
*/
|
|
60
|
+
const getApiKey = () => {
|
|
61
|
+
const config = (0, exports.getConfig)();
|
|
62
|
+
return config.apiKeys.groq || '';
|
|
63
|
+
};
|
|
64
|
+
exports.getApiKey = getApiKey;
|
|
65
|
+
/**
|
|
66
|
+
* Set API key (backward compatible)
|
|
67
|
+
*/
|
|
68
|
+
const setApiKey = (key) => {
|
|
69
|
+
const config = readConfig();
|
|
70
|
+
if (!config.apiKeys) {
|
|
71
|
+
config.apiKeys = { groq: '' };
|
|
72
|
+
}
|
|
73
|
+
config.apiKeys.groq = key;
|
|
74
|
+
writeConfig(config);
|
|
75
|
+
};
|
|
76
|
+
exports.setApiKey = setApiKey;
|
|
77
|
+
/**
|
|
78
|
+
* Clear API key (backward compatible)
|
|
79
|
+
*/
|
|
80
|
+
const clearApiKey = () => {
|
|
81
|
+
const config = readConfig();
|
|
82
|
+
if (config.apiKeys) {
|
|
83
|
+
config.apiKeys.groq = '';
|
|
84
|
+
}
|
|
85
|
+
writeConfig(config);
|
|
86
|
+
};
|
|
87
|
+
exports.clearApiKey = clearApiKey;
|
|
88
|
+
/**
|
|
89
|
+
* Get active model configuration
|
|
90
|
+
*/
|
|
91
|
+
const getModelConfig = () => {
|
|
92
|
+
const config = (0, exports.getConfig)();
|
|
93
|
+
return {
|
|
94
|
+
modelId: config.activeModel,
|
|
95
|
+
temperature: config.preferences.temperature
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
exports.getModelConfig = getModelConfig;
|
|
99
|
+
/**
|
|
100
|
+
* Set active model
|
|
101
|
+
*/
|
|
102
|
+
const setActiveModel = (modelId) => {
|
|
103
|
+
const config = readConfig();
|
|
104
|
+
config.activeModel = modelId;
|
|
105
|
+
writeConfig(config);
|
|
106
|
+
};
|
|
107
|
+
exports.setActiveModel = setActiveModel;
|
|
108
|
+
/**
|
|
109
|
+
* Get user preferences
|
|
110
|
+
*/
|
|
111
|
+
const getPreferences = () => {
|
|
112
|
+
return (0, exports.getConfig)().preferences;
|
|
113
|
+
};
|
|
114
|
+
exports.getPreferences = getPreferences;
|
|
115
|
+
/**
|
|
116
|
+
* Set a preference
|
|
117
|
+
*/
|
|
118
|
+
const setPreference = (key, value) => {
|
|
119
|
+
const config = readConfig();
|
|
120
|
+
if (!config.preferences) {
|
|
121
|
+
config.preferences = {
|
|
122
|
+
temperature: 0.7,
|
|
123
|
+
autoConfirm: false,
|
|
124
|
+
safeMode: true
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
config.preferences[key] = value;
|
|
128
|
+
writeConfig(config);
|
|
129
|
+
};
|
|
130
|
+
exports.setPreference = setPreference;
|
|
131
|
+
/**
|
|
132
|
+
* Get session data (todos, etc.)
|
|
133
|
+
*/
|
|
134
|
+
const getSessionData = () => {
|
|
135
|
+
const config = (0, exports.getConfig)();
|
|
136
|
+
return config.session || { todos: [] };
|
|
137
|
+
};
|
|
138
|
+
exports.getSessionData = getSessionData;
|
|
139
|
+
/**
|
|
140
|
+
* Set session data
|
|
141
|
+
*/
|
|
142
|
+
const setSessionData = (data) => {
|
|
143
|
+
const config = readConfig();
|
|
144
|
+
config.session = data;
|
|
145
|
+
writeConfig(config);
|
|
146
|
+
};
|
|
147
|
+
exports.setSessionData = setSessionData;
|
|
148
|
+
/**
|
|
149
|
+
* Clear session data
|
|
150
|
+
*/
|
|
151
|
+
const clearSessionData = () => {
|
|
152
|
+
const config = readConfig();
|
|
153
|
+
config.session = { todos: [] };
|
|
154
|
+
writeConfig(config);
|
|
155
|
+
};
|
|
156
|
+
exports.clearSessionData = clearSessionData;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeFile = exports.getFileContent = exports.getFileTree = exports.isGitRepo = void 0;
|
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
|
+
const glob_1 = require("glob");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const git = (0, simple_git_1.default)();
|
|
11
|
+
const isGitRepo = async () => {
|
|
12
|
+
try {
|
|
13
|
+
return await git.checkIsRepo();
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
exports.isGitRepo = isGitRepo;
|
|
20
|
+
const getFileTree = async (cwd = process.cwd()) => {
|
|
21
|
+
// Ignore node_modules, .git, dist, etc.
|
|
22
|
+
const ignore = ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/.env', '**/*.lock'];
|
|
23
|
+
const files = await (0, glob_1.glob)('**/*', { cwd, ignore, nodir: true });
|
|
24
|
+
return files;
|
|
25
|
+
};
|
|
26
|
+
exports.getFileTree = getFileTree;
|
|
27
|
+
const getFileContent = (filePath) => {
|
|
28
|
+
try {
|
|
29
|
+
return fs_1.default.readFileSync(filePath, 'utf-8');
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
exports.getFileContent = getFileContent;
|
|
36
|
+
const writeFile = (filePath, content) => {
|
|
37
|
+
try {
|
|
38
|
+
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.writeFile = writeFile;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchGroqModels = fetchGroqModels;
|
|
7
|
+
exports.groupModelsByOwner = groupModelsByOwner;
|
|
8
|
+
exports.formatContextWindow = formatContextWindow;
|
|
9
|
+
const groq_sdk_1 = __importDefault(require("groq-sdk"));
|
|
10
|
+
/**
|
|
11
|
+
* Fetch available models from Groq API
|
|
12
|
+
*/
|
|
13
|
+
async function fetchGroqModels(apiKey) {
|
|
14
|
+
try {
|
|
15
|
+
const groq = new groq_sdk_1.default({ apiKey });
|
|
16
|
+
const response = await groq.models.list();
|
|
17
|
+
// Filter and sort models
|
|
18
|
+
const models = response.data
|
|
19
|
+
.filter(m => m.active !== false)
|
|
20
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
21
|
+
return models;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error('Failed to fetch models:', error.message);
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Group models by provider/owner
|
|
30
|
+
*/
|
|
31
|
+
function groupModelsByOwner(models) {
|
|
32
|
+
const groups = new Map();
|
|
33
|
+
for (const model of models) {
|
|
34
|
+
const owner = model.owned_by || 'Other';
|
|
35
|
+
if (!groups.has(owner)) {
|
|
36
|
+
groups.set(owner, []);
|
|
37
|
+
}
|
|
38
|
+
groups.get(owner).push(model);
|
|
39
|
+
}
|
|
40
|
+
return groups;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Format context window size
|
|
44
|
+
*/
|
|
45
|
+
function formatContextWindow(tokens) {
|
|
46
|
+
if (tokens >= 1000000) {
|
|
47
|
+
return `${(tokens / 1000000).toFixed(0)}M`;
|
|
48
|
+
}
|
|
49
|
+
if (tokens >= 1000) {
|
|
50
|
+
return `${(tokens / 1000).toFixed(0)}K`;
|
|
51
|
+
}
|
|
52
|
+
return `${tokens}`;
|
|
53
|
+
}
|
package/dist/lib/groq.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.streamChat = exports.initGroq = void 0;
|
|
4
|
+
const groq_1 = require("@langchain/groq");
|
|
5
|
+
const messages_1 = require("@langchain/core/messages");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
let model = null;
|
|
8
|
+
const initGroq = () => {
|
|
9
|
+
const apiKey = (0, config_1.getApiKey)();
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
throw new Error('API Key not found');
|
|
12
|
+
}
|
|
13
|
+
model = new groq_1.ChatGroq({
|
|
14
|
+
apiKey,
|
|
15
|
+
model: 'llama-3.3-70b-versatile',
|
|
16
|
+
temperature: 0.7
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
exports.initGroq = initGroq;
|
|
20
|
+
const streamChat = async (messages) => {
|
|
21
|
+
if (!model)
|
|
22
|
+
(0, exports.initGroq)();
|
|
23
|
+
const langChainMessages = messages.map(m => {
|
|
24
|
+
if (m.role === 'system')
|
|
25
|
+
return new messages_1.SystemMessage(m.content);
|
|
26
|
+
if (m.role === 'user')
|
|
27
|
+
return new messages_1.HumanMessage(m.content);
|
|
28
|
+
return new messages_1.AIMessage(m.content);
|
|
29
|
+
});
|
|
30
|
+
const stream = await model.stream(langChainMessages);
|
|
31
|
+
return stream;
|
|
32
|
+
};
|
|
33
|
+
exports.streamChat = streamChat;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GroqProvider = void 0;
|
|
4
|
+
const groq_1 = require("@langchain/groq");
|
|
5
|
+
const provider_1 = require("./provider");
|
|
6
|
+
/**
|
|
7
|
+
* Groq model provider implementation
|
|
8
|
+
* Supports multiple Groq models with function calling
|
|
9
|
+
*/
|
|
10
|
+
class GroqProvider extends provider_1.ModelProvider {
|
|
11
|
+
constructor(modelId = 'llama-3.3-70b-versatile', temperature = 0.7) {
|
|
12
|
+
super();
|
|
13
|
+
this.name = 'groq';
|
|
14
|
+
this.supportsFunctionCalling = true;
|
|
15
|
+
this.model = null;
|
|
16
|
+
this.apiKey = '';
|
|
17
|
+
this.temperature = 0.7;
|
|
18
|
+
this.modelId = modelId;
|
|
19
|
+
this.temperature = temperature;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the Groq model
|
|
23
|
+
*/
|
|
24
|
+
initialize(apiKey, modelId) {
|
|
25
|
+
this.apiKey = apiKey;
|
|
26
|
+
if (modelId) {
|
|
27
|
+
this.modelId = modelId;
|
|
28
|
+
}
|
|
29
|
+
this.model = new groq_1.ChatGroq({
|
|
30
|
+
apiKey: this.apiKey,
|
|
31
|
+
model: this.modelId,
|
|
32
|
+
temperature: this.temperature
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Stream chat with optional tool support
|
|
37
|
+
*/
|
|
38
|
+
async *streamChat(messages, tools) {
|
|
39
|
+
if (!this.model) {
|
|
40
|
+
throw new Error('Model not initialized. Call initialize() first.');
|
|
41
|
+
}
|
|
42
|
+
// Bind tools if provided
|
|
43
|
+
const modelToUse = tools && tools.length > 0
|
|
44
|
+
? this.model.bindTools(tools)
|
|
45
|
+
: this.model;
|
|
46
|
+
// Stream the response
|
|
47
|
+
const stream = await modelToUse.stream(messages);
|
|
48
|
+
for await (const chunk of stream) {
|
|
49
|
+
yield chunk;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get model information
|
|
54
|
+
*/
|
|
55
|
+
getInfo() {
|
|
56
|
+
return {
|
|
57
|
+
id: this.modelId,
|
|
58
|
+
provider: 'groq',
|
|
59
|
+
capabilities: ['chat', 'streaming', 'function_calling'],
|
|
60
|
+
description: `Groq model: ${this.modelId}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Update model configuration
|
|
65
|
+
*/
|
|
66
|
+
setModel(modelId) {
|
|
67
|
+
this.modelId = modelId;
|
|
68
|
+
if (this.apiKey) {
|
|
69
|
+
this.initialize(this.apiKey, modelId);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Update temperature
|
|
74
|
+
*/
|
|
75
|
+
setTemperature(temperature) {
|
|
76
|
+
this.temperature = temperature;
|
|
77
|
+
if (this.apiKey) {
|
|
78
|
+
this.initialize(this.apiKey, this.modelId);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.GroqProvider = GroqProvider;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModelProvider = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for model providers
|
|
6
|
+
* Implementations handle specific AI service integrations
|
|
7
|
+
*/
|
|
8
|
+
class ModelProvider {
|
|
9
|
+
}
|
|
10
|
+
exports.ModelProvider = ModelProvider;
|