protoagent 0.0.3 ā 0.0.4
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/dist/agentic-loop.js +179 -121
- package/dist/config/client.js +11 -1
- package/dist/config/commands.js +15 -1
- package/dist/config/providers.js +59 -0
- package/dist/config/setup.js +35 -2
- package/dist/config/system-prompt.js +55 -21
- package/dist/tools/edit-file.js +43 -6
- package/dist/tools/index.js +14 -13
- package/dist/tools/run-shell-command.js +431 -26
- package/dist/tools/task-complete.js +26 -0
- package/dist/tools/write-file.js +15 -7
- package/dist/utils/enhanced-prompt.js +23 -0
- package/dist/utils/file-operations-approval.js +247 -25
- package/dist/utils/interrupt-handler.js +127 -0
- package/dist/utils/logger.js +1 -1
- package/dist/utils/user-cancellation.js +34 -0
- package/package.json +2 -2
package/dist/agentic-loop.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getModelConfig } from './config/providers.js';
|
|
|
5
5
|
import { estimateTokens, createUsageInfo, logUsageInfo, getContextInfo } from './utils/cost-tracker.js';
|
|
6
6
|
import { checkAndCompactIfNeeded } from './utils/conversation-compactor.js';
|
|
7
7
|
import { logger } from './utils/logger.js';
|
|
8
|
+
import { interruptHandler, UserInterruptError } from './utils/interrupt-handler.js';
|
|
8
9
|
// Create system message for ProtoAgent dynamically
|
|
9
10
|
async function createSystemMessage() {
|
|
10
11
|
const systemPrompt = await generateSystemPrompt();
|
|
@@ -70,7 +71,13 @@ export class AgenticLoop {
|
|
|
70
71
|
role: 'user',
|
|
71
72
|
content: userInput
|
|
72
73
|
});
|
|
73
|
-
|
|
74
|
+
// Show a subtle hint about Q key on first use (only if this is the first interaction)
|
|
75
|
+
if (this.messages.length === 2) { // System message + first user message
|
|
76
|
+
logger.consoleLog('š¤ Thinking... (press Q to pause)');
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.consoleLog('š¤ Thinking...');
|
|
80
|
+
}
|
|
74
81
|
logger.info('š¤ AI thinking process started');
|
|
75
82
|
logger.debug('š§ Starting thinking process', {
|
|
76
83
|
component: 'AgenticLoop',
|
|
@@ -143,84 +150,103 @@ export class AgenticLoop {
|
|
|
143
150
|
let hasToolCalls = false;
|
|
144
151
|
let actualUsage = undefined;
|
|
145
152
|
let chunkCount = 0;
|
|
146
|
-
for
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
finishReason: chunk.choices[0]?.finish_reason
|
|
155
|
-
});
|
|
156
|
-
if (chunk.usage) {
|
|
157
|
-
actualUsage = chunk.usage;
|
|
158
|
-
logger.trace('š Received usage data', {
|
|
159
|
-
component: 'AgenticLoop',
|
|
160
|
-
promptTokens: chunk.usage.prompt_tokens,
|
|
161
|
-
completionTokens: chunk.usage.completion_tokens,
|
|
162
|
-
totalTokens: chunk.usage.total_tokens
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
if (delta?.content) {
|
|
166
|
-
streamedContent += delta.content;
|
|
167
|
-
assistantMessage.content = streamedContent;
|
|
168
|
-
// Stream content to user in real-time if no tool calls are being made
|
|
169
|
-
if (this.options.streamOutput && !hasToolCalls && !delta.tool_calls) {
|
|
170
|
-
if (streamedContent === delta.content) {
|
|
171
|
-
// First content chunk
|
|
172
|
-
process.stdout.write('\nš¤ ProtoAgent: ');
|
|
173
|
-
}
|
|
174
|
-
process.stdout.write(delta.content);
|
|
153
|
+
// Start listening for 'Q' key interrupts during streaming
|
|
154
|
+
interruptHandler.startListening();
|
|
155
|
+
try {
|
|
156
|
+
for await (const chunk of stream) {
|
|
157
|
+
// Check for user interrupt
|
|
158
|
+
if (interruptHandler.isInterrupted()) {
|
|
159
|
+
logger.debug('User requested pause during streaming', { component: 'AgenticLoop' });
|
|
160
|
+
break;
|
|
175
161
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
logger.trace('š§ Tool calls detected in delta', {
|
|
162
|
+
chunkCount++;
|
|
163
|
+
const delta = chunk.choices[0]?.delta;
|
|
164
|
+
logger.trace('š¦ Processing chunk', {
|
|
180
165
|
component: 'AgenticLoop',
|
|
181
|
-
|
|
166
|
+
chunkNumber: chunkCount,
|
|
167
|
+
hasContent: !!delta?.content,
|
|
168
|
+
hasToolCalls: !!delta?.tool_calls,
|
|
169
|
+
finishReason: chunk.choices[0]?.finish_reason
|
|
182
170
|
});
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
// Handle tool calls in streaming
|
|
188
|
-
for (const toolCallDelta of delta.tool_calls) {
|
|
189
|
-
const index = toolCallDelta.index || 0;
|
|
190
|
-
logger.trace('š ļø Processing tool call delta', {
|
|
171
|
+
if (chunk.usage) {
|
|
172
|
+
actualUsage = chunk.usage;
|
|
173
|
+
logger.trace('š Received usage data', {
|
|
191
174
|
component: 'AgenticLoop',
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
hasArgs: !!toolCallDelta.function?.arguments
|
|
175
|
+
promptTokens: chunk.usage.prompt_tokens,
|
|
176
|
+
completionTokens: chunk.usage.completion_tokens,
|
|
177
|
+
totalTokens: chunk.usage.total_tokens
|
|
196
178
|
});
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
179
|
+
}
|
|
180
|
+
if (delta?.content) {
|
|
181
|
+
streamedContent += delta.content;
|
|
182
|
+
assistantMessage.content = streamedContent;
|
|
183
|
+
// Stream content to user in real-time if no tool calls are being made
|
|
184
|
+
if (this.options.streamOutput && !hasToolCalls && !delta.tool_calls) {
|
|
185
|
+
if (streamedContent === delta.content) {
|
|
186
|
+
// First content chunk
|
|
187
|
+
process.stdout.write('\nš¤ ProtoAgent: ');
|
|
188
|
+
}
|
|
189
|
+
process.stdout.write(delta.content);
|
|
207
190
|
}
|
|
208
|
-
|
|
209
|
-
|
|
191
|
+
}
|
|
192
|
+
if (delta?.tool_calls) {
|
|
193
|
+
hasToolCalls = true;
|
|
194
|
+
logger.trace('š§ Tool calls detected in delta', {
|
|
195
|
+
component: 'AgenticLoop',
|
|
196
|
+
toolCallsCount: delta.tool_calls.length
|
|
197
|
+
});
|
|
198
|
+
// Initialize tool_calls array if not exists
|
|
199
|
+
if (!assistantMessage.tool_calls) {
|
|
200
|
+
assistantMessage.tool_calls = [];
|
|
210
201
|
}
|
|
211
|
-
|
|
212
|
-
|
|
202
|
+
// Handle tool calls in streaming
|
|
203
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
204
|
+
const index = toolCallDelta.index || 0;
|
|
205
|
+
logger.trace('š ļø Processing tool call delta', {
|
|
206
|
+
component: 'AgenticLoop',
|
|
207
|
+
index,
|
|
208
|
+
hasId: !!toolCallDelta.id,
|
|
209
|
+
hasName: !!toolCallDelta.function?.name,
|
|
210
|
+
hasArgs: !!toolCallDelta.function?.arguments
|
|
211
|
+
});
|
|
212
|
+
// Ensure we have an entry at this index
|
|
213
|
+
if (!assistantMessage.tool_calls[index]) {
|
|
214
|
+
assistantMessage.tool_calls[index] = {
|
|
215
|
+
id: '',
|
|
216
|
+
type: 'function',
|
|
217
|
+
function: { name: '', arguments: '' }
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (toolCallDelta.id) {
|
|
221
|
+
assistantMessage.tool_calls[index].id = toolCallDelta.id;
|
|
222
|
+
}
|
|
223
|
+
if (toolCallDelta.function?.name) {
|
|
224
|
+
assistantMessage.tool_calls[index].function.name += toolCallDelta.function.name;
|
|
225
|
+
}
|
|
226
|
+
if (toolCallDelta.function?.arguments) {
|
|
227
|
+
assistantMessage.tool_calls[index].function.arguments += toolCallDelta.function.arguments;
|
|
228
|
+
}
|
|
213
229
|
}
|
|
214
230
|
}
|
|
215
231
|
}
|
|
232
|
+
logger.debug('ā
Stream processing complete', {
|
|
233
|
+
component: 'AgenticLoop',
|
|
234
|
+
totalChunks: chunkCount,
|
|
235
|
+
finalContentLength: streamedContent.length,
|
|
236
|
+
finalToolCallsCount: assistantMessage.tool_calls?.length || 0,
|
|
237
|
+
hasActualUsage: !!actualUsage
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
// Always stop listening for interrupts when streaming ends
|
|
242
|
+
interruptHandler.stopListening();
|
|
243
|
+
}
|
|
244
|
+
// Check if streaming was interrupted
|
|
245
|
+
if (interruptHandler.isInterrupted()) {
|
|
246
|
+
logger.debug('User paused during streaming', { component: 'AgenticLoop' });
|
|
247
|
+
interruptHandler.reset();
|
|
248
|
+
throw new UserInterruptError('User paused during streaming');
|
|
216
249
|
}
|
|
217
|
-
logger.debug('ā
Stream processing complete', {
|
|
218
|
-
component: 'AgenticLoop',
|
|
219
|
-
totalChunks: chunkCount,
|
|
220
|
-
finalContentLength: streamedContent.length,
|
|
221
|
-
finalToolCallsCount: assistantMessage.tool_calls?.length || 0,
|
|
222
|
-
hasActualUsage: !!actualUsage
|
|
223
|
-
});
|
|
224
250
|
const message = assistantMessage;
|
|
225
251
|
// DEBUG: Log the complete AI response
|
|
226
252
|
logger.debug('š¤ AI Response received', {
|
|
@@ -299,65 +325,79 @@ export class AgenticLoop {
|
|
|
299
325
|
logger.consoleLog(`š§ Using ${message.tool_calls.length} tool(s)...`);
|
|
300
326
|
logger.info(`š§ Tool usage message displayed: ${message.tool_calls.length} tools`);
|
|
301
327
|
// Execute each tool call
|
|
302
|
-
for
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
logger.debug('š Tool arguments parsed', {
|
|
315
|
-
component: 'AgenticLoop',
|
|
316
|
-
toolName: name,
|
|
317
|
-
parsedArgs: Object.keys(toolArgs)
|
|
318
|
-
});
|
|
319
|
-
const startTime = Date.now();
|
|
320
|
-
const result = await handleToolCall(name, toolArgs);
|
|
321
|
-
const executionTime = Date.now() - startTime;
|
|
322
|
-
logger.debug('ā
Tool execution successful', {
|
|
328
|
+
// Start listening for interrupts during tool execution
|
|
329
|
+
interruptHandler.startListening();
|
|
330
|
+
try {
|
|
331
|
+
for (const toolCall of message.tool_calls) {
|
|
332
|
+
// Check for user interrupt before each tool
|
|
333
|
+
if (interruptHandler.isInterrupted()) {
|
|
334
|
+
logger.debug('User requested pause during tool execution', { component: 'AgenticLoop' });
|
|
335
|
+
interruptHandler.reset();
|
|
336
|
+
throw new UserInterruptError('User paused during tool execution');
|
|
337
|
+
}
|
|
338
|
+
const { name, arguments: args } = toolCall.function;
|
|
339
|
+
logger.debug('š ļø Executing tool', {
|
|
323
340
|
component: 'AgenticLoop',
|
|
324
341
|
toolName: name,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
});
|
|
328
|
-
// Add tool result to conversation
|
|
329
|
-
this.addMessage({
|
|
330
|
-
role: 'tool',
|
|
331
|
-
tool_call_id: toolCall.id,
|
|
332
|
-
content: result
|
|
342
|
+
toolId: toolCall.id,
|
|
343
|
+
argsLength: args.length
|
|
333
344
|
});
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
logger.
|
|
345
|
+
logger.consoleLog(`š ļø ${name}`);
|
|
346
|
+
logger.info(`š ļø Tool execution message displayed: ${name}`);
|
|
347
|
+
try {
|
|
348
|
+
const toolArgs = JSON.parse(args);
|
|
349
|
+
logger.debug('š Tool arguments parsed', {
|
|
350
|
+
component: 'AgenticLoop',
|
|
351
|
+
toolName: name,
|
|
352
|
+
parsedArgs: Object.keys(toolArgs)
|
|
353
|
+
});
|
|
354
|
+
const startTime = Date.now();
|
|
355
|
+
const result = await handleToolCall(name, toolArgs);
|
|
356
|
+
const executionTime = Date.now() - startTime;
|
|
357
|
+
logger.debug('ā
Tool execution successful', {
|
|
358
|
+
component: 'AgenticLoop',
|
|
359
|
+
toolName: name,
|
|
360
|
+
executionTime,
|
|
361
|
+
resultLength: result.length
|
|
362
|
+
});
|
|
363
|
+
// Add tool result to conversation
|
|
364
|
+
this.addMessage({
|
|
365
|
+
role: 'tool',
|
|
366
|
+
tool_call_id: toolCall.id,
|
|
367
|
+
content: result
|
|
368
|
+
});
|
|
369
|
+
// Show abbreviated result to user
|
|
370
|
+
const lines = result.split('\n');
|
|
371
|
+
if (lines.length > 10) {
|
|
372
|
+
logger.consoleLog(` ā
${lines.slice(0, 3).join('\n ')}\n ... (${lines.length - 6} more lines) ...\n ${lines.slice(-3).join('\n ')}`);
|
|
373
|
+
logger.info(` ā
Tool result displayed (abbreviated, ${lines.length} lines)`);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
logger.consoleLog(` ā
${result.slice(0, 200)}${result.length > 200 ? '...' : ''}`);
|
|
377
|
+
logger.info(` ā
Tool result displayed (full, ${result.length} chars)`);
|
|
378
|
+
}
|
|
339
379
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
logger.
|
|
380
|
+
catch (error) {
|
|
381
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
382
|
+
logger.error('ā Tool execution failed', {
|
|
383
|
+
component: 'AgenticLoop',
|
|
384
|
+
toolName: name,
|
|
385
|
+
error: errorMessage
|
|
386
|
+
});
|
|
387
|
+
logger.consoleLog(` ā Error: ${errorMessage}`);
|
|
388
|
+
logger.error(` ā Tool error message displayed: ${errorMessage}`);
|
|
389
|
+
// Add error result to conversation
|
|
390
|
+
this.addMessage({
|
|
391
|
+
role: 'tool',
|
|
392
|
+
tool_call_id: toolCall.id,
|
|
393
|
+
content: `Error: ${errorMessage}`
|
|
394
|
+
});
|
|
343
395
|
}
|
|
344
396
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
toolName: name,
|
|
350
|
-
error: errorMessage
|
|
351
|
-
});
|
|
352
|
-
logger.consoleLog(` ā Error: ${errorMessage}`);
|
|
353
|
-
logger.error(` ā Tool error message displayed: ${errorMessage}`);
|
|
354
|
-
// Add error result to conversation
|
|
355
|
-
this.addMessage({
|
|
356
|
-
role: 'tool',
|
|
357
|
-
tool_call_id: toolCall.id,
|
|
358
|
-
content: `Error: ${errorMessage}`
|
|
359
|
-
});
|
|
360
|
-
}
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
// Always stop listening for interrupts when tool execution ends
|
|
400
|
+
interruptHandler.stopListening();
|
|
361
401
|
}
|
|
362
402
|
// Continue the loop to let AI process tool results
|
|
363
403
|
logger.trace('š Continuing agentic loop for AI to process tool results', {
|
|
@@ -426,6 +466,15 @@ export class AgenticLoop {
|
|
|
426
466
|
}
|
|
427
467
|
}
|
|
428
468
|
catch (apiError) {
|
|
469
|
+
// Check if this is a user interrupt first
|
|
470
|
+
if (apiError instanceof UserInterruptError) {
|
|
471
|
+
logger.warn('š User pressed Q - interrupt during processing', {
|
|
472
|
+
component: 'AgenticLoop',
|
|
473
|
+
reason: apiError.message
|
|
474
|
+
});
|
|
475
|
+
console.log('\n'); // Just a clean newline
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
429
478
|
// Handle API errors that weren't caught by the retry logic
|
|
430
479
|
logger.trace('ā API Error occurred - stopping agentic loop', {
|
|
431
480
|
component: 'AgenticLoop',
|
|
@@ -474,6 +523,15 @@ export class AgenticLoop {
|
|
|
474
523
|
}
|
|
475
524
|
}
|
|
476
525
|
catch (error) {
|
|
526
|
+
// Check if this is a user interrupt
|
|
527
|
+
if (error instanceof UserInterruptError) {
|
|
528
|
+
logger.debug('š User interrupt detected - returning control to user', {
|
|
529
|
+
component: 'AgenticLoop',
|
|
530
|
+
reason: error.message
|
|
531
|
+
});
|
|
532
|
+
logger.consoleLog('\n');
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
477
535
|
// Handle general processing errors
|
|
478
536
|
console.error('\nā Error during processing:', error?.message || 'Unknown error');
|
|
479
537
|
logger.error('\nā General processing error displayed to user', {
|
package/dist/config/client.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpenAI client manager for ProtoAgent
|
|
3
3
|
*/
|
|
4
4
|
import OpenAI from 'openai';
|
|
5
|
-
import { geminiProvider, cerebrasProvider, getModelConfig } from './providers.js';
|
|
5
|
+
import { geminiProvider, cerebrasProvider, anthropicProvider, getModelConfig } from './providers.js';
|
|
6
6
|
import { estimateTokens, getContextInfo } from '../utils/cost-tracker.js';
|
|
7
7
|
import { logger } from '../utils/logger.js';
|
|
8
8
|
/**
|
|
@@ -40,6 +40,16 @@ export function createOpenAIClient(config) {
|
|
|
40
40
|
baseURL: cerebrasProvider.baseURL
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
+
if (config.provider === 'anthropic') {
|
|
44
|
+
logger.debug('š Using Anthropic provider', {
|
|
45
|
+
component: 'OpenAIClient',
|
|
46
|
+
baseURL: anthropicProvider.baseURL
|
|
47
|
+
});
|
|
48
|
+
return new OpenAI({
|
|
49
|
+
apiKey: config.credentials.ANTHROPIC_API_KEY,
|
|
50
|
+
baseURL: anthropicProvider.baseURL
|
|
51
|
+
});
|
|
52
|
+
}
|
|
43
53
|
logger.error('ā Unknown provider', {
|
|
44
54
|
component: 'OpenAIClient',
|
|
45
55
|
provider: config.provider
|
package/dist/config/commands.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
5
|
import { hasConfig, loadConfig, saveConfig, getConfigDirectory } from './manager.js';
|
|
6
6
|
import { setupConfig } from './setup.js';
|
|
7
|
-
import { openaiProvider, geminiProvider, cerebrasProvider } from './providers.js';
|
|
7
|
+
import { openaiProvider, geminiProvider, cerebrasProvider, anthropicProvider } from './providers.js';
|
|
8
8
|
import fs from 'fs/promises';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { logger } from '../utils/logger.js';
|
|
@@ -46,6 +46,9 @@ export async function showCurrentConfig() {
|
|
|
46
46
|
else if (config.provider === 'cerebras' && config.credentials.CEREBRAS_API_KEY) {
|
|
47
47
|
logger.consoleLog(`API Key: ${maskApiKey(config.credentials.CEREBRAS_API_KEY)}`);
|
|
48
48
|
}
|
|
49
|
+
else if (config.provider === 'anthropic' && config.credentials.ANTHROPIC_API_KEY) {
|
|
50
|
+
logger.consoleLog(`API Key: ${maskApiKey(config.credentials.ANTHROPIC_API_KEY)}`);
|
|
51
|
+
}
|
|
49
52
|
logger.consoleLog(`Config Location: ${configDir}/config.json`);
|
|
50
53
|
logger.consoleLog('ā'.repeat(40));
|
|
51
54
|
}
|
|
@@ -84,6 +87,11 @@ export async function updateApiKey() {
|
|
|
84
87
|
helpUrl = 'https://cloud.cerebras.ai/platform';
|
|
85
88
|
keyPrefix = '';
|
|
86
89
|
}
|
|
90
|
+
else if (config.provider === 'anthropic') {
|
|
91
|
+
currentKey = config.credentials.ANTHROPIC_API_KEY || '';
|
|
92
|
+
helpUrl = 'https://console.anthropic.com/settings/keys';
|
|
93
|
+
keyPrefix = 'sk-';
|
|
94
|
+
}
|
|
87
95
|
logger.consoleLog(`Current API Key: ${maskApiKey(currentKey)}`);
|
|
88
96
|
logger.consoleLog(`Get your API key from: ${helpUrl}\n`);
|
|
89
97
|
const { newApiKey } = await inquirer.prompt([
|
|
@@ -113,6 +121,9 @@ export async function updateApiKey() {
|
|
|
113
121
|
else if (config.provider === 'cerebras') {
|
|
114
122
|
config.credentials.CEREBRAS_API_KEY = newApiKey.trim();
|
|
115
123
|
}
|
|
124
|
+
else if (config.provider === 'anthropic') {
|
|
125
|
+
config.credentials.ANTHROPIC_API_KEY = newApiKey.trim();
|
|
126
|
+
}
|
|
116
127
|
await saveConfig(config);
|
|
117
128
|
logger.consoleLog('\nā
API key updated successfully!');
|
|
118
129
|
logger.consoleLog(`New API Key: ${maskApiKey(newApiKey)}`);
|
|
@@ -145,6 +156,9 @@ export async function updateModel() {
|
|
|
145
156
|
else if (config.provider === 'cerebras') {
|
|
146
157
|
availableModels = cerebrasProvider.models;
|
|
147
158
|
}
|
|
159
|
+
else if (config.provider === 'anthropic') {
|
|
160
|
+
availableModels = anthropicProvider.models;
|
|
161
|
+
}
|
|
148
162
|
const { newModel } = await inquirer.prompt([
|
|
149
163
|
{
|
|
150
164
|
type: 'list',
|
package/dist/config/providers.js
CHANGED
|
@@ -153,6 +153,63 @@ export const cerebrasProvider = {
|
|
|
153
153
|
},
|
|
154
154
|
baseURL: 'https://api.cerebras.ai/v1'
|
|
155
155
|
};
|
|
156
|
+
export const anthropicModels = {
|
|
157
|
+
models: [
|
|
158
|
+
'claude-3-5-sonnet-4-20250126',
|
|
159
|
+
'claude-3-5-sonnet-20241022',
|
|
160
|
+
'claude-3-5-haiku-20241022',
|
|
161
|
+
'claude-3-opus-20240229',
|
|
162
|
+
'claude-3-sonnet-20240229',
|
|
163
|
+
'claude-3-haiku-20240307'
|
|
164
|
+
],
|
|
165
|
+
defaultModel: 'claude-3-5-sonnet-4-20250126'
|
|
166
|
+
};
|
|
167
|
+
export const anthropicModelConfigs = {
|
|
168
|
+
'claude-3-5-sonnet-4-20250126': {
|
|
169
|
+
name: 'Claude 3.5 Sonnet 4',
|
|
170
|
+
contextWindow: 200000,
|
|
171
|
+
pricing: { inputTokens: 0.000003, outputTokens: 0.000015 } // $3.00/$15.00 per 1M tokens
|
|
172
|
+
},
|
|
173
|
+
'claude-3-5-sonnet-20241022': {
|
|
174
|
+
name: 'Claude 3.5 Sonnet',
|
|
175
|
+
contextWindow: 200000,
|
|
176
|
+
pricing: { inputTokens: 0.000003, outputTokens: 0.000015 } // $3.00/$15.00 per 1M tokens
|
|
177
|
+
},
|
|
178
|
+
'claude-3-5-haiku-20241022': {
|
|
179
|
+
name: 'Claude 3.5 Haiku',
|
|
180
|
+
contextWindow: 200000,
|
|
181
|
+
pricing: { inputTokens: 0.0000008, outputTokens: 0.000004 } // $0.80/$4.00 per 1M tokens
|
|
182
|
+
},
|
|
183
|
+
'claude-3-opus-20240229': {
|
|
184
|
+
name: 'Claude 3 Opus',
|
|
185
|
+
contextWindow: 200000,
|
|
186
|
+
pricing: { inputTokens: 0.000015, outputTokens: 0.000075 } // $15.00/$75.00 per 1M tokens
|
|
187
|
+
},
|
|
188
|
+
'claude-3-sonnet-20240229': {
|
|
189
|
+
name: 'Claude 3 Sonnet',
|
|
190
|
+
contextWindow: 200000,
|
|
191
|
+
pricing: { inputTokens: 0.000003, outputTokens: 0.000015 } // $3.00/$15.00 per 1M tokens
|
|
192
|
+
},
|
|
193
|
+
'claude-3-haiku-20240307': {
|
|
194
|
+
name: 'Claude 3 Haiku',
|
|
195
|
+
contextWindow: 200000,
|
|
196
|
+
pricing: { inputTokens: 0.00000025, outputTokens: 0.00000125 } // $0.25/$1.25 per 1M tokens
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
export const anthropicProvider = {
|
|
200
|
+
id: 'anthropic',
|
|
201
|
+
name: 'Anthropic',
|
|
202
|
+
models: anthropicModels.models,
|
|
203
|
+
defaultModel: anthropicModels.defaultModel,
|
|
204
|
+
modelConfigs: anthropicModelConfigs,
|
|
205
|
+
auth: {
|
|
206
|
+
type: 'api_key',
|
|
207
|
+
envVar: 'ANTHROPIC_API_KEY',
|
|
208
|
+
label: 'Anthropic API Key',
|
|
209
|
+
helpUrl: 'https://console.anthropic.com/settings/keys'
|
|
210
|
+
},
|
|
211
|
+
baseURL: 'https://api.anthropic.com/v1'
|
|
212
|
+
};
|
|
156
213
|
/**
|
|
157
214
|
* Get model configuration for any provider
|
|
158
215
|
*/
|
|
@@ -164,6 +221,8 @@ export function getModelConfig(provider, model) {
|
|
|
164
221
|
return geminiModelConfigs[model] || null;
|
|
165
222
|
case 'cerebras':
|
|
166
223
|
return cerebrasModelConfigs[model] || null;
|
|
224
|
+
case 'anthropic':
|
|
225
|
+
return anthropicModelConfigs[model] || null;
|
|
167
226
|
default:
|
|
168
227
|
return null;
|
|
169
228
|
}
|
package/dist/config/setup.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Interactive configuration setup for ProtoAgent
|
|
3
3
|
*/
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
|
-
import { openaiProvider, geminiProvider, cerebrasProvider } from './providers.js';
|
|
5
|
+
import { openaiProvider, geminiProvider, cerebrasProvider, anthropicProvider } from './providers.js';
|
|
6
6
|
import { saveConfig, getConfigDirectory } from './manager.js';
|
|
7
7
|
import { logger } from '../utils/logger.js';
|
|
8
8
|
/**
|
|
@@ -18,6 +18,11 @@ export async function setupConfig() {
|
|
|
18
18
|
name: model === openaiProvider.defaultModel ? `${model} (recommended)` : model,
|
|
19
19
|
value: { provider: 'openai', model }
|
|
20
20
|
})),
|
|
21
|
+
new inquirer.Separator('--- Anthropic Models ---'),
|
|
22
|
+
...anthropicProvider.models.map(model => ({
|
|
23
|
+
name: model === anthropicProvider.defaultModel ? `${model} (recommended)` : model,
|
|
24
|
+
value: { provider: 'anthropic', model }
|
|
25
|
+
})),
|
|
21
26
|
new inquirer.Separator('--- Gemini Models ---'),
|
|
22
27
|
...geminiProvider.models.map(model => ({
|
|
23
28
|
name: model === geminiProvider.defaultModel ? `${model} (recommended)` : model,
|
|
@@ -45,7 +50,8 @@ export async function setupConfig() {
|
|
|
45
50
|
let credentials = {
|
|
46
51
|
OPENAI_API_KEY: '',
|
|
47
52
|
GEMINI_API_KEY: '',
|
|
48
|
-
CEREBRAS_API_KEY: ''
|
|
53
|
+
CEREBRAS_API_KEY: '',
|
|
54
|
+
ANTHROPIC_API_KEY: ''
|
|
49
55
|
};
|
|
50
56
|
if (provider === 'openai') {
|
|
51
57
|
logger.consoleLog(`\nš ${openaiProvider.auth.label} Setup`);
|
|
@@ -71,6 +77,30 @@ export async function setupConfig() {
|
|
|
71
77
|
apiKey = response.apiKey;
|
|
72
78
|
credentials.OPENAI_API_KEY = apiKey;
|
|
73
79
|
}
|
|
80
|
+
else if (provider === 'anthropic') {
|
|
81
|
+
logger.consoleLog(`\nš ${anthropicProvider.auth.label} Setup`);
|
|
82
|
+
logger.consoleLog("You'll need an API key from Anthropic.");
|
|
83
|
+
logger.consoleLog(`Get your API key from: ${anthropicProvider.auth.helpUrl}`);
|
|
84
|
+
const response = await inquirer.prompt([
|
|
85
|
+
{
|
|
86
|
+
type: 'password',
|
|
87
|
+
name: 'apiKey',
|
|
88
|
+
message: `Enter your ${anthropicProvider.auth.label}:`,
|
|
89
|
+
mask: '*',
|
|
90
|
+
validate: (input) => {
|
|
91
|
+
if (!input || input.trim().length === 0) {
|
|
92
|
+
return 'API key is required';
|
|
93
|
+
}
|
|
94
|
+
if (!input.trim().startsWith('sk-')) {
|
|
95
|
+
return 'Anthropic API keys should start with "sk-"';
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
]);
|
|
101
|
+
apiKey = response.apiKey;
|
|
102
|
+
credentials.ANTHROPIC_API_KEY = apiKey;
|
|
103
|
+
}
|
|
74
104
|
else if (provider === 'gemini') {
|
|
75
105
|
logger.consoleLog(`\nš ${geminiProvider.auth.label} Setup`);
|
|
76
106
|
logger.consoleLog("You'll need an API key from Gemini.");
|
|
@@ -121,6 +151,9 @@ export async function setupConfig() {
|
|
|
121
151
|
if (provider === 'openai') {
|
|
122
152
|
providerName = openaiProvider.name;
|
|
123
153
|
}
|
|
154
|
+
else if (provider === 'anthropic') {
|
|
155
|
+
providerName = anthropicProvider.name;
|
|
156
|
+
}
|
|
124
157
|
else if (provider === 'gemini') {
|
|
125
158
|
providerName = geminiProvider.name;
|
|
126
159
|
}
|