whatsapp-claude-gpt
Version:
WhatsApp-Claude-GPT is a WhatsApp chatbot that supports multiple AI providers for chat, optional image generation/editing, and voice (speech-to-text and text-to-speech). It’s built for natural, contextual conversations and can now also handle reminders an
124 lines (95 loc) • 3.86 kB
text/typescript
import logger from '../logger';
import Anthropic from '@anthropic-ai/sdk';
import { AIConfig, CONFIG } from '../config';
import { MessageParam, TextBlock } from '@anthropic-ai/sdk/resources';
import Roboto from "../bot/roboto";
import NodeCache from "node-cache";
import { AIRole, AIService, ToolExecutionContext } from "../interfaces/ai-interfaces";
import { countMessages, sanitizeForLog, trimCachePreserveMessageStart } from "../utils";
import { ChatConfiguration } from "../config/chat-configurations";
class AnthropicService implements AIService<MessageParam, any> {
private anthropic : Anthropic;
private messagesCache = new NodeCache();
constructor() {
this.anthropic = new Anthropic({
apiKey: AIConfig.ChatConfig.apiKey,
});
}
public deleteChatCache(chatId: string){
this.messagesCache.del(chatId);
}
public addMessageToCache(item: MessageParam, chatId: string){
const aiMessages: any[] = this.messagesCache.get(chatId) || [];
aiMessages.push(item);
this.messagesCache.set(chatId, aiMessages, CONFIG.BotConfig.nodeCacheTime);
}
public hasChatCache(chatId: string): boolean {
return this.messagesCache.has(chatId);
}
public async sendMessage(aiMessagesInputList: MessageParam[], systemPrompt: string, chatConfig: ChatConfiguration, tools: any, toolContext?: ToolExecutionContext): Promise<string> {
let cycleCount = 0;
const maxCycles = 5;
const chatId = chatConfig.chatId;
const aiMessages: any[] = this.messagesCache.get(chatId) || [];
aiMessages.push(...aiMessagesInputList)
while (cycleCount < maxCycles) {
const aiResponse: Anthropic.Messages.Message = await this.sendToApi(aiMessages, systemPrompt, tools);
let hasFunctionCall = false;
aiMessages.push({
role: AIRole.ASSISTANT,
content: aiResponse.content
})
const resultContent = [];
for (const c of aiResponse.content) {
if (c.type == 'tool_use') {
hasFunctionCall = true;
const functionResult = await Roboto.handleFunction(c.name, c.input, toolContext);
resultContent.push({
type: "tool_result",
tool_use_id: c.id,
content: JSON.stringify(functionResult)
})
}
}
if(resultContent.length>0)
aiMessages.push({
role: AIRole.USER,
content: resultContent
});
cycleCount += 1;
if (!hasFunctionCall) {
const finalMsgList = trimCachePreserveMessageStart(aiMessages, chatConfig.maxMsgsLimit ?? 30);
this.messagesCache.set(chatId, finalMsgList, CONFIG.BotConfig.nodeCacheTime);
const content = aiResponse.content[0];
if (!content || content.type !== 'text') {
logger.warn(`[Anthropic->sendMessage] Unexpected response block type: ${content?.type ?? 'undefined'} for chat ${chatId}`);
return "";
}
return (content as TextBlock)?.text || "";
}
}
throw new Error(`Reached the limit of ${maxCycles} communication cycles with Claude.`);
}
async sendToApi(
messageList: MessageParam[],
systemPrompt: string,
tools: any
) {
logger.debug(`[Claude->sendCompletion] Sending ${countMessages(messageList)} messages.`);
const response = await this.anthropic.messages.create({
system: systemPrompt,
model: AIConfig.ChatConfig.model,
messages: messageList,
max_tokens: CONFIG.BotConfig.claudeMaxTokens,
top_p: 1,
tools
});
logger.debug(`[Claude->sendCompletion] Completion Response: ${sanitizeForLog(response.content[0])?.type ?? 'unknown'} block`);
return response;
// const responseContent = response.content[0] as TextBlock;
//
// return responseContent;
}
}
const AnthropicSvc = new AnthropicService();
export default AnthropicSvc