UNPKG

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
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