171 lines
6.8 KiB
JavaScript
171 lines
6.8 KiB
JavaScript
// Direct API agent that uses Anthropic SDK without Claude Code dependency
|
|
import Anthropic from '@anthropic-ai/sdk';
|
|
import { logger } from "../utils/logger.js";
|
|
import { withRetry } from "../utils/retry.js";
|
|
function getCurrentProvider() {
|
|
// Determine provider from environment
|
|
if (process.env.PROVIDER === 'gemini' || process.env.USE_GEMINI === 'true') {
|
|
return 'gemini';
|
|
}
|
|
if (process.env.PROVIDER === 'requesty' || process.env.USE_REQUESTY === 'true') {
|
|
return 'requesty';
|
|
}
|
|
if (process.env.PROVIDER === 'openrouter' || process.env.USE_OPENROUTER === 'true') {
|
|
return 'openrouter';
|
|
}
|
|
if (process.env.PROVIDER === 'onnx' || process.env.USE_ONNX === 'true') {
|
|
return 'onnx';
|
|
}
|
|
return 'anthropic'; // Default
|
|
}
|
|
function getModelForProvider(provider) {
|
|
// Use DEFAULT_MODEL or COMPLETION_MODEL from environment (both supported for backward compatibility)
|
|
const envModel = process.env.DEFAULT_MODEL || process.env.COMPLETION_MODEL;
|
|
switch (provider) {
|
|
case 'gemini':
|
|
return {
|
|
model: envModel || 'gemini-2.0-flash-exp',
|
|
apiKey: process.env.GOOGLE_GEMINI_API_KEY || '',
|
|
baseURL: process.env.GEMINI_PROXY_URL || 'http://localhost:3000'
|
|
};
|
|
case 'requesty':
|
|
return {
|
|
model: envModel || 'deepseek/deepseek-chat',
|
|
apiKey: process.env.REQUESTY_API_KEY || '',
|
|
baseURL: process.env.REQUESTY_PROXY_URL || 'http://localhost:3000'
|
|
};
|
|
case 'openrouter':
|
|
return {
|
|
model: envModel || 'deepseek/deepseek-chat',
|
|
apiKey: process.env.OPENROUTER_API_KEY || '',
|
|
baseURL: process.env.OPENROUTER_PROXY_URL || 'http://localhost:3000'
|
|
};
|
|
case 'onnx':
|
|
return {
|
|
model: 'onnx-local',
|
|
apiKey: 'local',
|
|
baseURL: process.env.ONNX_PROXY_URL || 'http://localhost:3001'
|
|
};
|
|
case 'anthropic':
|
|
default:
|
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
if (!apiKey) {
|
|
throw new Error('ANTHROPIC_API_KEY is required for Anthropic provider');
|
|
}
|
|
return {
|
|
model: envModel || 'claude-sonnet-4-5-20250929',
|
|
apiKey,
|
|
// Direct Anthropic API - no baseURL needed
|
|
};
|
|
}
|
|
}
|
|
export async function claudeAgentDirect(agent, input, onStream, modelOverride) {
|
|
const startTime = Date.now();
|
|
const provider = getCurrentProvider();
|
|
logger.info('Starting Direct Anthropic SDK (no Claude Code dependency)', {
|
|
agent: agent.name,
|
|
provider,
|
|
input: input.substring(0, 100),
|
|
model: modelOverride || 'default'
|
|
});
|
|
return withRetry(async () => {
|
|
const modelConfig = getModelForProvider(provider);
|
|
const finalModel = modelOverride || modelConfig.model;
|
|
// Create Anthropic client with provider-specific configuration
|
|
const anthropic = new Anthropic({
|
|
apiKey: modelConfig.apiKey,
|
|
baseURL: modelConfig.baseURL, // undefined for direct Anthropic, proxy URL for others
|
|
timeout: 120000,
|
|
maxRetries: 3
|
|
});
|
|
logger.info('Direct API configuration', {
|
|
provider,
|
|
model: finalModel,
|
|
hasApiKey: !!modelConfig.apiKey,
|
|
hasBaseURL: !!modelConfig.baseURL
|
|
});
|
|
try {
|
|
// Build messages array
|
|
const messages = [
|
|
{ role: 'user', content: input }
|
|
];
|
|
// Call Anthropic API directly (no Claude Code subprocess)
|
|
const stream = await anthropic.messages.create({
|
|
model: finalModel,
|
|
max_tokens: 4096,
|
|
system: agent.systemPrompt,
|
|
messages,
|
|
stream: true
|
|
});
|
|
let output = '';
|
|
let toolCallCount = 0;
|
|
// Process streaming response
|
|
for await (const event of stream) {
|
|
if (event.type === 'content_block_start') {
|
|
if (event.content_block.type === 'text') {
|
|
// Text content start
|
|
continue;
|
|
}
|
|
else if (event.content_block.type === 'tool_use') {
|
|
// Tool use detected
|
|
toolCallCount++;
|
|
const toolName = event.content_block.name || 'unknown';
|
|
const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
|
|
const progressMsg = `\n[${timestamp}] 🔍 Tool call #${toolCallCount}: ${toolName}\n`;
|
|
process.stderr.write(progressMsg);
|
|
if (onStream) {
|
|
onStream(progressMsg);
|
|
}
|
|
}
|
|
}
|
|
else if (event.type === 'content_block_delta') {
|
|
if (event.delta.type === 'text_delta') {
|
|
const chunk = event.delta.text;
|
|
output += chunk;
|
|
if (onStream && chunk) {
|
|
onStream(chunk);
|
|
}
|
|
}
|
|
}
|
|
else if (event.type === 'content_block_stop') {
|
|
if (toolCallCount > 0) {
|
|
const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
|
|
const resultMsg = `[${timestamp}] ✅ Tool completed\n`;
|
|
process.stderr.write(resultMsg);
|
|
if (onStream) {
|
|
onStream(resultMsg);
|
|
}
|
|
}
|
|
}
|
|
else if (event.type === 'message_stop') {
|
|
// Stream complete
|
|
break;
|
|
}
|
|
// Flush output for immediate visibility
|
|
if (process.stderr.uncork) {
|
|
process.stderr.uncork();
|
|
}
|
|
if (process.stdout.uncork) {
|
|
process.stdout.uncork();
|
|
}
|
|
}
|
|
const duration = Date.now() - startTime;
|
|
logger.info('Direct SDK completed', {
|
|
agent: agent.name,
|
|
provider,
|
|
duration,
|
|
outputLength: output.length
|
|
});
|
|
return { output, agent: agent.name };
|
|
}
|
|
catch (error) {
|
|
logger.error('Direct SDK execution failed', {
|
|
provider,
|
|
model: finalModel,
|
|
error: error.message
|
|
});
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
//# sourceMappingURL=claudeAgentDirect.js.map
|