UNPKG

@paxsenix/ai

Version:

A lightweight and intuitive Node.js client for the Paxsenix AI API.

244 lines (215 loc) 7.08 kB
class Chat { constructor(client) { this.client = client; } /** * Create a chat completion * @param {Object} params - Parameters for the chat completion * @param {Array} params.messages - Array of message objects * @param {string} [params.model="gpt-3.5-turbo"] - Model to use * @param {number} [params.temperature=0.7] - Sampling temperature * @param {number} [params.max_tokens] - Maximum number of tokens to generate * @param {boolean} [params.stream=false] - Stream back partial progress * @param {Object} [options] - Request options * @returns {Promise<Object>} - The chat completion response */ async createCompletion(params, options = {}) { try { const requestParams = { ...params, model: params.model || 'gpt-3.5-turbo' }; const response = await this.client.post('/v1/chat/completions', requestParams, options); return response.data; } catch (error) { throw error; } } /** * Stream a chat completion response * @param {Object} params - Parameters for the chat completion * @param {function} onData - Callback for each chunk of data * @param {function} [onError] - Callback for errors * @param {function} [onEnd] - Callback when stream ends * @param {Object} [options] - Request options * @returns {Promise<void>} */ async streamCompletion(params, onData, onError, onEnd, options = {}) { if (typeof onError !== 'function') { options = onEnd || {}; onEnd = onError; onError = null; } if (typeof onEnd !== 'function') { if (typeof onEnd === 'object') { options = onEnd; } onEnd = null; } try { const requestParams = { ...params, model: params.model || 'gpt-3.5-turbo', stream: true }; const response = await this.client.post('/v1/chat/completions', requestParams, { ...options, stream: true }); return new Promise((resolve, reject) => { const stream = response.data; let buffer = ''; stream.setEncoding('utf8'); stream.on('data', (chunk) => { buffer += chunk; const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine === '' || !trimmedLine.startsWith('data: ')) { continue; } const data = trimmedLine.slice(6).trim(); if (data === '[DONE]') { if (onEnd) onEnd(); resolve(); return; } try { const parsedData = JSON.parse(data); onData(parsedData); } catch (e) { const parseError = new Error(`Error parsing stream data: ${e.message}`); parseError.rawData = data; if (onError) { onError(parseError); } else { console.error('Stream parsing error:', parseError); } } } }); stream.on('end', () => { // Process any remaining data in buffer if (buffer.trim()) { const lines = buffer.split('\n'); for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine && trimmedLine.startsWith('data: ')) { const data = trimmedLine.slice(6).trim(); if (data !== '[DONE]' && data !== '') { try { const parsedData = JSON.parse(data); onData(parsedData); } catch (e) { const parseError = new Error(`Error parsing final stream data: ${e.message}`); parseError.rawData = data; if (onError) { onError(parseError); } else { console.error('Final stream parsing error:', parseError); } } } } } } if (onEnd) onEnd(); resolve(); }); stream.on('error', (error) => { if (onError) { onError(error); } else { reject(error); } }); }); } catch (error) { if (onError) { onError(error); } else { throw error; } } } /** * Create a completion with custom configuration * @param {Object} params - Parameters for the chat completion * @param {Object} config - Custom configuration options * @returns {Promise<Object>} - The chat completion response */ async createCompletionWithConfig(params, config = {}) { const mergedParams = { model: 'gpt-3.5-turbo', temperature: 0.7, max_tokens: null, top_p: 1, frequency_penalty: 0, presence_penalty: 0, ...params }; return this.createCompletion(mergedParams, config); } /** * Simple streaming helper with promise-based API * @param {Object} params - Parameters for the chat completion * @param {Object} [options] - Request options * @returns {AsyncGenerator} - AsyncGenerator that yields chunks */ async* streamCompletionAsync(params, options = {}) { const requestParams = { ...params, model: params.model || 'gpt-3.5-turbo', stream: true }; const response = await this.client.post('/v1/chat/completions', requestParams, { ...options, stream: true }); const stream = response.data; let buffer = ''; stream.setEncoding('utf8'); yield* (async function* () { for await (const chunk of stream) { buffer += chunk; const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine === '' || !trimmedLine.startsWith('data: ')) { continue; } const data = trimmedLine.slice(6).trim(); if (data === '[DONE]') { return; } try { const parsedData = JSON.parse(data); yield parsedData; } catch (e) { console.error('Error parsing stream data:', e, 'Raw data:', data); } } } if (buffer.trim()) { const lines = buffer.split('\n'); for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine && trimmedLine.startsWith('data: ')) { const data = trimmedLine.slice(6).trim(); if (data !== '[DONE]' && data !== '') { try { const parsedData = JSON.parse(data); yield parsedData; } catch (e) { console.error('Error parsing final stream data:', e); } } } } } })(); } } module.exports = Chat;