UNPKG

@push.rocks/smartai

Version:

SmartAi is a versatile TypeScript library designed to facilitate integration and interaction with various AI models, offering functionalities for chat, audio generation, document processing, and vision tasks.

129 lines (109 loc) 4.23 kB
import * as plugins from './plugins.js'; import * as paths from './paths.js'; import { MultiModalModel } from './abstract.classes.multimodal.js'; import type { ChatOptions, ChatResponse, ChatMessage } from './abstract.classes.multimodal.js'; import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions'; export interface IExoProviderOptions { exoBaseUrl?: string; apiKey?: string; } export class ExoProvider extends MultiModalModel { private options: IExoProviderOptions; public openAiApiClient: plugins.openai.default; constructor(optionsArg: IExoProviderOptions = {}) { super(); this.options = { exoBaseUrl: 'http://localhost:8080/v1', // Default Exo API endpoint ...optionsArg }; } public async start() { this.openAiApiClient = new plugins.openai.default({ apiKey: this.options.apiKey || 'not-needed', // Exo might not require an API key for local deployment baseURL: this.options.exoBaseUrl, }); } public async stop() {} public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> { // Create a TextDecoder to handle incoming chunks const decoder = new TextDecoder(); let buffer = ''; let currentMessage: { role: string; content: string; } | null = null; // Create a TransformStream to process the input const transform = new TransformStream<Uint8Array, string>({ transform: async (chunk, controller) => { buffer += decoder.decode(chunk, { stream: true }); // Try to parse complete JSON messages from the buffer while (true) { const newlineIndex = buffer.indexOf('\n'); if (newlineIndex === -1) break; const line = buffer.slice(0, newlineIndex); buffer = buffer.slice(newlineIndex + 1); if (line.trim()) { try { const message = JSON.parse(line); currentMessage = message; // Process the message based on its type if (message.type === 'message') { const response = await this.chat({ systemMessage: '', userMessage: message.content, messageHistory: [{ role: message.role as 'user' | 'assistant' | 'system', content: message.content }] }); controller.enqueue(JSON.stringify(response) + '\n'); } } catch (error) { console.error('Error processing message:', error); } } } }, flush(controller) { if (buffer) { try { const message = JSON.parse(buffer); currentMessage = message; } catch (error) { console.error('Error processing remaining buffer:', error); } } } }); return input.pipeThrough(transform); } public async chat(options: ChatOptions): Promise<ChatResponse> { const messages: ChatCompletionMessageParam[] = [ { role: 'system', content: options.systemMessage }, ...options.messageHistory, { role: 'user', content: options.userMessage } ]; try { const response = await this.openAiApiClient.chat.completions.create({ model: 'local-model', // Exo uses local models messages: messages, stream: false }); return { role: 'assistant', message: response.choices[0]?.message?.content || '' }; } catch (error) { console.error('Error in chat completion:', error); throw error; } } public async audio(optionsArg: { message: string }): Promise<NodeJS.ReadableStream> { throw new Error('Audio generation is not supported by Exo provider'); } public async vision(optionsArg: { image: Buffer; prompt: string }): Promise<string> { throw new Error('Vision processing is not supported by Exo provider'); } public async document(optionsArg: { systemMessage: string; userMessage: string; pdfDocuments: Uint8Array[]; messageHistory: ChatMessage[]; }): Promise<{ message: any }> { throw new Error('Document processing is not supported by Exo provider'); } }