llmforge
Version:
One API, every AI model, instant switching. Change from GPT-4 to Gemini to local models with a single config update. LLMForge is the lightweight, TypeScript-first solution for multi-provider AI applications with zero vendor lock-in.
147 lines • 4.95 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageValidator = exports.FileEncoder = exports.ContentBuilder = void 0;
class ContentBuilder {
constructor() {
this.contents = [];
}
static create() {
return new ContentBuilder();
}
addTextMessage(text, role = 'user') {
this.contents.push({
role,
parts: [{ text }],
});
return this;
}
addConversationTurn(userText, modelText) {
this.addTextMessage(userText, 'user');
this.addTextMessage(modelText, 'model');
return this;
}
build() {
return [...this.contents];
}
clear() {
this.contents = [];
return this;
}
getLastContent() {
return this.contents[this.contents.length - 1];
}
removeLastContent() {
this.contents.pop();
return this;
}
static fromMessages(messages) {
return messages.map(msg => ({
role: msg.role,
parts: [{ text: msg.text }],
}));
}
static textOnly(text, role = 'user') {
return [{ role, parts: [{ text }] }];
}
}
exports.ContentBuilder = ContentBuilder;
class FileEncoder {
static async encodeFileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const result = reader.result;
// Remove data URL prefix (e.g., "data:image/jpeg;base64,")
const base64 = result.split(',')[1];
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
static async encodeBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
static getMimeTypeFromFile(file) {
return file.type || this.getMimeTypeFromExtension(file.name);
}
static getMimeTypeFromExtension(filename) {
const ext = filename.toLowerCase().split('.').pop();
const mimeTypes = {
// Images
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
webp: 'image/webp',
bmp: 'image/bmp',
svg: 'image/svg+xml',
// Documents
pdf: 'application/pdf',
txt: 'text/plain',
md: 'text/markdown',
html: 'text/html',
css: 'text/css',
js: 'application/javascript',
json: 'application/json',
xml: 'application/xml',
// Office documents
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
// Audio/Video
mp3: 'audio/mpeg',
wav: 'audio/wav',
mp4: 'video/mp4',
avi: 'video/x-msvideo',
mov: 'video/quicktime',
};
return mimeTypes[ext || ''] || 'application/octet-stream';
}
}
exports.FileEncoder = FileEncoder;
class MessageValidator {
static validateContent(content) {
if (!content.parts || content.parts.length === 0) {
throw new Error('Content must have at least one part');
}
for (const part of content.parts) {
if ('text' in part) {
if (!part.text || typeof part.text !== 'string') {
throw new Error('Text part must have non-empty text string');
}
}
else if ('inline_data' in part) {
if (!part.inline_data.mime_type || !part.inline_data.data) {
throw new Error('Inline data part must have mime_type and data');
}
}
else if ('file_data' in part) {
if (!part.file_data.mime_type || !part.file_data.file_uri) {
throw new Error('File data part must have mime_type and file_uri');
}
}
else {
throw new Error('Invalid content part type');
}
}
}
static validateContents(contents) {
if (!contents || contents.length === 0) {
throw new Error('Contents array cannot be empty');
}
for (const content of contents) {
this.validateContent(content);
}
}
}
exports.MessageValidator = MessageValidator;
//# sourceMappingURL=ai.builder.js.map