Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
574f7a594c | ||
|
0b2a058550 | ||
|
88d15c89e5 |
21
changelog.md
21
changelog.md
@@ -1,5 +1,26 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-08-01 - 0.5.9 - fix(documentation)
|
||||||
|
Remove contribution section from readme
|
||||||
|
|
||||||
|
- Removed the contribution section from readme.md as requested
|
||||||
|
- Kept the roadmap section for future development plans
|
||||||
|
|
||||||
|
## 2025-08-01 - 0.5.8 - fix(core)
|
||||||
|
Fix SmartPdf lifecycle management and update dependencies
|
||||||
|
|
||||||
|
- Moved SmartPdf instance management to the MultiModalModel base class for better resource sharing
|
||||||
|
- Fixed memory leaks by properly implementing cleanup in the base class stop() method
|
||||||
|
- Updated SmartAi class to properly stop all providers on shutdown
|
||||||
|
- Updated @push.rocks/smartrequest from v2.1.0 to v4.2.1 with migration to new API
|
||||||
|
- Enhanced readme with professional documentation and feature matrix
|
||||||
|
|
||||||
|
## 2025-07-26 - 0.5.7 - fix(provider.openai)
|
||||||
|
Fix stream type mismatch in audio method
|
||||||
|
|
||||||
|
- Fixed type error where OpenAI SDK returns a web ReadableStream but the audio method needs to return a Node.js ReadableStream
|
||||||
|
- Added conversion using Node.js's built-in Readable.fromWeb() method
|
||||||
|
|
||||||
## 2025-07-25 - 0.5.5 - feat(documentation)
|
## 2025-07-25 - 0.5.5 - feat(documentation)
|
||||||
Comprehensive documentation enhancement and test improvements
|
Comprehensive documentation enhancement and test improvements
|
||||||
|
|
||||||
|
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartai",
|
"name": "@push.rocks/smartai",
|
||||||
"version": "0.5.5",
|
"version": "0.5.9",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "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.",
|
"description": "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.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
@@ -26,12 +26,12 @@
|
|||||||
"@anthropic-ai/sdk": "^0.57.0",
|
"@anthropic-ai/sdk": "^0.57.0",
|
||||||
"@push.rocks/smartarray": "^1.1.0",
|
"@push.rocks/smartarray": "^1.1.0",
|
||||||
"@push.rocks/smartfile": "^11.2.5",
|
"@push.rocks/smartfile": "^11.2.5",
|
||||||
"@push.rocks/smartpath": "^5.0.18",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpdf": "^3.2.2",
|
"@push.rocks/smartpdf": "^3.3.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartrequest": "^2.1.0",
|
"@push.rocks/smartrequest": "^4.2.1",
|
||||||
"@push.rocks/webstream": "^1.0.10",
|
"@push.rocks/webstream": "^1.0.10",
|
||||||
"openai": "^5.10.2"
|
"openai": "^5.11.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
505
pnpm-lock.yaml
generated
505
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
680
readme.md
680
readme.md
@@ -1,393 +1,467 @@
|
|||||||
# @push.rocks/smartai
|
# @push.rocks/smartai
|
||||||
|
**One API to rule them all** 🚀
|
||||||
|
|
||||||
SmartAi is a powerful TypeScript library that provides a unified interface for integrating with multiple AI providers including OpenAI, Anthropic, Perplexity, Ollama, Groq, XAI, and Exo. It offers comprehensive support for chat interactions, streaming conversations, text-to-speech, document analysis, and vision processing.
|
[](https://www.npmjs.com/package/@push.rocks/smartai)
|
||||||
|
[](https://www.typescriptlang.org/)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
## Install
|
SmartAI unifies the world's leading AI providers - OpenAI, Anthropic, Perplexity, Ollama, Groq, XAI, and Exo - under a single, elegant TypeScript interface. Build AI applications at lightning speed without vendor lock-in.
|
||||||
|
|
||||||
To install SmartAi into your project, use pnpm:
|
## 🎯 Why SmartAI?
|
||||||
|
|
||||||
|
- **🔌 Universal Interface**: Write once, run with any AI provider. Switch between GPT-4, Claude, Llama, or Grok with a single line change.
|
||||||
|
- **🛡️ Type-Safe**: Full TypeScript support with comprehensive type definitions for all operations
|
||||||
|
- **🌊 Streaming First**: Built for real-time applications with native streaming support
|
||||||
|
- **🎨 Multi-Modal**: Seamlessly work with text, images, audio, and documents
|
||||||
|
- **🏠 Local & Cloud**: Support for both cloud providers and local models via Ollama
|
||||||
|
- **⚡ Zero Lock-In**: Your code remains portable across all AI providers
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm install @push.rocks/smartai
|
npm install @push.rocks/smartai
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
SmartAi provides a clean, consistent API across all supported AI providers. This documentation covers all features with practical examples for each provider and capability.
|
|
||||||
|
|
||||||
### Initialization
|
|
||||||
|
|
||||||
First, initialize SmartAi with the API tokens and configuration for the providers you want to use:
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { SmartAi } from '@push.rocks/smartai';
|
import { SmartAi } from '@push.rocks/smartai';
|
||||||
|
|
||||||
const smartAi = new SmartAi({
|
// Initialize with your favorite providers
|
||||||
// OpenAI - for GPT models, DALL-E, and TTS
|
const ai = new SmartAi({
|
||||||
openaiToken: 'your-openai-api-key',
|
openaiToken: 'sk-...',
|
||||||
|
anthropicToken: 'sk-ant-...'
|
||||||
// Anthropic - for Claude models
|
|
||||||
anthropicToken: 'your-anthropic-api-key',
|
|
||||||
|
|
||||||
// Perplexity - for research-focused AI
|
|
||||||
perplexityToken: 'your-perplexity-api-key',
|
|
||||||
|
|
||||||
// Groq - for fast inference
|
|
||||||
groqToken: 'your-groq-api-key',
|
|
||||||
|
|
||||||
// XAI - for Grok models
|
|
||||||
xaiToken: 'your-xai-api-key',
|
|
||||||
|
|
||||||
// Ollama - for local models
|
|
||||||
ollama: {
|
|
||||||
baseUrl: 'http://localhost:11434',
|
|
||||||
model: 'llama2', // default model for chat
|
|
||||||
visionModel: 'llava' // default model for vision
|
|
||||||
},
|
|
||||||
|
|
||||||
// Exo - for distributed inference
|
|
||||||
exo: {
|
|
||||||
baseUrl: 'http://localhost:8080/v1',
|
|
||||||
apiKey: 'your-exo-api-key'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the SmartAi instance
|
await ai.start();
|
||||||
await smartAi.start();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Providers
|
// Same API, multiple providers
|
||||||
|
const response = await ai.openaiProvider.chat({
|
||||||
SmartAi supports the following AI providers:
|
|
||||||
|
|
||||||
| Provider | Use Case | Key Features |
|
|
||||||
|----------|----------|--------------|
|
|
||||||
| **OpenAI** | General purpose, GPT models | Chat, streaming, TTS, vision, documents |
|
|
||||||
| **Anthropic** | Claude models, safety-focused | Chat, streaming, vision, documents |
|
|
||||||
| **Perplexity** | Research and factual queries | Chat, streaming, documents |
|
|
||||||
| **Groq** | Fast inference | Chat, streaming |
|
|
||||||
| **XAI** | Grok models | Chat, streaming |
|
|
||||||
| **Ollama** | Local models | Chat, streaming, vision |
|
|
||||||
| **Exo** | Distributed inference | Chat, streaming |
|
|
||||||
|
|
||||||
## Core Features
|
|
||||||
|
|
||||||
### 1. Chat Interactions
|
|
||||||
|
|
||||||
SmartAi provides both synchronous and streaming chat capabilities across all supported providers.
|
|
||||||
|
|
||||||
#### Synchronous Chat
|
|
||||||
|
|
||||||
Simple request-response interactions with any provider:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// OpenAI Example
|
|
||||||
const openAiResponse = await smartAi.openaiProvider.chat({
|
|
||||||
systemMessage: 'You are a helpful assistant.',
|
systemMessage: 'You are a helpful assistant.',
|
||||||
userMessage: 'What is the capital of France?',
|
userMessage: 'Explain quantum computing in simple terms',
|
||||||
messageHistory: []
|
messageHistory: []
|
||||||
});
|
});
|
||||||
console.log(openAiResponse.message); // "The capital of France is Paris."
|
|
||||||
|
|
||||||
// Anthropic Example
|
|
||||||
const anthropicResponse = await smartAi.anthropicProvider.chat({
|
|
||||||
systemMessage: 'You are a knowledgeable historian.',
|
|
||||||
userMessage: 'Tell me about the French Revolution',
|
|
||||||
messageHistory: []
|
|
||||||
});
|
|
||||||
console.log(anthropicResponse.message);
|
|
||||||
|
|
||||||
// Using message history for context
|
|
||||||
const contextualResponse = await smartAi.openaiProvider.chat({
|
|
||||||
systemMessage: 'You are a math tutor.',
|
|
||||||
userMessage: 'What about multiplication?',
|
|
||||||
messageHistory: [
|
|
||||||
{ role: 'user', content: 'Can you teach me math?' },
|
|
||||||
{ role: 'assistant', content: 'Of course! What would you like to learn?' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Streaming Chat
|
## 📊 Provider Capabilities Matrix
|
||||||
|
|
||||||
For real-time, token-by-token responses:
|
Choose the right provider for your use case:
|
||||||
|
|
||||||
|
| Provider | Chat | Streaming | TTS | Vision | Documents | Highlights |
|
||||||
|
|----------|:----:|:---------:|:---:|:------:|:---------:|------------|
|
||||||
|
| **OpenAI** | ✅ | ✅ | ✅ | ✅ | ✅ | • GPT-4, DALL-E 3<br>• Industry standard<br>• Most features |
|
||||||
|
| **Anthropic** | ✅ | ✅ | ❌ | ✅ | ✅ | • Claude 3 Opus<br>• Superior reasoning<br>• 200k context |
|
||||||
|
| **Ollama** | ✅ | ✅ | ❌ | ✅ | ✅ | • 100% local<br>• Privacy-first<br>• No API costs |
|
||||||
|
| **XAI** | ✅ | ✅ | ❌ | ❌ | ✅ | • Grok models<br>• Real-time data<br>• Uncensored |
|
||||||
|
| **Perplexity** | ✅ | ✅ | ❌ | ❌ | ❌ | • Web-aware<br>• Research-focused<br>• Citations |
|
||||||
|
| **Groq** | ✅ | ✅ | ❌ | ❌ | ❌ | • 10x faster<br>• LPU inference<br>• Low latency |
|
||||||
|
| **Exo** | ✅ | ✅ | ❌ | ❌ | ❌ | • Distributed<br>• P2P compute<br>• Decentralized |
|
||||||
|
|
||||||
|
## 🎮 Core Features
|
||||||
|
|
||||||
|
### 💬 Universal Chat Interface
|
||||||
|
|
||||||
|
Works identically across all providers:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Create a readable stream for input
|
// Use GPT-4 for complex reasoning
|
||||||
const { readable, writable } = new TransformStream();
|
const gptResponse = await ai.openaiProvider.chat({
|
||||||
const writer = writable.getWriter();
|
systemMessage: 'You are a expert physicist.',
|
||||||
|
userMessage: 'Explain the implications of quantum entanglement',
|
||||||
|
messageHistory: []
|
||||||
|
});
|
||||||
|
|
||||||
// Send a message
|
// Use Claude for safety-critical applications
|
||||||
const encoder = new TextEncoder();
|
const claudeResponse = await ai.anthropicProvider.chat({
|
||||||
await writer.write(encoder.encode(JSON.stringify({
|
systemMessage: 'You are a medical advisor.',
|
||||||
role: 'user',
|
userMessage: 'Review this patient data for concerns',
|
||||||
content: 'Write a haiku about programming'
|
messageHistory: []
|
||||||
})));
|
});
|
||||||
await writer.close();
|
|
||||||
|
|
||||||
// Get streaming response
|
// Use Groq for lightning-fast responses
|
||||||
const responseStream = await smartAi.openaiProvider.chatStream(readable);
|
const groqResponse = await ai.groqProvider.chat({
|
||||||
const reader = responseStream.getReader();
|
systemMessage: 'You are a code reviewer.',
|
||||||
const decoder = new TextDecoder();
|
userMessage: 'Quick! Find the bug in this code: ...',
|
||||||
|
messageHistory: []
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
// Read the stream
|
### 🌊 Real-Time Streaming
|
||||||
|
|
||||||
|
Build responsive chat interfaces with token-by-token streaming:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Create a chat stream
|
||||||
|
const stream = await ai.openaiProvider.chatStream(inputStream);
|
||||||
|
const reader = stream.getReader();
|
||||||
|
|
||||||
|
// Display responses as they arrive
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
if (done) break;
|
if (done) break;
|
||||||
process.stdout.write(value); // Print each chunk as it arrives
|
|
||||||
|
// Update UI in real-time
|
||||||
|
process.stdout.write(value);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Text-to-Speech (Audio Generation)
|
### 🎙️ Text-to-Speech
|
||||||
|
|
||||||
Convert text to natural-sounding speech (currently supported by OpenAI):
|
Generate natural voices with OpenAI:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import * as fs from 'fs';
|
const audioStream = await ai.openaiProvider.audio({
|
||||||
|
message: 'Welcome to the future of AI development!'
|
||||||
// Generate speech from text
|
|
||||||
const audioStream = await smartAi.openaiProvider.audio({
|
|
||||||
message: 'Hello world! This is a test of the text-to-speech system.'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save to file
|
// Stream directly to speakers
|
||||||
const writeStream = fs.createWriteStream('output.mp3');
|
audioStream.pipe(speakerOutput);
|
||||||
audioStream.pipe(writeStream);
|
|
||||||
|
|
||||||
// Or use in your application directly
|
// Or save to file
|
||||||
audioStream.on('data', (chunk) => {
|
audioStream.pipe(fs.createWriteStream('welcome.mp3'));
|
||||||
// Process audio chunks
|
```
|
||||||
|
|
||||||
|
### 👁️ Vision Analysis
|
||||||
|
|
||||||
|
Understand images with multiple providers:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const image = fs.readFileSync('product-photo.jpg');
|
||||||
|
|
||||||
|
// OpenAI: General purpose vision
|
||||||
|
const gptVision = await ai.openaiProvider.vision({
|
||||||
|
image,
|
||||||
|
prompt: 'Describe this product and suggest marketing angles'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Anthropic: Detailed analysis
|
||||||
|
const claudeVision = await ai.anthropicProvider.vision({
|
||||||
|
image,
|
||||||
|
prompt: 'Identify any safety concerns or defects'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ollama: Private, local analysis
|
||||||
|
const ollamaVision = await ai.ollamaProvider.vision({
|
||||||
|
image,
|
||||||
|
prompt: 'Extract all text and categorize the content'
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Vision Processing
|
### 📄 Document Intelligence
|
||||||
|
|
||||||
Analyze images and get detailed descriptions:
|
Extract insights from PDFs with AI:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import * as fs from 'fs';
|
const contract = fs.readFileSync('contract.pdf');
|
||||||
|
const invoice = fs.readFileSync('invoice.pdf');
|
||||||
|
|
||||||
// Read an image file
|
// Analyze documents
|
||||||
const imageBuffer = fs.readFileSync('image.jpg');
|
const analysis = await ai.openaiProvider.document({
|
||||||
|
|
||||||
// OpenAI Vision
|
|
||||||
const openAiVision = await smartAi.openaiProvider.vision({
|
|
||||||
image: imageBuffer,
|
|
||||||
prompt: 'What is in this image? Describe in detail.'
|
|
||||||
});
|
|
||||||
console.log('OpenAI:', openAiVision);
|
|
||||||
|
|
||||||
// Anthropic Vision
|
|
||||||
const anthropicVision = await smartAi.anthropicProvider.vision({
|
|
||||||
image: imageBuffer,
|
|
||||||
prompt: 'Analyze this image and identify any text or objects.'
|
|
||||||
});
|
|
||||||
console.log('Anthropic:', anthropicVision);
|
|
||||||
|
|
||||||
// Ollama Vision (using local model)
|
|
||||||
const ollamaVision = await smartAi.ollamaProvider.vision({
|
|
||||||
image: imageBuffer,
|
|
||||||
prompt: 'Describe the colors and composition of this image.'
|
|
||||||
});
|
|
||||||
console.log('Ollama:', ollamaVision);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Document Analysis
|
|
||||||
|
|
||||||
Process and analyze PDF documents with AI:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import * as fs from 'fs';
|
|
||||||
|
|
||||||
// Read PDF documents
|
|
||||||
const pdfBuffer = fs.readFileSync('document.pdf');
|
|
||||||
|
|
||||||
// Analyze with OpenAI
|
|
||||||
const openAiAnalysis = await smartAi.openaiProvider.document({
|
|
||||||
systemMessage: 'You are a document analyst. Extract key information.',
|
|
||||||
userMessage: 'Summarize this document and list the main points.',
|
|
||||||
messageHistory: [],
|
|
||||||
pdfDocuments: [pdfBuffer]
|
|
||||||
});
|
|
||||||
console.log('OpenAI Analysis:', openAiAnalysis.message);
|
|
||||||
|
|
||||||
// Analyze with Anthropic
|
|
||||||
const anthropicAnalysis = await smartAi.anthropicProvider.document({
|
|
||||||
systemMessage: 'You are a legal expert.',
|
systemMessage: 'You are a legal expert.',
|
||||||
userMessage: 'Identify any legal terms or implications in this document.',
|
userMessage: 'Compare these documents and highlight key differences',
|
||||||
messageHistory: [],
|
messageHistory: [],
|
||||||
pdfDocuments: [pdfBuffer]
|
pdfDocuments: [contract, invoice]
|
||||||
});
|
});
|
||||||
console.log('Anthropic Analysis:', anthropicAnalysis.message);
|
|
||||||
|
|
||||||
// Process multiple documents
|
// Multi-document analysis
|
||||||
const doc1 = fs.readFileSync('contract1.pdf');
|
const taxDocs = [form1099, w2, receipts];
|
||||||
const doc2 = fs.readFileSync('contract2.pdf');
|
const taxAnalysis = await ai.anthropicProvider.document({
|
||||||
|
systemMessage: 'You are a tax advisor.',
|
||||||
const comparison = await smartAi.openaiProvider.document({
|
userMessage: 'Prepare a tax summary from these documents',
|
||||||
systemMessage: 'You are a contract analyst.',
|
|
||||||
userMessage: 'Compare these two contracts and highlight the differences.',
|
|
||||||
messageHistory: [],
|
messageHistory: [],
|
||||||
pdfDocuments: [doc1, doc2]
|
pdfDocuments: taxDocs
|
||||||
});
|
});
|
||||||
console.log('Comparison:', comparison.message);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Conversation Management
|
### 🔄 Persistent Conversations
|
||||||
|
|
||||||
Create persistent conversation sessions with any provider:
|
Maintain context across interactions:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Create a conversation with OpenAI
|
// Create a coding assistant conversation
|
||||||
const conversation = smartAi.createConversation('openai');
|
const assistant = ai.createConversation('openai');
|
||||||
|
await assistant.setSystemMessage('You are an expert TypeScript developer.');
|
||||||
|
|
||||||
// Set the system message
|
// First question
|
||||||
await conversation.setSystemMessage('You are a helpful coding assistant.');
|
const inputWriter = assistant.getInputStreamWriter();
|
||||||
|
await inputWriter.write('How do I implement a singleton pattern?');
|
||||||
// Get input and output streams
|
|
||||||
const inputWriter = conversation.getInputStreamWriter();
|
|
||||||
const outputStream = conversation.getOutputStream();
|
|
||||||
|
|
||||||
// Set up output reader
|
|
||||||
const reader = outputStream.getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
|
|
||||||
// Send messages
|
|
||||||
await inputWriter.write('How do I create a REST API in Node.js?');
|
|
||||||
|
|
||||||
// Read responses
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
console.log('Assistant:', decoder.decode(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue the conversation
|
// Continue the conversation
|
||||||
await inputWriter.write('Can you show me an example with Express?');
|
await inputWriter.write('Now show me how to make it thread-safe');
|
||||||
|
|
||||||
// Create conversations with different providers
|
// The assistant remembers the entire context
|
||||||
const anthropicConversation = smartAi.createConversation('anthropic');
|
|
||||||
const groqConversation = smartAi.createConversation('groq');
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced Usage
|
## 🚀 Real-World Examples
|
||||||
|
|
||||||
### Error Handling
|
### Build a Customer Support Bot
|
||||||
|
|
||||||
Always wrap AI operations in try-catch blocks for robust error handling:
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
try {
|
const supportBot = new SmartAi({
|
||||||
const response = await smartAi.openaiProvider.chat({
|
anthropicToken: process.env.ANTHROPIC_KEY // Claude for empathetic responses
|
||||||
systemMessage: 'You are an assistant.',
|
});
|
||||||
userMessage: 'Hello!',
|
|
||||||
|
async function handleCustomerQuery(query: string, history: ChatMessage[]) {
|
||||||
|
try {
|
||||||
|
const response = await supportBot.anthropicProvider.chat({
|
||||||
|
systemMessage: `You are a helpful customer support agent.
|
||||||
|
Be empathetic, professional, and solution-oriented.`,
|
||||||
|
userMessage: query,
|
||||||
|
messageHistory: history
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.message;
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback to another provider if needed
|
||||||
|
return await supportBot.openaiProvider.chat({...});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a Code Review Assistant
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const codeReviewer = new SmartAi({
|
||||||
|
groqToken: process.env.GROQ_KEY // Groq for speed
|
||||||
|
});
|
||||||
|
|
||||||
|
async function reviewCode(code: string, language: string) {
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
const review = await codeReviewer.groqProvider.chat({
|
||||||
|
systemMessage: `You are a ${language} expert. Review code for:
|
||||||
|
- Security vulnerabilities
|
||||||
|
- Performance issues
|
||||||
|
- Best practices
|
||||||
|
- Potential bugs`,
|
||||||
|
userMessage: `Review this code:\n\n${code}`,
|
||||||
messageHistory: []
|
messageHistory: []
|
||||||
});
|
});
|
||||||
console.log(response.message);
|
|
||||||
} catch (error) {
|
console.log(`Review completed in ${Date.now() - startTime}ms`);
|
||||||
if (error.code === 'rate_limit_exceeded') {
|
return review.message;
|
||||||
console.error('Rate limit hit, please retry later');
|
}
|
||||||
} else if (error.code === 'invalid_api_key') {
|
```
|
||||||
console.error('Invalid API key provided');
|
|
||||||
} else {
|
### Build a Research Assistant
|
||||||
console.error('Unexpected error:', error.message);
|
|
||||||
|
```typescript
|
||||||
|
const researcher = new SmartAi({
|
||||||
|
perplexityToken: process.env.PERPLEXITY_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
async function research(topic: string) {
|
||||||
|
// Perplexity excels at web-aware research
|
||||||
|
const findings = await researcher.perplexityProvider.chat({
|
||||||
|
systemMessage: 'You are a research assistant. Provide factual, cited information.',
|
||||||
|
userMessage: `Research the latest developments in ${topic}`,
|
||||||
|
messageHistory: []
|
||||||
|
});
|
||||||
|
|
||||||
|
return findings.message;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local AI for Sensitive Data
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const localAI = new SmartAi({
|
||||||
|
ollama: {
|
||||||
|
baseUrl: 'http://localhost:11434',
|
||||||
|
model: 'llama2',
|
||||||
|
visionModel: 'llava'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process sensitive documents without leaving your infrastructure
|
||||||
|
async function analyzeSensitiveDoc(pdfBuffer: Buffer) {
|
||||||
|
const analysis = await localAI.ollamaProvider.document({
|
||||||
|
systemMessage: 'Extract and summarize key information.',
|
||||||
|
userMessage: 'Analyze this confidential document',
|
||||||
|
messageHistory: [],
|
||||||
|
pdfDocuments: [pdfBuffer]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Data never leaves your servers
|
||||||
|
return analysis.message;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚡ Performance Tips
|
||||||
|
|
||||||
|
### 1. Provider Selection Strategy
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class SmartAIRouter {
|
||||||
|
constructor(private ai: SmartAi) {}
|
||||||
|
|
||||||
|
async query(message: string, requirements: {
|
||||||
|
speed?: boolean;
|
||||||
|
accuracy?: boolean;
|
||||||
|
cost?: boolean;
|
||||||
|
privacy?: boolean;
|
||||||
|
}) {
|
||||||
|
if (requirements.privacy) {
|
||||||
|
return this.ai.ollamaProvider.chat({...}); // Local only
|
||||||
|
}
|
||||||
|
if (requirements.speed) {
|
||||||
|
return this.ai.groqProvider.chat({...}); // 10x faster
|
||||||
|
}
|
||||||
|
if (requirements.accuracy) {
|
||||||
|
return this.ai.anthropicProvider.chat({...}); // Best reasoning
|
||||||
|
}
|
||||||
|
// Default fallback
|
||||||
|
return this.ai.openaiProvider.chat({...});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Streaming with Custom Processing
|
### 2. Streaming for Large Responses
|
||||||
|
|
||||||
Implement custom transformations on streaming responses:
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Create a custom transform stream
|
// Don't wait for the entire response
|
||||||
const customTransform = new TransformStream({
|
async function streamResponse(userQuery: string) {
|
||||||
transform(chunk, controller) {
|
const stream = await ai.openaiProvider.chatStream(createInputStream(userQuery));
|
||||||
// Example: Add timestamps to each chunk
|
|
||||||
const timestamp = new Date().toISOString();
|
// Process tokens as they arrive
|
||||||
controller.enqueue(`[${timestamp}] ${chunk}`);
|
for await (const chunk of stream) {
|
||||||
|
updateUI(chunk); // Immediate feedback
|
||||||
|
await processChunk(chunk); // Parallel processing
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Apply to streaming chat
|
|
||||||
const inputStream = new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(new TextEncoder().encode(JSON.stringify({
|
|
||||||
role: 'user',
|
|
||||||
content: 'Tell me a story'
|
|
||||||
})));
|
|
||||||
controller.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const responseStream = await smartAi.openaiProvider.chatStream(inputStream);
|
|
||||||
const processedStream = responseStream.pipeThrough(customTransform);
|
|
||||||
|
|
||||||
// Read processed stream
|
|
||||||
const reader = processedStream.getReader();
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
console.log(value);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Provider-Specific Features
|
### 3. Parallel Multi-Provider Queries
|
||||||
|
|
||||||
Each provider may have unique capabilities. Here's how to leverage them:
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// OpenAI - Use specific models
|
// Get the best answer from multiple AIs
|
||||||
const gpt4Response = await smartAi.openaiProvider.chat({
|
async function consensusQuery(question: string) {
|
||||||
systemMessage: 'You are a helpful assistant.',
|
const providers = [
|
||||||
userMessage: 'Explain quantum computing',
|
ai.openaiProvider.chat({...}),
|
||||||
messageHistory: []
|
ai.anthropicProvider.chat({...}),
|
||||||
});
|
ai.perplexityProvider.chat({...})
|
||||||
|
];
|
||||||
|
|
||||||
// Anthropic - Use Claude's strength in analysis
|
const responses = await Promise.all(providers);
|
||||||
const codeReview = await smartAi.anthropicProvider.chat({
|
return synthesizeResponses(responses);
|
||||||
systemMessage: 'You are a code reviewer.',
|
}
|
||||||
userMessage: 'Review this code for security issues: ...',
|
|
||||||
messageHistory: []
|
|
||||||
});
|
|
||||||
|
|
||||||
// Perplexity - Best for research and current events
|
|
||||||
const research = await smartAi.perplexityProvider.chat({
|
|
||||||
systemMessage: 'You are a research assistant.',
|
|
||||||
userMessage: 'What are the latest developments in renewable energy?',
|
|
||||||
messageHistory: []
|
|
||||||
});
|
|
||||||
|
|
||||||
// Groq - Optimized for speed
|
|
||||||
const quickResponse = await smartAi.groqProvider.chat({
|
|
||||||
systemMessage: 'You are a quick helper.',
|
|
||||||
userMessage: 'Give me a one-line summary of photosynthesis',
|
|
||||||
messageHistory: []
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Performance Optimization
|
## 🛠️ Advanced Features
|
||||||
|
|
||||||
Tips for optimal performance:
|
### Custom Streaming Transformations
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 1. Reuse providers instead of creating new instances
|
// Add real-time translation
|
||||||
const smartAi = new SmartAi({ /* config */ });
|
const translationStream = new TransformStream({
|
||||||
await smartAi.start(); // Initialize once
|
async transform(chunk, controller) {
|
||||||
|
const translated = await translateChunk(chunk);
|
||||||
|
controller.enqueue(translated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 2. Use streaming for long responses
|
const responseStream = await ai.openaiProvider.chatStream(input);
|
||||||
// Streaming reduces time-to-first-token and memory usage
|
const translatedStream = responseStream.pipeThrough(translationStream);
|
||||||
|
|
||||||
// 3. Batch operations when possible
|
|
||||||
const promises = [
|
|
||||||
smartAi.openaiProvider.chat({ /* ... */ }),
|
|
||||||
smartAi.anthropicProvider.chat({ /* ... */ })
|
|
||||||
];
|
|
||||||
const results = await Promise.all(promises);
|
|
||||||
|
|
||||||
// 4. Clean up resources
|
|
||||||
await smartAi.stop(); // When done
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Error Handling & Fallbacks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class ResilientAI {
|
||||||
|
private providers = ['openai', 'anthropic', 'groq'];
|
||||||
|
|
||||||
|
async query(opts: ChatOptions): Promise<ChatResponse> {
|
||||||
|
for (const provider of this.providers) {
|
||||||
|
try {
|
||||||
|
return await this.ai[`${provider}Provider`].chat(opts);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`${provider} failed, trying next...`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('All providers failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Counting & Cost Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Track usage across providers
|
||||||
|
class UsageTracker {
|
||||||
|
async trackedChat(provider: string, options: ChatOptions) {
|
||||||
|
const start = Date.now();
|
||||||
|
const response = await ai[`${provider}Provider`].chat(options);
|
||||||
|
|
||||||
|
const usage = {
|
||||||
|
provider,
|
||||||
|
duration: Date.now() - start,
|
||||||
|
inputTokens: estimateTokens(options),
|
||||||
|
outputTokens: estimateTokens(response.message)
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.logUsage(usage);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Installation & Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Node.js 16+
|
||||||
|
- TypeScript 4.5+
|
||||||
|
- API keys for your chosen providers
|
||||||
|
|
||||||
|
### Environment Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install
|
||||||
|
npm install @push.rocks/smartai
|
||||||
|
|
||||||
|
# Set up environment variables
|
||||||
|
export OPENAI_API_KEY=sk-...
|
||||||
|
export ANTHROPIC_API_KEY=sk-ant-...
|
||||||
|
export PERPLEXITY_API_KEY=pplx-...
|
||||||
|
# ... etc
|
||||||
|
```
|
||||||
|
|
||||||
|
### TypeScript Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Choosing the Right Provider
|
||||||
|
|
||||||
|
| Use Case | Recommended Provider | Why |
|
||||||
|
|----------|---------------------|-----|
|
||||||
|
| **General Purpose** | OpenAI | Most features, stable, well-documented |
|
||||||
|
| **Complex Reasoning** | Anthropic | Superior logical thinking, safer outputs |
|
||||||
|
| **Research & Facts** | Perplexity | Web-aware, provides citations |
|
||||||
|
| **Speed Critical** | Groq | 10x faster inference, sub-second responses |
|
||||||
|
| **Privacy Critical** | Ollama | 100% local, no data leaves your servers |
|
||||||
|
| **Real-time Data** | XAI | Access to current information |
|
||||||
|
| **Cost Sensitive** | Ollama/Exo | Free (local) or distributed compute |
|
||||||
|
|
||||||
|
## 📈 Roadmap
|
||||||
|
|
||||||
|
- [ ] Streaming function calls
|
||||||
|
- [ ] Image generation support
|
||||||
|
- [ ] Voice input processing
|
||||||
|
- [ ] Fine-tuning integration
|
||||||
|
- [ ] Embedding support
|
||||||
|
- [ ] Agent framework
|
||||||
|
- [ ] More providers (Cohere, AI21, etc.)
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
|
||||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||||
|
@@ -29,12 +29,14 @@ tap.test('should create chat response with openai', async () => {
|
|||||||
|
|
||||||
tap.test('should document a pdf', async () => {
|
tap.test('should document a pdf', async () => {
|
||||||
const pdfUrl = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf';
|
const pdfUrl = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf';
|
||||||
const pdfResponse = await smartrequest.getBinary(pdfUrl);
|
const pdfResponse = await smartrequest.SmartRequest.create()
|
||||||
|
.url(pdfUrl)
|
||||||
|
.get();
|
||||||
const result = await testSmartai.openaiProvider.document({
|
const result = await testSmartai.openaiProvider.document({
|
||||||
systemMessage: 'Classify the document. Only the following answers are allowed: "invoice", "bank account statement", "contract", "other". The answer should only contain the keyword for machine use.',
|
systemMessage: 'Classify the document. Only the following answers are allowed: "invoice", "bank account statement", "contract", "other". The answer should only contain the keyword for machine use.',
|
||||||
userMessage: "Classify the document.",
|
userMessage: "Classify the document.",
|
||||||
messageHistory: [],
|
messageHistory: [],
|
||||||
pdfDocuments: [pdfResponse.body],
|
pdfDocuments: [Buffer.from(await pdfResponse.arrayBuffer())],
|
||||||
});
|
});
|
||||||
console.log(result);
|
console.log(result);
|
||||||
});
|
});
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message format for chat interactions
|
* Message format for chat interactions
|
||||||
*/
|
*/
|
||||||
@@ -28,17 +30,30 @@ export interface ChatResponse {
|
|||||||
* Provides a common interface for different AI providers (OpenAI, Anthropic, Perplexity, Ollama)
|
* Provides a common interface for different AI providers (OpenAI, Anthropic, Perplexity, Ollama)
|
||||||
*/
|
*/
|
||||||
export abstract class MultiModalModel {
|
export abstract class MultiModalModel {
|
||||||
|
/**
|
||||||
|
* SmartPdf instance for document processing
|
||||||
|
* Shared across all methods that need PDF functionality
|
||||||
|
*/
|
||||||
|
protected smartpdfInstance: plugins.smartpdf.SmartPdf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the model and any necessary resources
|
* Initializes the model and any necessary resources
|
||||||
* Should be called before using any other methods
|
* Should be called before using any other methods
|
||||||
*/
|
*/
|
||||||
abstract start(): Promise<void>;
|
public async start(): Promise<void> {
|
||||||
|
this.smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
||||||
|
await this.smartpdfInstance.start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up any resources used by the model
|
* Cleans up any resources used by the model
|
||||||
* Should be called when the model is no longer needed
|
* Should be called when the model is no longer needed
|
||||||
*/
|
*/
|
||||||
abstract stop(): Promise<void>;
|
public async stop(): Promise<void> {
|
||||||
|
if (this.smartpdfInstance) {
|
||||||
|
await this.smartpdfInstance.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronous chat interaction with the model
|
* Synchronous chat interaction with the model
|
||||||
|
@@ -91,7 +91,29 @@ export class SmartAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop() {}
|
public async stop() {
|
||||||
|
if (this.openaiProvider) {
|
||||||
|
await this.openaiProvider.stop();
|
||||||
|
}
|
||||||
|
if (this.anthropicProvider) {
|
||||||
|
await this.anthropicProvider.stop();
|
||||||
|
}
|
||||||
|
if (this.perplexityProvider) {
|
||||||
|
await this.perplexityProvider.stop();
|
||||||
|
}
|
||||||
|
if (this.groqProvider) {
|
||||||
|
await this.groqProvider.stop();
|
||||||
|
}
|
||||||
|
if (this.xaiProvider) {
|
||||||
|
await this.xaiProvider.stop();
|
||||||
|
}
|
||||||
|
if (this.ollamaProvider) {
|
||||||
|
await this.ollamaProvider.stop();
|
||||||
|
}
|
||||||
|
if (this.exoProvider) {
|
||||||
|
await this.exoProvider.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a new conversation
|
* create a new conversation
|
||||||
|
@@ -20,12 +20,15 @@ export class AnthropicProvider extends MultiModalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
|
await super.start();
|
||||||
this.anthropicApiClient = new plugins.anthropic.default({
|
this.anthropicApiClient = new plugins.anthropic.default({
|
||||||
apiKey: this.options.anthropicToken,
|
apiKey: this.options.anthropicToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop() {}
|
async stop() {
|
||||||
|
await super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
||||||
// Create a TextDecoder to handle incoming chunks
|
// Create a TextDecoder to handle incoming chunks
|
||||||
@@ -178,11 +181,10 @@ export class AnthropicProvider extends MultiModalModel {
|
|||||||
messageHistory: ChatMessage[];
|
messageHistory: ChatMessage[];
|
||||||
}): Promise<{ message: any }> {
|
}): Promise<{ message: any }> {
|
||||||
// Convert PDF documents to images using SmartPDF
|
// Convert PDF documents to images using SmartPDF
|
||||||
const smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
|
||||||
let documentImageBytesArray: Uint8Array[] = [];
|
let documentImageBytesArray: Uint8Array[] = [];
|
||||||
|
|
||||||
for (const pdfDocument of optionsArg.pdfDocuments) {
|
for (const pdfDocument of optionsArg.pdfDocuments) {
|
||||||
const documentImageArray = await smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
const documentImageArray = await this.smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
||||||
documentImageBytesArray = documentImageBytesArray.concat(documentImageArray);
|
documentImageBytesArray = documentImageBytesArray.concat(documentImageArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ export class OllamaProvider extends MultiModalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
|
await super.start();
|
||||||
// Verify Ollama is running
|
// Verify Ollama is running
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${this.baseUrl}/api/tags`);
|
const response = await fetch(`${this.baseUrl}/api/tags`);
|
||||||
@@ -35,7 +36,9 @@ export class OllamaProvider extends MultiModalModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop() {}
|
async stop() {
|
||||||
|
await super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
||||||
// Create a TextDecoder to handle incoming chunks
|
// Create a TextDecoder to handle incoming chunks
|
||||||
@@ -205,11 +208,10 @@ export class OllamaProvider extends MultiModalModel {
|
|||||||
messageHistory: ChatMessage[];
|
messageHistory: ChatMessage[];
|
||||||
}): Promise<{ message: any }> {
|
}): Promise<{ message: any }> {
|
||||||
// Convert PDF documents to images using SmartPDF
|
// Convert PDF documents to images using SmartPDF
|
||||||
const smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
|
||||||
let documentImageBytesArray: Uint8Array[] = [];
|
let documentImageBytesArray: Uint8Array[] = [];
|
||||||
|
|
||||||
for (const pdfDocument of optionsArg.pdfDocuments) {
|
for (const pdfDocument of optionsArg.pdfDocuments) {
|
||||||
const documentImageArray = await smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
const documentImageArray = await this.smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
||||||
documentImageBytesArray = documentImageBytesArray.concat(documentImageArray);
|
documentImageBytesArray = documentImageBytesArray.concat(documentImageArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
import * as paths from './paths.js';
|
import * as paths from './paths.js';
|
||||||
|
import { Readable } from 'stream';
|
||||||
|
|
||||||
// Custom type definition for chat completion messages
|
// Custom type definition for chat completion messages
|
||||||
export type TChatCompletionRequestMessage = {
|
export type TChatCompletionRequestMessage = {
|
||||||
@@ -20,7 +21,6 @@ export interface IOpenaiProviderOptions {
|
|||||||
export class OpenAiProvider extends MultiModalModel {
|
export class OpenAiProvider extends MultiModalModel {
|
||||||
private options: IOpenaiProviderOptions;
|
private options: IOpenaiProviderOptions;
|
||||||
public openAiApiClient: plugins.openai.default;
|
public openAiApiClient: plugins.openai.default;
|
||||||
public smartpdfInstance: plugins.smartpdf.SmartPdf;
|
|
||||||
|
|
||||||
constructor(optionsArg: IOpenaiProviderOptions) {
|
constructor(optionsArg: IOpenaiProviderOptions) {
|
||||||
super();
|
super();
|
||||||
@@ -28,14 +28,16 @@ export class OpenAiProvider extends MultiModalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async start() {
|
public async start() {
|
||||||
|
await super.start();
|
||||||
this.openAiApiClient = new plugins.openai.default({
|
this.openAiApiClient = new plugins.openai.default({
|
||||||
apiKey: this.options.openaiToken,
|
apiKey: this.options.openaiToken,
|
||||||
dangerouslyAllowBrowser: true,
|
dangerouslyAllowBrowser: true,
|
||||||
});
|
});
|
||||||
this.smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop() {}
|
public async stop() {
|
||||||
|
await super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
||||||
// Create a TextDecoder to handle incoming chunks
|
// Create a TextDecoder to handle incoming chunks
|
||||||
@@ -148,7 +150,8 @@ export class OpenAiProvider extends MultiModalModel {
|
|||||||
speed: 1,
|
speed: 1,
|
||||||
});
|
});
|
||||||
const stream = result.body;
|
const stream = result.body;
|
||||||
done.resolve(stream);
|
const nodeStream = Readable.fromWeb(stream as any);
|
||||||
|
done.resolve(nodeStream);
|
||||||
return done.promise;
|
return done.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,13 +167,10 @@ export class OpenAiProvider extends MultiModalModel {
|
|||||||
let pdfDocumentImageBytesArray: Uint8Array[] = [];
|
let pdfDocumentImageBytesArray: Uint8Array[] = [];
|
||||||
|
|
||||||
// Convert each PDF into one or more image byte arrays.
|
// Convert each PDF into one or more image byte arrays.
|
||||||
const smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
|
||||||
await smartpdfInstance.start();
|
|
||||||
for (const pdfDocument of optionsArg.pdfDocuments) {
|
for (const pdfDocument of optionsArg.pdfDocuments) {
|
||||||
const documentImageArray = await smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
const documentImageArray = await this.smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
||||||
pdfDocumentImageBytesArray = pdfDocumentImageBytesArray.concat(documentImageArray);
|
pdfDocumentImageBytesArray = pdfDocumentImageBytesArray.concat(documentImageArray);
|
||||||
}
|
}
|
||||||
await smartpdfInstance.stop();
|
|
||||||
|
|
||||||
console.log(`image smartfile array`);
|
console.log(`image smartfile array`);
|
||||||
console.log(pdfDocumentImageBytesArray.map((smartfile) => smartfile.length));
|
console.log(pdfDocumentImageBytesArray.map((smartfile) => smartfile.length));
|
||||||
|
@@ -11,7 +11,6 @@ export interface IXAIProviderOptions {
|
|||||||
export class XAIProvider extends MultiModalModel {
|
export class XAIProvider extends MultiModalModel {
|
||||||
private options: IXAIProviderOptions;
|
private options: IXAIProviderOptions;
|
||||||
public openAiApiClient: plugins.openai.default;
|
public openAiApiClient: plugins.openai.default;
|
||||||
public smartpdfInstance: plugins.smartpdf.SmartPdf;
|
|
||||||
|
|
||||||
constructor(optionsArg: IXAIProviderOptions) {
|
constructor(optionsArg: IXAIProviderOptions) {
|
||||||
super();
|
super();
|
||||||
@@ -19,14 +18,16 @@ export class XAIProvider extends MultiModalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async start() {
|
public async start() {
|
||||||
|
await super.start();
|
||||||
this.openAiApiClient = new plugins.openai.default({
|
this.openAiApiClient = new plugins.openai.default({
|
||||||
apiKey: this.options.xaiToken,
|
apiKey: this.options.xaiToken,
|
||||||
baseURL: 'https://api.x.ai/v1',
|
baseURL: 'https://api.x.ai/v1',
|
||||||
});
|
});
|
||||||
this.smartpdfInstance = new plugins.smartpdf.SmartPdf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop() {}
|
public async stop() {
|
||||||
|
await super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
public async chatStream(input: ReadableStream<Uint8Array>): Promise<ReadableStream<string>> {
|
||||||
// Create a TextDecoder to handle incoming chunks
|
// Create a TextDecoder to handle incoming chunks
|
||||||
|
Reference in New Issue
Block a user