UNPKG

helpingai

Version:

The official JavaScript/TypeScript library for the HelpingAI API - Advanced AI with Emotional Intelligence

263 lines 8.97 kB
/** * Main HelpingAI client implementation */ import { parseErrorResponse, TimeoutError, NetworkError, ToolExecutionError } from './errors'; import { MCPManager } from './mcp/manager'; import { executeTool, getRegistry } from './tools/core'; import { executeBuiltinTool, isBuiltinTool } from './tools/builtin'; export class HelpingAI { constructor(config = {}) { /** * Chat completions API */ this.chat = { completions: { create: async (request) => { return this.createChatCompletion(request); }, }, }; /** * Models API */ this.models = { list: async () => { return this.listModels(); }, retrieve: async (modelId) => { return this.retrieveModel(modelId); }, }; this.config = { apiKey: config.apiKey || '', baseURL: config.baseURL || 'https://api.helpingai.co/v1', timeout: config.timeout || 30000, organization: config.organization || '', defaultHeaders: config.defaultHeaders || {}, }; if (!this.config.apiKey) { console.warn('HelpingAI API key not provided. Set HAI_API_KEY environment variable or pass apiKey in config.'); } } /** * Direct tool execution method */ // eslint-disable-next-line @typescript-eslint/no-explicit-any async call(toolName, args) { try { // Check if it's an MCP tool if (this.mcpManager?.isMCPTool(toolName)) { return await this.mcpManager.executeTool(toolName, args); } // Check if it's a registered tool if (getRegistry().has(toolName)) { return await executeTool(toolName, args); } // Handle built-in tools if (isBuiltinTool(toolName)) { return await executeBuiltinTool(toolName, args); } throw new ToolExecutionError(`Tool '${toolName}' not found`, toolName); } catch (error) { throw new ToolExecutionError(`Failed to execute tool '${toolName}': ${error instanceof Error ? error.message : String(error)}`, toolName); } } /** * Create chat completion */ async createChatCompletion(request) { // Process tools if provided const processedTools = await this.processTools(request.tools); const requestBody = { ...request, ...(processedTools && { tools: processedTools }), }; if (request.stream) { return this.createStreamingCompletion(requestBody); } else { return this.createNonStreamingCompletion(requestBody); } } /** * Process tools configuration */ // eslint-disable-next-line @typescript-eslint/no-explicit-any async processTools(tools) { if (!tools || tools.length === 0) { return undefined; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const processedTools = []; for (const tool of tools) { if (typeof tool === 'string') { // Built-in tool processedTools.push(tool); } else if ('mcpServers' in tool) { // MCP configuration try { const { manager, tools: mcpTools } = await MCPManager.processConfiguration(tool); this.mcpManager = manager; processedTools.push(...mcpTools); } catch (error) { console.warn('Failed to process MCP configuration:', error); } } else { // Regular OpenAI-format tool processedTools.push(tool); } } return processedTools; } /** * Create non-streaming completion */ async createNonStreamingCompletion(request) { const response = await this.makeRequest('/chat/completions', { method: 'POST', body: JSON.stringify(request), }); return response; } /** * Create streaming completion */ async createStreamingCompletion(_request) { const url = `${this.config.baseURL}/chat/completions`; const headers = this.getHeaders(); return new Promise((resolve, reject) => { const eventSource = new EventSource(url, { headers: { ...headers, 'Content-Type': 'application/json', }, }); const chunks = []; let resolved = false; // eslint-disable-next-line @typescript-eslint/no-explicit-any eventSource.onmessage = (event) => { if (event.data === '[DONE]') { eventSource.close(); if (!resolved) { resolved = true; resolve(this.createAsyncIterable(chunks)); } return; } try { const chunk = JSON.parse(event.data); chunks.push(chunk); } catch (error) { console.warn('Failed to parse streaming chunk:', error); } }; // eslint-disable-next-line @typescript-eslint/no-explicit-any eventSource.onerror = (_error) => { eventSource.close(); if (!resolved) { resolved = true; reject(new NetworkError('Streaming connection failed')); } }; // Send the request body (this is a simplified implementation) // In a real implementation, you'd need to handle POST data with EventSource setTimeout(() => { if (!resolved) { resolved = true; resolve(this.createAsyncIterable(chunks)); } }, 100); }); } /** * Create async iterable from chunks */ async *createAsyncIterable(chunks) { for (const chunk of chunks) { yield chunk; } } /** * List available models */ async listModels() { const response = await this.makeRequest('/models', { method: 'GET' }); return response; } /** * Retrieve specific model */ async retrieveModel(modelId) { const response = await this.makeRequest(`/models/${modelId}`, { method: 'GET' }); return response; } /** * Make HTTP request */ // eslint-disable-next-line @typescript-eslint/no-explicit-any async makeRequest(endpoint, options) { const url = `${this.config.baseURL}${endpoint}`; const headers = this.getHeaders(); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.timeout); try { const response = await fetch(url, { ...options, headers: { ...headers, ...options.headers, }, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw parseErrorResponse(response.status, errorData, Object.fromEntries(response.headers.entries())); } return await response.json(); } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.name === 'AbortError') { throw new TimeoutError('Request timeout'); } if (error instanceof TypeError && error.message.includes('fetch')) { throw new NetworkError('Network error'); } throw error; } } /** * Get request headers */ getHeaders() { const headers = { 'Content-Type': 'application/json', 'User-Agent': 'HelpingAI-JS/1.0.0', ...this.config.defaultHeaders, }; if (this.config.apiKey) { headers['Authorization'] = `Bearer ${this.config.apiKey}`; } if (this.config.organization) { headers['HelpingAI-Organization'] = this.config.organization; } return headers; } /** * Cleanup resources */ async cleanup() { if (this.mcpManager) { await this.mcpManager.cleanup(); } } } // Export as HAI for compatibility export { HelpingAI as HAI }; //# sourceMappingURL=client.js.map