Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
0a80ac0a8a | |||
6ce442354e | |||
9b38a3c06e | |||
5dead05324 | |||
6916dd9e2a | |||
f89888a542 |
21
changelog.md
21
changelog.md
@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-02-25 - 0.4.2 - fix(core)
|
||||
Fix OpenAI chat streaming and PDF document processing logic.
|
||||
|
||||
- Updated OpenAI chat streaming to handle new async iterable format.
|
||||
- Improved PDF document processing by filtering out empty image buffers.
|
||||
- Removed unsupported temperature options from OpenAI requests.
|
||||
|
||||
## 2025-02-25 - 0.4.1 - fix(provider)
|
||||
Fix provider modules for consistency
|
||||
|
||||
- Updated TypeScript interfaces and options in provider modules for better type safety.
|
||||
- Modified transform stream handlers in Exo, Groq, and Ollama providers for consistency.
|
||||
- Added optional model options to OpenAI provider for custom model usage.
|
||||
|
||||
## 2025-02-08 - 0.4.0 - feat(core)
|
||||
Added support for Exo AI provider
|
||||
|
||||
- Introduced ExoProvider with chat functionalities.
|
||||
- Updated SmartAi class to initialize ExoProvider.
|
||||
- Extended Conversation class to support ExoProvider.
|
||||
|
||||
## 2025-02-05 - 0.3.3 - fix(documentation)
|
||||
Update readme with detailed license and legal information.
|
||||
|
||||
|
37
package.json
37
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartai",
|
||||
"version": "0.3.3",
|
||||
"version": "0.4.2",
|
||||
"private": false,
|
||||
"description": "A TypeScript library for integrating and interacting with multiple AI models, offering capabilities for chat and potentially audio responses.",
|
||||
"main": "dist_ts/index.js",
|
||||
@ -14,24 +14,24 @@
|
||||
"buildDocs": "(tsdoc)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.84",
|
||||
"@git.zone/tsbundle": "^2.0.5",
|
||||
"@git.zone/tsrun": "^1.2.49",
|
||||
"@git.zone/tstest": "^1.0.90",
|
||||
"@push.rocks/qenv": "^6.0.5",
|
||||
"@push.rocks/tapbundle": "^5.3.0",
|
||||
"@types/node": "^22.5.5"
|
||||
"@git.zone/tsbuild": "^2.2.1",
|
||||
"@git.zone/tsbundle": "^2.2.5",
|
||||
"@git.zone/tsrun": "^1.3.3",
|
||||
"@git.zone/tstest": "^1.0.96",
|
||||
"@push.rocks/qenv": "^6.1.0",
|
||||
"@push.rocks/tapbundle": "^5.5.6",
|
||||
"@types/node": "^22.13.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.27.3",
|
||||
"@push.rocks/smartarray": "^1.0.8",
|
||||
"@push.rocks/smartfile": "^11.0.21",
|
||||
"@anthropic-ai/sdk": "^0.37.0",
|
||||
"@push.rocks/smartarray": "^1.1.0",
|
||||
"@push.rocks/smartfile": "^11.2.0",
|
||||
"@push.rocks/smartpath": "^5.0.18",
|
||||
"@push.rocks/smartpdf": "^3.1.6",
|
||||
"@push.rocks/smartpromise": "^4.0.4",
|
||||
"@push.rocks/smartrequest": "^2.0.22",
|
||||
"@push.rocks/smartpdf": "^3.2.2",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
"@push.rocks/smartrequest": "^2.0.23",
|
||||
"@push.rocks/webstream": "^1.0.10",
|
||||
"openai": "^4.62.1"
|
||||
"openai": "^4.85.4"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -66,5 +66,10 @@
|
||||
"audio responses",
|
||||
"text-to-speech",
|
||||
"streaming chat"
|
||||
]
|
||||
],
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"puppeteer"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
6084
pnpm-lock.yaml
generated
6084
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
17
readme.md
17
readme.md
@ -110,6 +110,19 @@ SmartAi supports multiple AI providers. Configure each provider with its corresp
|
||||
}
|
||||
```
|
||||
|
||||
### Exo
|
||||
|
||||
- **Models:** Configurable (supports LLaMA, Mistral, LlaVA, Qwen, and Deepseek)
|
||||
- **Features:** Chat, Streaming
|
||||
- **Configuration Example:**
|
||||
|
||||
```typescript
|
||||
exo: {
|
||||
baseUrl: 'http://localhost:8080/v1', // Optional
|
||||
apiKey: 'your-api-key' // Optional for local deployments
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Initialize SmartAi with the provider configurations you plan to use:
|
||||
@ -126,6 +139,10 @@ const smartAi = new SmartAi({
|
||||
ollama: {
|
||||
baseUrl: 'http://localhost:11434',
|
||||
model: 'llama2'
|
||||
},
|
||||
exo: {
|
||||
baseUrl: 'http://localhost:8080/v1',
|
||||
apiKey: 'your-api-key'
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartai',
|
||||
version: '0.3.3',
|
||||
version: '0.4.2',
|
||||
description: 'A TypeScript library for integrating and interacting with multiple AI models, offering capabilities for chat and potentially audio responses.'
|
||||
}
|
||||
|
@ -48,6 +48,18 @@ export class Conversation {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
public static async createWithExo(smartaiRefArg: SmartAi) {
|
||||
if (!smartaiRefArg.exoProvider) {
|
||||
throw new Error('Exo provider not available');
|
||||
}
|
||||
const conversation = new Conversation(smartaiRefArg, {
|
||||
processFunction: async (input) => {
|
||||
return '' // TODO implement proper streaming
|
||||
}
|
||||
});
|
||||
return conversation;
|
||||
}
|
||||
|
||||
public static async createWithOllama(smartaiRefArg: SmartAi) {
|
||||
if (!smartaiRefArg.ollamaProvider) {
|
||||
throw new Error('Ollama provider not available');
|
||||
@ -60,6 +72,30 @@ export class Conversation {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
public static async createWithGroq(smartaiRefArg: SmartAi) {
|
||||
if (!smartaiRefArg.groqProvider) {
|
||||
throw new Error('Groq provider not available');
|
||||
}
|
||||
const conversation = new Conversation(smartaiRefArg, {
|
||||
processFunction: async (input) => {
|
||||
return '' // TODO implement proper streaming
|
||||
}
|
||||
});
|
||||
return conversation;
|
||||
}
|
||||
|
||||
public static async createWithXai(smartaiRefArg: SmartAi) {
|
||||
if (!smartaiRefArg.xaiProvider) {
|
||||
throw new Error('XAI provider not available');
|
||||
}
|
||||
const conversation = new Conversation(smartaiRefArg, {
|
||||
processFunction: async (input) => {
|
||||
return '' // TODO implement proper streaming
|
||||
}
|
||||
});
|
||||
return conversation;
|
||||
}
|
||||
|
||||
// INSTANCE
|
||||
smartaiRef: SmartAi
|
||||
private systemMessage: string;
|
||||
|
@ -1,18 +1,32 @@
|
||||
import { Conversation } from './classes.conversation.js';
|
||||
import * as plugins from './plugins.js';
|
||||
import { AnthropicProvider } from './provider.anthropic.js';
|
||||
import type { OllamaProvider } from './provider.ollama.js';
|
||||
import { OllamaProvider } from './provider.ollama.js';
|
||||
import { OpenAiProvider } from './provider.openai.js';
|
||||
import type { PerplexityProvider } from './provider.perplexity.js';
|
||||
import { PerplexityProvider } from './provider.perplexity.js';
|
||||
import { ExoProvider } from './provider.exo.js';
|
||||
import { GroqProvider } from './provider.groq.js';
|
||||
import { XAIProvider } from './provider.xai.js';
|
||||
|
||||
|
||||
export interface ISmartAiOptions {
|
||||
openaiToken?: string;
|
||||
anthropicToken?: string;
|
||||
perplexityToken?: string;
|
||||
groqToken?: string;
|
||||
xaiToken?: string;
|
||||
exo?: {
|
||||
baseUrl?: string;
|
||||
apiKey?: string;
|
||||
};
|
||||
ollama?: {
|
||||
baseUrl?: string;
|
||||
model?: string;
|
||||
visionModel?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type TProvider = 'openai' | 'anthropic' | 'perplexity' | 'ollama';
|
||||
export type TProvider = 'openai' | 'anthropic' | 'perplexity' | 'ollama' | 'exo' | 'groq' | 'xai';
|
||||
|
||||
export class SmartAi {
|
||||
public options: ISmartAiOptions;
|
||||
@ -21,6 +35,9 @@ export class SmartAi {
|
||||
public anthropicProvider: AnthropicProvider;
|
||||
public perplexityProvider: PerplexityProvider;
|
||||
public ollamaProvider: OllamaProvider;
|
||||
public exoProvider: ExoProvider;
|
||||
public groqProvider: GroqProvider;
|
||||
public xaiProvider: XAIProvider;
|
||||
|
||||
constructor(optionsArg: ISmartAiOptions) {
|
||||
this.options = optionsArg;
|
||||
@ -37,6 +54,40 @@ export class SmartAi {
|
||||
this.anthropicProvider = new AnthropicProvider({
|
||||
anthropicToken: this.options.anthropicToken,
|
||||
});
|
||||
await this.anthropicProvider.start();
|
||||
}
|
||||
if (this.options.perplexityToken) {
|
||||
this.perplexityProvider = new PerplexityProvider({
|
||||
perplexityToken: this.options.perplexityToken,
|
||||
});
|
||||
await this.perplexityProvider.start();
|
||||
}
|
||||
if (this.options.groqToken) {
|
||||
this.groqProvider = new GroqProvider({
|
||||
groqToken: this.options.groqToken,
|
||||
});
|
||||
await this.groqProvider.start();
|
||||
}
|
||||
if (this.options.xaiToken) {
|
||||
this.xaiProvider = new XAIProvider({
|
||||
xaiToken: this.options.xaiToken,
|
||||
});
|
||||
await this.xaiProvider.start();
|
||||
}
|
||||
if (this.options.ollama) {
|
||||
this.ollamaProvider = new OllamaProvider({
|
||||
baseUrl: this.options.ollama.baseUrl,
|
||||
model: this.options.ollama.model,
|
||||
visionModel: this.options.ollama.visionModel,
|
||||
});
|
||||
await this.ollamaProvider.start();
|
||||
}
|
||||
if (this.options.exo) {
|
||||
this.exoProvider = new ExoProvider({
|
||||
exoBaseUrl: this.options.exo.baseUrl,
|
||||
apiKey: this.options.exo.apiKey,
|
||||
});
|
||||
await this.exoProvider.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +98,8 @@ export class SmartAi {
|
||||
*/
|
||||
createConversation(provider: TProvider) {
|
||||
switch (provider) {
|
||||
case 'exo':
|
||||
return Conversation.createWithExo(this);
|
||||
case 'openai':
|
||||
return Conversation.createWithOpenAi(this);
|
||||
case 'anthropic':
|
||||
@ -55,6 +108,10 @@ export class SmartAi {
|
||||
return Conversation.createWithPerplexity(this);
|
||||
case 'ollama':
|
||||
return Conversation.createWithOllama(this);
|
||||
case 'groq':
|
||||
return Conversation.createWithGroq(this);
|
||||
case 'xai':
|
||||
return Conversation.createWithXai(this);
|
||||
default:
|
||||
throw new Error('Provider not available');
|
||||
}
|
||||
|
128
ts/provider.exo.ts
Normal file
128
ts/provider.exo.ts
Normal file
@ -0,0 +1,128 @@
|
||||
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');
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ export class GroqProvider extends MultiModalModel {
|
||||
|
||||
// Create a TransformStream to process the input
|
||||
const transform = new TransformStream<Uint8Array, string>({
|
||||
async transform(chunk, controller) {
|
||||
transform: async (chunk, controller) => {
|
||||
buffer += decoder.decode(chunk, { stream: true });
|
||||
|
||||
// Try to parse complete JSON messages from the buffer
|
||||
|
@ -45,7 +45,7 @@ export class OllamaProvider extends MultiModalModel {
|
||||
|
||||
// Create a TransformStream to process the input
|
||||
const transform = new TransformStream<Uint8Array, string>({
|
||||
async transform(chunk, controller) {
|
||||
transform: async (chunk, controller) => {
|
||||
buffer += decoder.decode(chunk, { stream: true });
|
||||
|
||||
// Try to parse complete JSON messages from the buffer
|
||||
|
@ -1,10 +1,20 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
|
||||
// Custom type definition for chat completion messages
|
||||
export type TChatCompletionRequestMessage = {
|
||||
role: "system" | "user" | "assistant";
|
||||
content: string;
|
||||
};
|
||||
|
||||
import { MultiModalModel } from './abstract.classes.multimodal.js';
|
||||
|
||||
export interface IOpenaiProviderOptions {
|
||||
openaiToken: string;
|
||||
chatModel?: string;
|
||||
audioModel?: string;
|
||||
visionModel?: string;
|
||||
// Optionally add more model options (e.g., documentModel) if needed.
|
||||
}
|
||||
|
||||
export class OpenAiProvider extends MultiModalModel {
|
||||
@ -31,11 +41,14 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
// Create a TextDecoder to handle incoming chunks
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let currentMessage: { role: string; content: string; } | null = null;
|
||||
let currentMessage: {
|
||||
role: "function" | "user" | "system" | "assistant" | "tool" | "developer";
|
||||
content: string;
|
||||
} | null = null;
|
||||
|
||||
// Create a TransformStream to process the input
|
||||
const transform = new TransformStream<Uint8Array, string>({
|
||||
async transform(chunk, controller) {
|
||||
transform: async (chunk, controller) => {
|
||||
buffer += decoder.decode(chunk, { stream: true });
|
||||
|
||||
// Try to parse complete JSON messages from the buffer
|
||||
@ -50,7 +63,7 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
try {
|
||||
const message = JSON.parse(line);
|
||||
currentMessage = {
|
||||
role: message.role || 'user',
|
||||
role: (message.role || 'user') as "function" | "user" | "system" | "assistant" | "tool" | "developer",
|
||||
content: message.content || '',
|
||||
};
|
||||
} catch (e) {
|
||||
@ -61,20 +74,24 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
|
||||
// If we have a complete message, send it to OpenAI
|
||||
if (currentMessage) {
|
||||
const stream = await this.openAiApiClient.chat.completions.create({
|
||||
model: 'gpt-4',
|
||||
messages: [{ role: currentMessage.role, content: currentMessage.content }],
|
||||
const messageToSend = { role: "user" as const, content: currentMessage.content };
|
||||
const chatModel = this.options.chatModel ?? 'o3-mini';
|
||||
const requestParams: any = {
|
||||
model: chatModel,
|
||||
messages: [messageToSend],
|
||||
stream: true,
|
||||
});
|
||||
|
||||
};
|
||||
// Temperature is omitted since the model does not support it.
|
||||
const stream = await this.openAiApiClient.chat.completions.create(requestParams);
|
||||
// Explicitly cast the stream as an async iterable to satisfy TypeScript.
|
||||
const streamAsyncIterable = stream as unknown as AsyncIterableIterator<any>;
|
||||
// Process each chunk from OpenAI
|
||||
for await (const chunk of stream) {
|
||||
for await (const chunk of streamAsyncIterable) {
|
||||
const content = chunk.choices[0]?.delta?.content;
|
||||
if (content) {
|
||||
controller.enqueue(content);
|
||||
}
|
||||
}
|
||||
|
||||
currentMessage = null;
|
||||
}
|
||||
},
|
||||
@ -104,15 +121,17 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
content: string;
|
||||
}[];
|
||||
}) {
|
||||
const result = await this.openAiApiClient.chat.completions.create({
|
||||
model: 'gpt-4o',
|
||||
|
||||
const chatModel = this.options.chatModel ?? 'o3-mini';
|
||||
const requestParams: any = {
|
||||
model: chatModel,
|
||||
messages: [
|
||||
{ role: 'system', content: optionsArg.systemMessage },
|
||||
...optionsArg.messageHistory,
|
||||
{ role: 'user', content: optionsArg.userMessage },
|
||||
],
|
||||
});
|
||||
};
|
||||
// Temperature parameter removed to avoid unsupported error.
|
||||
const result = await this.openAiApiClient.chat.completions.create(requestParams);
|
||||
return {
|
||||
role: result.choices[0].message.role as 'assistant',
|
||||
message: result.choices[0].message.content,
|
||||
@ -122,7 +141,7 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
public async audio(optionsArg: { message: string }): Promise<NodeJS.ReadableStream> {
|
||||
const done = plugins.smartpromise.defer<NodeJS.ReadableStream>();
|
||||
const result = await this.openAiApiClient.audio.speech.create({
|
||||
model: 'tts-1-hd',
|
||||
model: this.options.audioModel ?? 'o3-mini',
|
||||
input: optionsArg.message,
|
||||
voice: 'nova',
|
||||
response_format: 'mp3',
|
||||
@ -144,27 +163,30 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
}) {
|
||||
let pdfDocumentImageBytesArray: Uint8Array[] = [];
|
||||
|
||||
// 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) {
|
||||
const documentImageArray = await this.smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
||||
const documentImageArray = await smartpdfInstance.convertPDFToPngBytes(pdfDocument);
|
||||
pdfDocumentImageBytesArray = pdfDocumentImageBytesArray.concat(documentImageArray);
|
||||
}
|
||||
await smartpdfInstance.stop();
|
||||
|
||||
console.log(`image smartfile array`);
|
||||
console.log(pdfDocumentImageBytesArray.map((smartfile) => smartfile.length));
|
||||
|
||||
const smartfileArray = await plugins.smartarray.map(
|
||||
pdfDocumentImageBytesArray,
|
||||
async (pdfDocumentImageBytes) => {
|
||||
return plugins.smartfile.SmartFile.fromBuffer(
|
||||
'pdfDocumentImage.jpg',
|
||||
Buffer.from(pdfDocumentImageBytes)
|
||||
);
|
||||
}
|
||||
);
|
||||
// Filter out any empty buffers to avoid sending invalid image URLs.
|
||||
const validImageBytesArray = pdfDocumentImageBytesArray.filter(imageBytes => imageBytes && imageBytes.length > 0);
|
||||
const imageAttachments = validImageBytesArray.map(imageBytes => ({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: 'data:image/png;base64,' + Buffer.from(imageBytes).toString('base64'),
|
||||
},
|
||||
}));
|
||||
|
||||
const result = await this.openAiApiClient.chat.completions.create({
|
||||
model: 'gpt-4o',
|
||||
// response_format: { type: "json_object" }, // not supported for now
|
||||
const chatModel = this.options.chatModel ?? 'gpt-4o';
|
||||
const requestParams: any = {
|
||||
model: chatModel,
|
||||
messages: [
|
||||
{ role: 'system', content: optionsArg.systemMessage },
|
||||
...optionsArg.messageHistory,
|
||||
@ -172,30 +194,22 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: optionsArg.userMessage },
|
||||
...(() => {
|
||||
const returnArray = [];
|
||||
for (const imageBytes of pdfDocumentImageBytesArray) {
|
||||
returnArray.push({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: 'data:image/png;base64,' + Buffer.from(imageBytes).toString('base64'),
|
||||
},
|
||||
});
|
||||
}
|
||||
return returnArray;
|
||||
})(),
|
||||
...imageAttachments,
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
// Temperature parameter removed.
|
||||
const result = await this.openAiApiClient.chat.completions.create(requestParams);
|
||||
return {
|
||||
message: result.choices[0].message,
|
||||
};
|
||||
}
|
||||
|
||||
public async vision(optionsArg: { image: Buffer; prompt: string }): Promise<string> {
|
||||
const result = await this.openAiApiClient.chat.completions.create({
|
||||
model: 'gpt-4-vision-preview',
|
||||
const visionModel = this.options.visionModel ?? 'gpt-4o';
|
||||
const requestParams: any = {
|
||||
model: visionModel,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
@ -211,8 +225,8 @@ export class OpenAiProvider extends MultiModalModel {
|
||||
}
|
||||
],
|
||||
max_tokens: 300
|
||||
});
|
||||
|
||||
};
|
||||
const result = await this.openAiApiClient.chat.completions.create(requestParams);
|
||||
return result.choices[0].message.content || '';
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user